@lensmcp/protocol-types 1.25.32 → 1.25.34

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/index.d.ts CHANGED
@@ -17,6 +17,12 @@ export * from './lib/log-query.js';
17
17
  export * from './lib/error-dismissal.js';
18
18
  export * from './lib/ai-content.js';
19
19
  export * from './lib/http-exchange.js';
20
+ export * from './lib/ai-call.js';
21
+ export * from './lib/ai-call-row.js';
22
+ export * from './lib/ai-call-groups.js';
23
+ export * from './lib/ai-context.js';
24
+ export * from './lib/ai-origins.js';
25
+ export * from './lib/ai-playground.js';
20
26
  export * from './lib/pod-memory.js';
21
27
  export * from './lib/perf-rules.js';
22
28
  export * from './lib/sources.js';
@@ -24,3 +30,6 @@ export * from './lib/page-visit.js';
24
30
  export * from './lib/replay.js';
25
31
  export * from './lib/event-loop.js';
26
32
  export * from './lib/workspace-actions.js';
33
+ export * from './lib/team.js';
34
+ export * from './lib/team-design.js';
35
+ export * from './lib/team-library.js';
package/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";export*from"./lib/schema-version.js";export*from"./lib/source-location.js";export*from"./lib/context.js";export*from"./lib/event.js";export*from"./lib/node.js";export*from"./lib/edge.js";export*from"./lib/attributes.js";export*from"./lib/producer-health.js";export*from"./lib/resources.js";export*from"./lib/tokens.js";export*from"./lib/capture-demand.js";export*from"./lib/bounded-clone.js";export*from"./lib/bounded-map.js";export*from"./lib/flood-guard.js";export*from"./lib/dashboard.js";export*from"./lib/log-query.js";export*from"./lib/error-dismissal.js";export*from"./lib/ai-content.js";export*from"./lib/http-exchange.js";export*from"./lib/pod-memory.js";export*from"./lib/perf-rules.js";export*from"./lib/sources.js";export*from"./lib/page-visit.js";export*from"./lib/replay.js";export*from"./lib/event-loop.js";export*from"./lib/workspace-actions.js";
1
+ "use strict";export*from"./lib/schema-version.js";export*from"./lib/source-location.js";export*from"./lib/context.js";export*from"./lib/event.js";export*from"./lib/node.js";export*from"./lib/edge.js";export*from"./lib/attributes.js";export*from"./lib/producer-health.js";export*from"./lib/resources.js";export*from"./lib/tokens.js";export*from"./lib/capture-demand.js";export*from"./lib/bounded-clone.js";export*from"./lib/bounded-map.js";export*from"./lib/flood-guard.js";export*from"./lib/dashboard.js";export*from"./lib/log-query.js";export*from"./lib/error-dismissal.js";export*from"./lib/ai-content.js";export*from"./lib/http-exchange.js";export*from"./lib/ai-call.js";export*from"./lib/ai-call-row.js";export*from"./lib/ai-call-groups.js";export*from"./lib/ai-context.js";export*from"./lib/ai-origins.js";export*from"./lib/ai-playground.js";export*from"./lib/pod-memory.js";export*from"./lib/perf-rules.js";export*from"./lib/sources.js";export*from"./lib/page-visit.js";export*from"./lib/replay.js";export*from"./lib/event-loop.js";export*from"./lib/workspace-actions.js";export*from"./lib/team.js";export*from"./lib/team-design.js";export*from"./lib/team-library.js";
@@ -0,0 +1,56 @@
1
+ import type { AiCallRow, AiNames, AiNameSource } from './ai-call-row.js';
2
+ import type { AiPartKind } from './ai-context.js';
3
+ /**
4
+ * The kept AI calls in groups (planning/ai-calls) — by the agent that made them (the default), the action they were
5
+ * for, their run, their thread, their flow, the model, the service, or any label the app put on them (`meta:case`) —
6
+ * and inside a group, its runs: one turn of an agent's tool loop, its calls by step.
7
+ *
8
+ * Pure, on rows already named and in their runs (`/api/ai/calls`, the AI call index's view), and shared: the AI calls
9
+ * page groups by it, and so does the MCP server's `ai_calls`.
10
+ */
11
+ export type AiGroupBy = 'agent' | 'action' | 'run' | 'thread' | 'flow' | 'model' | 'service' | `meta:${string}`;
12
+ export interface AiTotals {
13
+ calls: number;
14
+ runs: number;
15
+ failed: number;
16
+ input: number;
17
+ cached: number;
18
+ written: number;
19
+ output: number;
20
+ /** What the input went to, by kind — summed over the calls whose split was read. */
21
+ split: Partial<Record<AiPartKind, number>>;
22
+ /** Estimated; `priced` of the calls had a price. */
23
+ costUsd: number;
24
+ priced: number;
25
+ first: number;
26
+ last: number;
27
+ models: string[];
28
+ }
29
+ export interface AiRun {
30
+ key: string;
31
+ from: AiCallRow['run']['from'];
32
+ /** Its calls by step. */
33
+ calls: AiCallRow[];
34
+ at: number;
35
+ last: number;
36
+ totals: AiTotals;
37
+ }
38
+ export interface AiGroup {
39
+ key: string;
40
+ name: string;
41
+ /** Where an agent's or an action's name came from. */
42
+ from?: AiNameSource;
43
+ /** Agent and action groups can be renamed and merged; the others are what they are. */
44
+ nameable: boolean;
45
+ /** The groups merged into this one (their keys), from the names file — each can be taken back out. */
46
+ merged: string[];
47
+ runs: AiRun[];
48
+ totals: AiTotals;
49
+ }
50
+ export declare function aiTotalsOf(rows: readonly AiCallRow[]): AiTotals;
51
+ /** The rows' runs, newest first — oldest first (`inOrder`) where the order is the story, as in one flow; each run's calls by step. */
52
+ export declare function aiRunsOf(rows: readonly AiCallRow[], inOrder?: boolean): AiRun[];
53
+ /** The rows in groups, the group with the latest call first; a group's runs newest first. */
54
+ export declare function groupAiCalls(rows: readonly AiCallRow[], by: AiGroupBy, names?: AiNames): AiGroup[];
55
+ /** A call matches a search when any word of it is in its agent, action, model, service, ids, labels or what was said. */
56
+ export declare function matchesAi(r: AiCallRow, q: string): boolean;
@@ -0,0 +1,2 @@
1
+ "use strict";var u=Object.defineProperty;var i=(t,e)=>u(t,"name",{value:e,configurable:!0});var m=Object.defineProperty,c=i((t,e)=>m(t,"name",{value:e,configurable:!0}),"i");export function aiTotalsOf(t){const e={calls:t.length,runs:new Set(t.map(a=>a.run.key)).size,failed:0,input:0,cached:0,written:0,output:0,split:{},costUsd:0,priced:0,first:1/0,last:0,models:[]},o=new Map;for(const a of t){a.failed&&e.failed++,e.input+=a.tokens?.input??0,e.cached+=a.tokens?.cached??0,e.written+=a.tokens?.written??0,e.output+=a.tokens?.output??0;for(const[s,n]of Object.entries(a.split??{}))e.split[s]=(e.split[s]??0)+n;a.costUsd!==void 0&&(e.costUsd+=a.costUsd,e.priced++),e.first=Math.min(e.first,a.at),e.last=Math.max(e.last,a.at),a.model&&o.set(a.model,(o.get(a.model)??0)+1)}return e.models=[...o].sort((a,s)=>s[1]-a[1]).map(([a])=>a),t.length||(e.first=0),e}i(aiTotalsOf,"aiTotalsOf"),c(aiTotalsOf,"aiTotalsOf");export function aiRunsOf(t,e=!1){const o=new Map;for(const a of t){const s=o.get(a.run.key);s?s.push(a):o.set(a.run.key,[a])}return[...o.entries()].map(([a,s])=>{s.sort((r,l)=>(r.step??0)-(l.step??0)||r.at-l.at);const n=aiTotalsOf(s);return{key:a,from:s[0].run.from,calls:s,at:n.first,last:n.last,totals:n}}).sort((a,s)=>e?a.at-s.at:s.last-a.last)}i(aiRunsOf,"aiRunsOf"),c(aiRunsOf,"aiRunsOf");const d="(none)";function f(t,e){if(e==="agent")return t.agent;if(e==="action")return t.action??{key:"action:",name:"(no action)"};if(e==="run")return{key:t.run.key,name:t.agent.name};if(e==="thread")return t.thread?{key:t.thread.key,name:`thread ${t.thread.name}`}:{key:"thread:",name:"(no thread)"};if(e==="flow")return t.flowId?{key:`flow:${t.flowId}`,name:t.flowId}:t.requestId?{key:`request:${t.requestId}`,name:`request ${t.requestId}`}:{key:"flow:",name:"(no flow)"};if(e==="model")return{key:`model:${t.model??""}`,name:t.model??"(unknown model)"};if(e==="service")return{key:`service:${t.service}`,name:t.service};const o=e.slice(5),a=t.meta?.[o];return{key:`${e}=${a??""}`,name:a??d}}i(f,"m"),c(f,"keyOf");export function groupAiCalls(t,e,o){const a=new Map;for(const n of t){const r=f(n,e),l=a.get(r.key);l?l.rows.push(n):a.set(r.key,{...r,rows:[n]})}const s=e==="agent"||e==="action";return[...a.values()].map(n=>({key:n.key,name:n.name,...n.from?{from:n.from}:{},nameable:s&&/^(agent|action):./.test(n.key),merged:o?Object.entries(o.merge).filter(([,r])=>r===n.key).map(([r])=>r).sort():[],runs:aiRunsOf(n.rows,e==="flow"),totals:aiTotalsOf(n.rows)})).sort((n,r)=>r.totals.last-n.totals.last||n.name.localeCompare(r.name))}i(groupAiCalls,"groupAiCalls"),c(groupAiCalls,"groupAiCalls");export function matchesAi(t,e){const o=e.toLowerCase().split(/\s+/).filter(Boolean);if(!o.length)return!0;const a=[t.id,t.agent.name,t.action?.name,t.model,t.service,t.flowId,t.requestId,t.schema,t.asked,t.said,...t.called??[],...Object.entries(t.meta??{}).map(([s,n])=>`${s}:${n}`)].filter(Boolean).join(`
2
+ `).toLowerCase();return o.every(s=>a.includes(s))}i(matchesAi,"matchesAi"),c(matchesAi,"matchesAi");
@@ -0,0 +1,252 @@
1
+ import type { HttpExchange } from './http-exchange.js';
2
+ import { type AiApi, type AiShape } from './ai-call.js';
3
+ import { type AiPartKind } from './ai-context.js';
4
+ import type { AiOrigins } from './ai-origins.js';
5
+ /**
6
+ * One AI provider's call as a line of the AI calls page — and what it belongs to: the agent that made it, the run
7
+ * (one turn of an agent's tool loop, its rounds) it is part of, the action it was for, its flow.
8
+ *
9
+ * Names come from what the app said, in order: its own labels (OpenAI `metadata` — FoodGuard's conductor sends
10
+ * `agent`, `run`, `act`, `round`, `attempt`), the `x-lensmcp-agent` / `-run` / `-action` headers, else what the call
11
+ * itself says — the system prompt's title (`# Critical Limits Analyst — …`, `You are a compliance gate analyst …`),
12
+ * the structured answer's schema (`product_flow`), the tools' MCP server (`conductor__…`) — else the service. A run the
13
+ * app did not name is found from the calls themselves: one continuing another's answer (`previous_response_id`), or
14
+ * carrying the results of the tools another's answer called. What a person renames or merges ({@link AiNames}) wins.
15
+ *
16
+ * What joins runs across requests and services is the app's to say: its thread (`metadata.thread` / `conversation` /
17
+ * `case`, or the `x-lensmcp-thread` header — not `session`, a word the capture redacts as a credential) and the run that
18
+ * started this one (`metadata.parent_run`, `x-lensmcp-parent`).
19
+ *
20
+ * Pure, and shared: the server indexes the kept exchanges with it, the page groups by it.
21
+ */
22
+ /** Where a name came from: renamed by a person, said by the app, or read off the call. */
23
+ export type AiNameSource = 'renamed' | 'metadata' | 'header' | 'prompt' | 'schema' | 'tools' | 'service';
24
+ /** How the call's run was found: the app named it, it continues an answer, it carries tool results — or it is alone. */
25
+ export type AiRunSource = 'metadata' | 'header' | 'chain' | 'tools' | 'alone';
26
+ export interface AiNamed {
27
+ /** Stable across the calls it names (`agent:onboarding-chat-conductor`); what a rename or a merge is keyed by. */
28
+ key: string;
29
+ name: string;
30
+ from: AiNameSource;
31
+ }
32
+ export interface AiCallRow {
33
+ /** The exchange's id (`hx_…`): the call's page is `/ai/calls/<id>`. */
34
+ id: string;
35
+ at: number;
36
+ durationMs?: number;
37
+ status?: number;
38
+ /** The provider said it failed, or no answer came. */
39
+ failed?: true;
40
+ service: string;
41
+ flowId?: string;
42
+ requestId?: string;
43
+ provider: string;
44
+ host: string;
45
+ api: AiApi;
46
+ shape: AiShape;
47
+ model?: string;
48
+ streamed?: true;
49
+ tokens?: {
50
+ input?: number;
51
+ cached?: number;
52
+ written?: number;
53
+ output?: number;
54
+ reasoning?: number;
55
+ };
56
+ /** What its input tokens went to, by kind — the provider's count split by estimate ({@link aiContextOf}). */
57
+ split?: Partial<Record<AiPartKind, number>>;
58
+ /** What it cost, estimated from its tokens by the lens's price table (the server's) — none for a model it has no price for. */
59
+ costUsd?: number;
60
+ agent: AiNamed;
61
+ run: {
62
+ key: string;
63
+ from: AiRunSource;
64
+ };
65
+ /** The conversation, case or document its run serves: what joins runs across requests and services. */
66
+ thread?: {
67
+ key: string;
68
+ name: string;
69
+ from: 'metadata' | 'header';
70
+ };
71
+ /** The run that started its run (`run:<id>`): an agent called by another, a job a conversation started. */
72
+ parent?: string;
73
+ /** Its place in its run, from 1 — set when the rows are linked ({@link linkAiRuns}). */
74
+ step?: number;
75
+ /** The app's own round and attempt (`metadata.round`, `metadata.attempt`). */
76
+ round?: number;
77
+ attempt?: number;
78
+ action?: AiNamed;
79
+ /** The structured answer's schema name. */
80
+ schema?: string;
81
+ /** The tools it was offered, by name (at most {@link MAX_TOOLS}). */
82
+ tools?: string[];
83
+ /** The tools its answer called, by name. */
84
+ called?: string[];
85
+ /** The ids of the tool calls in its answer: the next call of its run carries their results. */
86
+ callIds?: string[];
87
+ /** The ids of tool calls whose results it carries: what joins it to the call that asked for them. */
88
+ answers?: string[];
89
+ responseId?: string;
90
+ previousResponseId?: string;
91
+ /** The app's own labels, as strings (at most {@link MAX_META} of them). */
92
+ meta?: Record<string, string>;
93
+ /** A body was cut by the capture: some of the call is not readable. */
94
+ cut?: true;
95
+ /** What it was last given and what it answered, a line each — only where content may be shown. */
96
+ asked?: string;
97
+ said?: string;
98
+ }
99
+ /** A person's names for groups, kept in `.lensmcp/ai-names/names.json` and committed: the team sees the same names. */
100
+ export interface AiNames {
101
+ version: 1;
102
+ /** A group's key (`agent:…`, `action:…`) → its name. */
103
+ names: Record<string, string>;
104
+ /** A group's key → the key it was merged into: its calls are shown as that group's. */
105
+ merge: Record<string, string>;
106
+ }
107
+ export declare const EMPTY_AI_NAMES: AiNames;
108
+ /** `GET /api/ai/calls?from=&to=&rev=` — the AI calls the lens kept, in a window, newest first, named and in their runs. */
109
+ export interface AiCallsView {
110
+ /** Changes when a call, or a name, does: sent back as `rev`, an unchanged answer is `{ same: true }`. */
111
+ rev: string;
112
+ at: number;
113
+ rows: AiCallRow[];
114
+ /** Calls kept in all — the window may hold fewer — and when the first of them was made. */
115
+ total: number;
116
+ oldest?: number;
117
+ /** The kept calls are still being read for the first time: how far. */
118
+ reading?: {
119
+ done: number;
120
+ total: number;
121
+ };
122
+ names: AiNames;
123
+ /** What was said is in the rows (`asked`, `said`): a loopback-bound dashboard. */
124
+ content: boolean;
125
+ }
126
+ /** `GET /api/ai/call?id=` — one call: its row, the other calls of its run, and the exchange itself. */
127
+ export interface AiCallView {
128
+ row: AiCallRow;
129
+ /** Its run's calls by step, itself among them. */
130
+ run: AiCallRow[];
131
+ /**
132
+ * The other calls of its thread — the conversation, case or document it serves, across requests and services — by
133
+ * time, at most {@link MAX_THREAD_CALLS}; absent when it has no thread.
134
+ */
135
+ thread?: AiCallRow[];
136
+ exchange?: HttpExchange;
137
+ /** Why the exchange is not here: a dashboard not bound to loopback, or a file no longer kept. */
138
+ withheld?: string;
139
+ names: AiNames;
140
+ /** Where each part of what the model was given came from — set beside the earlier calls of its run, thread and flow (`aiOriginsOf`); only with the exchange. */
141
+ origins?: AiOrigins;
142
+ }
143
+ /** One model's share of an AI provider's kept calls — Data › External APIs › its host › AI models, when the AI tap read none. */
144
+ export interface AiModelUse {
145
+ model: string;
146
+ calls: number;
147
+ failed: number;
148
+ /** Calls its provider reported no usage for. */
149
+ unmetered: number;
150
+ input: number;
151
+ cached: number;
152
+ output: number;
153
+ /** Over the `priced` calls — those the lens's price table has a price for. */
154
+ costUsd: number;
155
+ priced: number;
156
+ /** The agents that called it. */
157
+ agents: number;
158
+ first: number;
159
+ last: number;
160
+ }
161
+ /** One agent's share of an AI provider's kept calls — and what its input went to (its calls' `split`, summed). */
162
+ export interface AiAgentUse {
163
+ key: string;
164
+ name: string;
165
+ from: AiNameSource;
166
+ calls: number;
167
+ runs: number;
168
+ failed: number;
169
+ input: number;
170
+ cached: number;
171
+ written: number;
172
+ output: number;
173
+ costUsd: number;
174
+ priced: number;
175
+ split: Partial<Record<AiPartKind, number>>;
176
+ models: string[];
177
+ last: number;
178
+ }
179
+ /** `GET /api/ai/models?host=` — the host's kept AI calls, by model and by agent: the AI tap is off unless a service turns it on, the kept calls are not. */
180
+ export interface AiModelsView {
181
+ host: string;
182
+ at: number;
183
+ calls: number;
184
+ oldest?: number;
185
+ models: AiModelUse[];
186
+ agents?: AiAgentUse[];
187
+ /** The kept calls are still being read for the first time: how far. */
188
+ reading?: {
189
+ done: number;
190
+ total: number;
191
+ };
192
+ }
193
+ /** The calls to `host` by model, the most called first. */
194
+ export declare function aiModelUses(rows: readonly AiCallRow[], host: string): AiModelUse[];
195
+ /** The calls to `host` — to every host when none is named (`ai://agents`) — by agent (named: renames and merges applied), the one with the most input first. */
196
+ export declare function aiAgentUses(rows: readonly AiCallRow[], host?: string): AiAgentUse[];
197
+ /** `POST /api/ai/names` — rename a group (an empty name takes the rename back), merge one into another, or undo a merge. */
198
+ export type AiNamesChange = {
199
+ op: 'rename';
200
+ key: string;
201
+ name: string;
202
+ } | {
203
+ op: 'merge';
204
+ key: string;
205
+ into: string;
206
+ } | {
207
+ op: 'unmerge';
208
+ key: string;
209
+ };
210
+ export declare const MAX_TOOLS = 40;
211
+ /** A call's page shows at most this many calls of its thread — the latest. */
212
+ export declare const MAX_THREAD_CALLS = 300;
213
+ export declare const MAX_META = 24;
214
+ /** A key out of a name: lower case, words joined by `-` (`Critical Limits Analyst` → `critical-limits-analyst`). */
215
+ export declare const aiKeyOf: (kind: "agent" | "action", name: string) => string;
216
+ /** A name as a person reads it: `onboarding-chat-conductor` → `Onboarding chat conductor`; one already written for people stays. */
217
+ export declare function humanName(s: string): string;
218
+ /**
219
+ * What a system prompt calls the one it instructs: its heading (`# Critical Limits Analyst — establish …` →
220
+ * `Critical Limits Analyst`), or who it says the model is (`You are a compliance gate analyst for …` →
221
+ * `Compliance gate analyst`). `undefined` when it says neither — its first words are not a name.
222
+ */
223
+ export declare function promptTitle(text: string): string | undefined;
224
+ /**
225
+ * A prompt that names no one, by how it opens — its first words (`You look at ONE product and say …`): two prompts
226
+ * that open alike are one agent, until a person names or merges it.
227
+ */
228
+ export declare function promptOpening(text: string): string | undefined;
229
+ /** An action's name without the ids in it: `ask:products:material.source-adoption.01a163b4-…::509c` → `ask:products:material.source-adoption`. */
230
+ export declare function actionName(s: string): string;
231
+ /**
232
+ * The row of one exchange — `undefined` when it is not an AI provider's call. `content: false` leaves out what was
233
+ * said (`asked`, `said`) for a dashboard not bound to loopback, as the Request & response panel does.
234
+ */
235
+ export declare function aiCallRow(x: HttpExchange, opts: {
236
+ content: boolean;
237
+ }): AiCallRow | undefined;
238
+ /**
239
+ * Joins the calls the app did not put in a run to the run they continue — the call whose answer they continue
240
+ * (`previous_response_id`) or whose tool calls they carry the results of — and numbers each run's calls by time
241
+ * (`step`). Rows are changed in place; any order in, the same result.
242
+ */
243
+ export declare function linkAiRuns(rows: AiCallRow[]): AiCallRow[];
244
+ /** Follows a merge to the group it ended in (a chain of merges, never a loop). */
245
+ export declare function mergedKey(names: AiNames, key: string): string;
246
+ /**
247
+ * A person's renames and merges over the rows: a merged group's calls take the key and name of the group they were
248
+ * merged into; a renamed group its new name (`from: 'renamed'`). Rows are changed in place.
249
+ */
250
+ export declare function applyAiNames(rows: AiCallRow[], names: AiNames): AiCallRow[];
251
+ /** A names file's content, read leniently: anything not a name or a merge is dropped. */
252
+ export declare function aiNamesOf(v: unknown): AiNames;
@@ -0,0 +1,3 @@
1
+ "use strict";var R=Object.defineProperty;var u=(e,s)=>R(e,"name",{value:s,configurable:!0});var B=Object.defineProperty,c=u((e,s)=>B(e,"name",{value:s,configurable:!0}),"d");import{aiCallOf as S}from"./ai-call.js";import{aiContextOf as T,aiSplitOf as z}from"./ai-context.js";export const EMPTY_AI_NAMES={version:1,names:{},merge:{}};export function aiModelUses(e,s){const n=s.toLowerCase(),r=new Map;for(const t of e){if(t.host.toLowerCase()!==n)continue;const o=t.model??"unknown";let a=r.get(o);a||(a={use:{model:o,calls:0,failed:0,unmetered:0,input:0,cached:0,output:0,costUsd:0,priced:0,agents:0,first:t.at,last:t.at},agents:new Set},r.set(o,a));const i=a.use;i.calls+=1,t.failed&&(i.failed+=1),t.tokens?(i.input+=t.tokens.input??0,i.cached+=t.tokens.cached??0,i.output+=t.tokens.output??0):i.unmetered+=1,t.costUsd!==void 0&&(i.costUsd+=t.costUsd,i.priced+=1),a.agents.add(t.agent.key),i.first=Math.min(i.first,t.at),i.last=Math.max(i.last,t.at)}return[...r.values()].map(({use:t,agents:o})=>({...t,agents:o.size})).sort((t,o)=>o.calls-t.calls||o.last-t.last)}u(aiModelUses,"aiModelUses"),c(aiModelUses,"aiModelUses");export function aiAgentUses(e,s){const n=s?.toLowerCase(),r=new Map;for(const t of e){if(n!==void 0&&t.host.toLowerCase()!==n)continue;let o=r.get(t.agent.key);o||(o={use:{key:t.agent.key,name:t.agent.name,from:t.agent.from,calls:0,runs:0,failed:0,input:0,cached:0,written:0,output:0,costUsd:0,priced:0,split:{},models:[],last:t.at},runs:new Set,models:new Map},r.set(t.agent.key,o));const a=o.use;a.calls+=1,o.runs.add(t.run.key),t.failed&&(a.failed+=1),a.input+=t.tokens?.input??0,a.cached+=t.tokens?.cached??0,a.written+=t.tokens?.written??0,a.output+=t.tokens?.output??0,t.costUsd!==void 0&&(a.costUsd+=t.costUsd,a.priced+=1);for(const[i,m]of Object.entries(t.split??{}))a.split[i]=(a.split[i]??0)+m;t.model&&o.models.set(t.model,(o.models.get(t.model)??0)+1),a.last=Math.max(a.last,t.at)}return[...r.values()].map(({use:t,runs:o,models:a})=>({...t,runs:o.size,models:[...a].sort((i,m)=>m[1]-i[1]).map(([i])=>i)})).sort((t,o)=>o.input-t.input||o.calls-t.calls)}u(aiAgentUses,"aiAgentUses"),c(aiAgentUses,"aiAgentUses");export const MAX_TOOLS=40,MAX_THREAD_CALLS=300,MAX_META=24;const E=160,C="unknown service";export const aiKeyOf=c((e,s)=>`${e}:${s.normalize("NFKD").toLowerCase().replace(/[^a-z0-9:.]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"unnamed"}`,"aiKeyOf");export function humanName(e){const s=e.trim();if(/[A-Z]/.test(s)||/ /.test(s))return s;const n=s.replace(/[-_.]+/g," ").trim();return n?n[0].toUpperCase()+n.slice(1):s}u(humanName,"humanName"),c(humanName,"humanName");const f=c((e,s=E)=>{if(!e)return;const n=e.replace(/\s+/g," ").trim();if(n)return n.length>s?`${n.slice(0,s-1)}\u2026`:n},"line");export function promptTitle(e){const s=e.split(`
2
+ `).map(t=>t.trim()).find(Boolean)??"",n=/^#{1,6}\s+(.+)$/.exec(s);if(n){const t=n[1].split(/\s+[—–]\s+|\s+-\s+|:\s/)[0].replace(/[*_`]/g,"").trim();return t?t.slice(0,60):void 0}const r=/^you are (?:an? |the )?(.+?)(?:[.,;:!]|\s+(?:who|that|which|for|in|on|whose|with|and|following|using|working|based|at|to)\b|$)/i.exec(s);if(r){const t=r[1].trim().split(/\s+/);if(t.length>6||/^(?:going|about|given|now|here|responsible|able|allowed|expected|tasked|asked|helping|working|being|not)$/i.test(t[0]))return;const o=t.join(" ");return o[0].toUpperCase()+o.slice(1)}}u(promptTitle,"promptTitle"),c(promptTitle,"promptTitle");export function promptOpening(e){const s=e.split(`
3
+ `).map(r=>r.trim()).find(Boolean);if(!s)return;const n=s.replace(/^[#>*\s-]+/,"").split(/\s+/).filter(Boolean);if(!(n.length<3))return n.length>7?`${n.slice(0,7).join(" ")} \u2026`:n.join(" ")}u(promptOpening,"promptOpening"),c(promptOpening,"promptOpening");export function actionName(e){const s=e.split("::")[0],n=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{8,}|\d{4,})$/i;return s.split(":").map(r=>r.split(".").filter(t=>!n.test(t)).join(".")).filter((r,t,o)=>r||t<o.length-1).join(":").slice(0,120)}u(actionName,"actionName"),c(actionName,"actionName");const g=c((e,s)=>{const n=e.request.headers.find(([r])=>r.toLowerCase()===s)?.[1];if(n===void 0||!s.startsWith("x-lensmcp-")||!n.includes("%"))return n;try{return decodeURIComponent(n)}catch{return n}},"header"),A=c(e=>e!==void 0&&/^\d{1,6}$/.test(e.trim())?Number(e.trim()):void 0,"intOf"),h=c((e,...s)=>{for(const n of s)if(e[n]?.trim())return e[n].trim()},"first");function j(e){const s={};for(const n of e.settings)if(n.name.startsWith("metadata.")){if(Object.keys(s).length>=MAX_META)break;s[n.name.slice(9)]=n.value.slice(0,200)}return s}u(j,"S"),c(j,"metaOf");function U(e,s,n){const r=c((p,k)=>({key:aiKeyOf("agent",p),name:humanName(p),from:k}),"named"),t=h(n,"agent","agent_name","agentName","assistant");if(t)return r(t,"metadata");const o=g(e,"x-lensmcp-agent");if(o?.trim())return r(o.trim(),"header");const a=s.system.map(p=>promptTitle(p.text)).find(Boolean);if(a)return r(a,"prompt");const i=s.answer.format?.name;if(i)return r(i,"schema");const m=[...s.tools,...s.answer.toolCalls].map(p=>p.server).find(Boolean);if(m)return r(m,"tools");const l=s.system.map(p=>promptOpening(p.text)).find(Boolean);return l?{key:aiKeyOf("agent",`prompt ${l.replace(/ …$/,"")}`),name:l,from:"prompt"}:r(`${e.service??C} ${s.api==="embeddings"?"embeddings":s.model??s.api}`,"service")}u(U,"B"),c(U,"agentOf");function _(e,s,n){const r=c((a,i)=>({key:aiKeyOf("action",a),name:a,from:i}),"named"),t=h(n,"act","action","task","step","operation");if(t&&actionName(t))return r(actionName(t),"metadata");const o=g(e,"x-lensmcp-action");if(o?.trim())return r(actionName(o.trim()),"header");if(s.answer.format?.name)return r(s.answer.format.name,"schema")}u(_,"E"),c(_,"actionOf");function N(e){if(e.kind==="message")return f(e.text);if(e.kind==="tool")return f(e.output!==void 0?`${e.tool} \u2192 ${e.output}`:`${e.tool}(${e.arguments})`)}u(N,"z"),c(N,"turnLine");function v(e){const s=e.answer;return s.error?f(`error: ${s.error}`):s.refusal?f(`refused: ${s.refusal}`):s.toolCalls.length?f(s.toolCalls.map(n=>`${n.tool}(${n.arguments})`).join(" \xB7 ")):e.embedding?`${e.embedding.vectors??e.embedding.count} vector${(e.embedding.vectors??e.embedding.count)===1?"":"s"}${e.embedding.dimensions?` \xD7 ${e.embedding.dimensions}`:""}`:f(s.text)}u(v,"A"),c(v,"saidLine");export function aiCallRow(e,s){const n=S(e);if(!n||!e.ai)return;const r=j(n),t=new Map(n.settings.map(d=>[d.name,d.value])),o=h(r,"run","run_id","runId"),a=g(e,"x-lensmcp-run")?.trim(),i=n.turns.filter(d=>d.kind==="tool").filter(d=>d.output!==void 0&&d.callId).map(d=>d.callId),m=n.answer.toolCalls.map(d=>d.callId).filter(d=>!!d),l=n.usage,p=l?{...l.input!==void 0?{input:l.input}:{},...l.cached?{cached:l.cached}:{},...l.written?{written:l.written}:{},...l.output!==void 0?{output:l.output}:{},...l.reasoning?{reasoning:l.reasoning}:{}}:void 0,k=n.shape==="embedding"||l?.input===void 0?{}:z(T(n,{sections:!1})),w=h(r,"thread","thread_id","threadId","conversation","conversation_id","conversationId","case","case_id","caseId"),y=g(e,"x-lensmcp-thread")?.trim(),$=h(r,"parent_run","parentRun","parent")??g(e,"x-lensmcp-parent")?.trim(),x=_(e,n,r),b=A(r.round),I=A(r.attempt),O=t.get("continues response"),M=[...n.turns].reverse().map(N).find(Boolean),L=n.answer.status==="failed"||!!n.answer.error||(e.status??0)>=400||!!e.error;return{id:e.id,at:e.startedAt,...e.durationMs!==void 0?{durationMs:e.durationMs}:{},...e.status!==void 0?{status:e.status}:{},...L?{failed:!0}:{},service:e.service??C,...e.flowId?{flowId:e.flowId}:{},...e.requestId?{requestId:e.requestId}:{},provider:e.ai.provider,host:e.host,api:n.api,shape:n.shape,...n.model?{model:n.model}:{},...e.response.stream?{streamed:!0}:{},...p&&Object.keys(p).length?{tokens:p}:{},...Object.keys(k).length?{split:k}:{},agent:U(e,n,r),run:o?{key:`run:${o}`,from:"metadata"}:a?{key:`run:${a}`,from:"header"}:{key:`run:${e.id}`,from:"alone"},...w?{thread:{key:`thread:${w}`,name:w,from:"metadata"}}:y?{thread:{key:`thread:${y}`,name:y,from:"header"}}:{},...$?{parent:`run:${$}`}:{},...b!==void 0?{round:b}:{},...I!==void 0?{attempt:I}:{},...x?{action:x}:{},...n.answer.format?.name?{schema:n.answer.format.name}:{},...n.tools.length?{tools:n.tools.slice(0,MAX_TOOLS).map(d=>d.name)}:{},...n.answer.toolCalls.length?{called:n.answer.toolCalls.map(d=>d.name)}:{},...m.length?{callIds:m}:{},...i.length?{answers:i}:{},...n.responseId?{responseId:n.responseId}:{},...O?{previousResponseId:O}:{},...Object.keys(r).length?{meta:r}:{},...n.gaps.some(d=>/kept to its first|cut/.test(d))?{cut:!0}:{},...s.content&&M?{asked:M}:{},...s.content&&v(n)?{said:v(n)}:{}}}u(aiCallRow,"aiCallRow"),c(aiCallRow,"aiCallRow");export function linkAiRuns(e){const s=[...e].sort((o,a)=>o.at-a.at||o.id.localeCompare(a.id)),n=new Map,r=new Map;for(const o of s){if(o.run.from==="alone"){const a=o.previousResponseId?n.get(o.previousResponseId):void 0,i=a?void 0:o.answers?.map(l=>r.get(l)).find(Boolean),m=a??i;if(m){const l=a?"chain":"tools";o.run={key:m.run.key,from:m.run.from==="alone"||m.run.from===l?l:m.run.from},m.run.from==="alone"&&(m.run={key:m.run.key,from:l})}}o.responseId&&n.set(o.responseId,o);for(const a of o.callIds??[])r.set(a,o)}const t=new Map;for(const o of s){const a=(t.get(o.run.key)??0)+1;t.set(o.run.key,a),o.step=a}return e}u(linkAiRuns,"linkAiRuns"),c(linkAiRuns,"linkAiRuns");export function mergedKey(e,s){let n=s;for(let r=0;r<8&&e.merge[n]&&e.merge[n]!==s;r++)n=e.merge[n];return n}u(mergedKey,"mergedKey"),c(mergedKey,"mergedKey");export function applyAiNames(e,s){if(!Object.keys(s.names).length&&!Object.keys(s.merge).length)return e;const n=new Map;for(const t of e)n.has(t.agent.key)||n.set(t.agent.key,t.agent),t.action&&!n.has(t.action.key)&&n.set(t.action.key,t.action);const r=c(t=>{const o=mergedKey(s,t.key),a=s.names[o];if(a)return{key:o,name:a,from:"renamed"};if(o===t.key)return t;const i=n.get(o);return{key:o,name:i?.name??humanName(o.replace(/^[a-z]+:/,"")),from:i?.from??t.from}},"resolve");for(const t of e)t.agent=r(t.agent),t.action&&(t.action=r(t.action));return e}u(applyAiNames,"applyAiNames"),c(applyAiNames,"applyAiNames");export function aiNamesOf(e){const s=e&&typeof e=="object"?e:{},n=c(r=>{const t={};if(r&&typeof r=="object"&&!Array.isArray(r))for(const[o,a]of Object.entries(r))/^(agent|action):/.test(o)&&typeof a=="string"&&a.trim()&&(t[o]=a.trim().slice(0,120));return t},"pick");return{version:1,names:n(s.names),merge:n(s.merge)}}u(aiNamesOf,"aiNamesOf"),c(aiNamesOf,"aiNamesOf");
@@ -0,0 +1,146 @@
1
+ import type { HttpExchange } from './http-exchange.js';
2
+ /**
3
+ * An AI provider's call read for a person (Data › External APIs › Request & response): what the model was told — its
4
+ * system prompt, the context the app put in, the conversation so far with each tool call and what it returned, the
5
+ * tools it was offered — and what it answered: text, a structured decision, the tools it chose to call. Read from the
6
+ * bodies the egress tap kept: OpenAI Responses and Chat Completions, embeddings, Anthropic Messages.
7
+ *
8
+ * A body the capture cut (at 64 KB, before AI calls were kept whole) is read as far as it goes (`parseJsonPrefix`), and
9
+ * says so (`gaps`). A streamed answer is put back together from its events, the way the providers' SDKs do.
10
+ *
11
+ * Pure: run on the exchange the panel already holds — no wire change, nothing more fetched.
12
+ */
13
+ export type AiShape = 'prompt' | 'chat' | 'tools' | 'decision' | 'embedding';
14
+ export type AiApi = 'responses' | 'chat' | 'embeddings' | 'messages';
15
+ export interface AiCall {
16
+ provider: string;
17
+ api: AiApi;
18
+ /** What kind of call it is, read from its request: one prompt, a conversation, a tool loop, a structured decision, an embedding. */
19
+ shape: AiShape;
20
+ model?: string;
21
+ /** The provider's id of its answer (`resp_…`, `chatcmpl-…`, `msg_…`): what a later call continuing it names. */
22
+ responseId?: string;
23
+ /** How it was asked — reasoning effort, the output cap, tool choice, temperature… — as written. */
24
+ settings: Array<{
25
+ name: string;
26
+ value: string;
27
+ }>;
28
+ /** The system prompt — `instructions`, a system message — in order. */
29
+ system: AiText[];
30
+ /** Everything the model was given after the system prompt, oldest first. */
31
+ turns: AiTurn[];
32
+ tools: AiTool[];
33
+ answer: AiAnswer;
34
+ usage?: AiUsage;
35
+ embedding?: {
36
+ inputs: string[];
37
+ count: number;
38
+ dimensions?: number;
39
+ vectors?: number;
40
+ };
41
+ /** What could not be read, in words: a body the capture cut, a stream whose events were not kept. */
42
+ gaps: string[];
43
+ }
44
+ export interface AiText {
45
+ source: string;
46
+ text: string;
47
+ cut?: boolean;
48
+ }
49
+ export type AiRole = 'user' | 'assistant' | 'developer' | 'system';
50
+ export type AiTurn = {
51
+ kind: 'message';
52
+ role: AiRole;
53
+ text: string;
54
+ attachments?: string[];
55
+ } | ({
56
+ kind: 'tool';
57
+ } & AiToolCall) | {
58
+ kind: 'reasoning';
59
+ text?: string;
60
+ };
61
+ export interface AiToolCall {
62
+ callId?: string;
63
+ /** As the model named it (`conductor__fill_question`). */
64
+ name: string;
65
+ /** An MCP tool's server (`conductor`): a hosted `mcp` tool's label, or the `server__tool` convention's first part. */
66
+ server?: string;
67
+ /** The tool's own name (`fill_question`). */
68
+ tool: string;
69
+ /** What the model sent, as written (JSON for a function tool). */
70
+ arguments: string;
71
+ /** What came back — present when this call's conversation carries it. */
72
+ output?: string;
73
+ error?: string;
74
+ }
75
+ export interface AiTool {
76
+ name: string;
77
+ server?: string;
78
+ tool: string;
79
+ /** `function`, `mcp`, `web_search`… */
80
+ type: string;
81
+ description?: string;
82
+ /** Its input schema, as offered. */
83
+ parameters?: unknown;
84
+ strict?: boolean;
85
+ /** Calls of it in the conversation, and in the answer. */
86
+ called: number;
87
+ }
88
+ export interface AiAnswer {
89
+ /** `completed`, `incomplete`, `failed`… */
90
+ status?: string;
91
+ /** Why it stopped: `stop`, `tool_calls`, `max_output_tokens`, `end_turn`… */
92
+ finish?: string;
93
+ text?: string;
94
+ /** The structured decision, parsed — when one was asked for (a JSON schema, a JSON object). */
95
+ json?: unknown;
96
+ format?: {
97
+ type: string;
98
+ name?: string;
99
+ schema?: unknown;
100
+ strict?: boolean;
101
+ };
102
+ /** The tools it chose to call — the app runs them and answers in its next call. */
103
+ toolCalls: AiToolCall[];
104
+ reasoning?: string;
105
+ refusal?: string;
106
+ error?: string;
107
+ }
108
+ export interface AiUsage {
109
+ input?: number;
110
+ cached?: number;
111
+ /** Written to the provider's prompt cache by this call — read at a discount by a later call that opens the same way. */
112
+ written?: number;
113
+ output?: number;
114
+ reasoning?: number;
115
+ total?: number;
116
+ }
117
+ /** Where a cut string ends, in what is shown. */
118
+ export declare const CUT_MARK = "\u2026";
119
+ /**
120
+ * JSON that may have been cut anywhere — a body the capture kept to its first 64 KB, a stream event kept to its first
121
+ * 4 KB: what parses of it, closed where it stopped. A string cut in the middle keeps what arrived (marked `…`); a key, a
122
+ * number or a literal cut in the middle is dropped with its member. `undefined` when it is not JSON at all.
123
+ */
124
+ export declare function parseJsonPrefix(text: string): {
125
+ value: unknown;
126
+ cut: boolean;
127
+ } | undefined;
128
+ /** Which API a call went to, by its path. */
129
+ export declare function aiApiOf(path: string): AiApi | undefined;
130
+ /**
131
+ * A tool's server and its own name. A hosted MCP tool names its server (`server_label`); a client that hands MCP tools to
132
+ * the model as functions names them `server__tool` (`conductor__fill_question`), or `mcp__server__tool`.
133
+ */
134
+ export declare function splitToolName(name: string, server?: string): {
135
+ server?: string;
136
+ tool: string;
137
+ };
138
+ /** The call, read — or `undefined` when the exchange is not an AI provider's call this reads. */
139
+ export declare function aiCallOf(x: HttpExchange): AiCall | undefined;
140
+ /** How each kind of call reads, for a person. */
141
+ export declare const SHAPE_LABEL: Record<AiShape, string>;
142
+ /** A JSON text pretty — or as it was, when it is not JSON. */
143
+ export declare function prettyJson(text: string): {
144
+ value?: unknown;
145
+ text: string;
146
+ };
package/lib/ai-call.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";var se=Object.defineProperty;var v=(t,s)=>se(t,"name",{value:s,configurable:!0});var oe=Object.defineProperty,_=v((t,s)=>oe(t,"name",{value:s,configurable:!0}),"d");const b=_(t=>!!t&&typeof t=="object"&&!Array.isArray(t),"isObj"),e=_(t=>typeof t=="string"?t:void 0,"str"),h=_(t=>typeof t=="number"&&Number.isFinite(t)?t:void 0,"num"),k=_(t=>Array.isArray(t)?t:[],"arr"),c=_(t=>b(t)?t:{},"obj"),A=_(t=>typeof t=="string"?t:t===void 0?"":JSON.stringify(t),"json"),U=_(t=>t>=1024*1024?`${(t/1024/1024).toFixed(1)} MB`:`${Math.round(t/1024)} KB`,"kb");export const CUT_MARK="\u2026";export function parseJsonPrefix(t){try{return{value:JSON.parse(t),cut:!1}}catch{}const s=[];let n=0,l=!1,a=!1,r=!1;const o=t.length,i=_(m=>{const d=s[s.length-1];d&&(d.state="after"),n=m},"ended");for(let m=0;m<o;m++){const d=t[m];if(l){r?r=!1:d==="\\"?r=!0:d==='"'&&(l=!1,a?s[s.length-1].state="colon":i(m+1));continue}if(d===" "||d===`
2
+ `||d==="\r"||d===" ")continue;const f=s[s.length-1];if(d==='"'){l=!0,a=!!f&&f.c==="{"&&f.state==="key";continue}if(d==="{"||d==="["){s.push({c:d,state:d==="{"?"key":"value"}),n=m+1;continue}if(d==="}"||d==="]"){s.pop(),i(m+1);continue}if(d===":"){f&&(f.state="value");continue}if(d===","){f&&(f.state=f.c==="{"?"key":"value");continue}let g=m;for(;g<o&&!`,}]
3
+ \r `.includes(t[g]);)g++;if(g>=o)break;i(g),m=g-1}const u=s.map(m=>m.c==="{"?"}":"]").reverse().join("");if(l&&!a)for(let m=0;m<=6;m++)try{return{value:JSON.parse(`${t.slice(0,o-m)}${CUT_MARK}"${u}`),cut:!0}}catch{}if(n)try{return{value:JSON.parse(`${t.slice(0,n)}${u}`),cut:!0}}catch{return}}v(parseJsonPrefix,"parseJsonPrefix"),_(parseJsonPrefix,"parseJsonPrefix");export function aiApiOf(t){const s=t.split("?")[0].replace(/\/+$/,"");if(/\/responses$/.test(s))return"responses";if(/\/chat\/completions$/.test(s))return"chat";if(/\/embeddings$/.test(s))return"embeddings";if(/\/messages$/.test(s))return"messages"}v(aiApiOf,"aiApiOf"),_(aiApiOf,"aiApiOf");export function splitToolName(t,s){if(s)return{server:s,tool:t};const n=/^(?:mcp__)?([A-Za-z0-9-]+(?:_[A-Za-z0-9-]+)*)__(.+)$/.exec(t);return n?{server:n[1],tool:n[2]}:{tool:t}}v(splitToolName,"splitToolName"),_(splitToolName,"splitToolName");const $=_((t,s,n={})=>({...n.callId?{callId:n.callId}:{},name:t,...splitToolName(t,n.server),arguments:s,...n.output!==void 0?{output:n.output}:{},...n.error?{error:n.error}:{}}),"toolCall");function M(t,s,n){if(!t)return;if(t.omitted==="ai"){n.push(`The ${s==="request"?"prompt":"answer"} was not kept \u2014 an AI provider\u2019s bodies are kept only when asked for: Settings \u203A Features \u203A Capture \u203A AI calls, for the calls made after.`);return}if(t.omitted||t.text===void 0)return;const l=parseJsonPrefix(t.text);if(l)return(l.cut||t.truncated)&&n.push(`The ${s} was kept to its first ${U(t.kept)} of ${U(t.size)} \u2014 read as far as it goes; what follows is not shown (the ${s==="request"?"end of the conversation":"rest of the answer"}, marked ${CUT_MARK}).`),l.value}v(M,"E"),_(M,"readBody");function J(t){if(t.data===void 0||t.data==="[DONE]")return;const s=parseJsonPrefix(t.data);return s&&b(s.value)?{v:s.value,cut:s.cut}:void 0}v(J,"M"),_(J,"eventJson");function I(t){if(typeof t=="string")return{text:t,attachments:[]};const s=[],n=[];for(const l of k(t)){if(typeof l=="string"){s.push(l);continue}const a=c(l),r=e(a.type)??"",o=e(a.text)??e(a.refusal);o!==void 0?s.push(o):/image/.test(r)?n.push(e(a.detail)?`image (${e(a.detail)})`:"image"):/file|document/.test(r)?n.push(`file ${e(a.filename)??e(c(a.source).media_type)??e(a.file_id)??""}`.trim()):/audio/.test(r)?n.push("audio"):r&&n.push(r)}return{text:s.join(`
4
+
5
+ `),attachments:n}}v(I,"I"),_(I,"contentOf");const N=_(t=>typeof t=="string"?t:Array.isArray(t)&&I(t).text||A(t),"outputOf");function C(t,s){const n=[];for(const[a,r]of s){let o=t;for(const u of a.split("."))o=c(o)[u];if(o==null)continue;const i=typeof o=="string"?o:typeof o=="number"||typeof o=="boolean"?String(o):b(o)&&e(o.name)?`${e(o.type)??"tool"} ${e(o.name)}`:b(o)&&e(o.type)?e(o.type):JSON.stringify(o);n.push({name:r,value:i})}const l=c(t.metadata);for(const[a,r]of Object.entries(l))n.push({name:`metadata.${a}`,value:typeof r=="string"?r:JSON.stringify(r)});return n}v(C,"O"),_(C,"settingsOf");function O(t,s,n,l){const a=s?t.byCallId.get(s):void 0;if(a){a.output=n,l&&(a.error=l);return}t.call.turns.push({kind:"tool",...$("(an earlier call)","",{callId:s,output:n,error:l})})}v(O,"T"),_(O,"addOutput");function j(t,s){const n={kind:"tool",...s};t.call.turns.push(n),s.callId&&t.byCallId.set(s.callId,n)}v(j,"S"),_(j,"addCall");const ne=[["reasoning.effort","reasoning effort"],["reasoning.summary","reasoning summary"],["max_output_tokens","max output tokens"],["temperature","temperature"],["top_p","top_p"],["tool_choice","tool choice"],["parallel_tool_calls","parallel tool calls"],["text.verbosity","verbosity"],["truncation","truncation"],["previous_response_id","continues response"],["store","store"],["service_tier","service tier"],["stream","stream"],["prompt_cache_key","prompt cache key"],["user","user"],["safety_identifier","safety identifier"]],W=_(t=>A(Object.fromEntries(Object.entries(t).filter(([s])=>s!=="type"&&s!=="id"&&s!=="status"))),"builtInCall");function Z(t,s){const n=e(s.type)??(s.role?"message":"");if(n==="message"){const l=e(s.role)??"user",{text:a,attachments:r}=I(s.content);l==="system"?t.call.system.push({source:"system message",text:a}):t.call.turns.push({kind:"message",role:l,text:a,...r.length?{attachments:r}:{}})}else if(n==="function_call"||n==="custom_tool_call")j(t,$(e(s.name)??"?",e(s.arguments)??A(s.input),{callId:e(s.call_id)}));else if(n==="function_call_output"||n==="custom_tool_call_output"||n==="computer_call_output"||n==="local_shell_call_output")O(t,e(s.call_id),N(s.output));else if(n==="mcp_call")j(t,$(e(s.name)??"?",e(s.arguments)??"",{callId:e(s.id),server:e(s.server_label),...s.output!==void 0&&s.output!==null?{output:N(s.output)}:{},error:e(s.error)}));else if(n==="mcp_list_tools"){const l=k(s.tools).map(a=>e(c(a).name)).filter(Boolean);j(t,$("list_tools","",{server:e(s.server_label),output:l.join(", ")}))}else if(n==="reasoning"){const l=k(s.summary).map(a=>e(c(a).text)).filter(Boolean).join(`
6
+
7
+ `);t.call.turns.push({kind:"reasoning",...l?{text:l}:{}})}else n&&j(t,$(n.replace(/_call$/,""),W(s),{callId:e(s.call_id)??e(s.id)}))}v(Z,"R"),_(Z,"responsesItem");function D(t){return k(t.tools).map(s=>{const n=c(s),l=e(n.type)??"function";if(l==="mcp"){const r=e(n.server_label)??"mcp",o=k(n.allowed_tools).map(i=>e(i)).filter(Boolean);return{name:r,server:r,tool:o.length?o.join(", "):"every tool",type:l,...e(n.server_description)??e(n.server_url)?{description:e(n.server_description)??e(n.server_url)}:{},called:0}}const a=e(n.name)??l;return{name:a,...splitToolName(a),type:l,...e(n.description)?{description:e(n.description)}:{},...n.parameters!==void 0?{parameters:n.parameters}:{},...typeof n.strict=="boolean"?{strict:n.strict}:{},called:0}})}v(D,"W"),_(D,"responsesTools");function q(t,s){const n=t.call.answer;e(s.status)&&(n.status=e(s.status));const l=e(c(s.incomplete_details).reason);l&&(n.finish=l);const a=c(s.error);e(a.message)&&(n.error=e(a.message)),e(s.model)&&(t.call.model??=e(s.model)),e(s.id)&&(t.call.responseId=e(s.id)),E(t,k(s.output).map(c));const r=c(s.usage);Object.keys(r).length&&(t.call.usage={...h(r.input_tokens)!==void 0?{input:h(r.input_tokens)}:{},...h(c(r.input_tokens_details).cached_tokens)!==void 0?{cached:h(c(r.input_tokens_details).cached_tokens)}:{},...h(c(r.input_tokens_details).cache_write_tokens)?{written:h(c(r.input_tokens_details).cache_write_tokens)}:{},...h(r.output_tokens)!==void 0?{output:h(r.output_tokens)}:{},...h(c(r.output_tokens_details).reasoning_tokens)!==void 0?{reasoning:h(c(r.output_tokens_details).reasoning_tokens)}:{},...h(r.total_tokens)!==void 0?{total:h(r.total_tokens)}:{}})}v(q,"z"),_(q,"responsesAnswer");function E(t,s){const n=t.call.answer,l=[],a=[];for(const r of s){const o=e(r.type);if(o==="message")for(const i of k(r.content).map(c))e(i.refusal)?n.refusal=e(i.refusal):e(i.text)!==void 0&&l.push(e(i.text));else if(o==="function_call"||o==="custom_tool_call")n.toolCalls.push($(e(r.name)??"?",e(r.arguments)??A(r.input),{callId:e(r.call_id)}));else if(o==="mcp_call")n.toolCalls.push($(e(r.name)??"?",e(r.arguments)??"",{callId:e(r.id),server:e(r.server_label),...r.output!==void 0&&r.output!==null?{output:N(r.output)}:{},error:e(r.error)}));else if(o==="reasoning"){const i=k(r.summary).map(u=>e(c(u).text)).filter(Boolean).join(`
8
+
9
+ `);i&&a.push(i)}else o&&o!=="mcp_list_tools"&&n.toolCalls.push($(o.replace(/_call$/,""),W(r),{callId:e(r.call_id)??e(r.id)}))}l.length&&(n.text=l.join(`
10
+
11
+ `)),a.length&&(n.reasoning=a.join(`
12
+
13
+ `))}v(E,"F"),_(E,"responsesOutput");function H(t,s,n){let l,a=0;const r=new Map,o=new Set,i=new Map,u=new Map,m=new Map;for(const f of s){const g=J(f);if(!g)continue;g.cut&&a++;const p=g.v,y=e(p.type)??f.event,w=h(p.output_index)??0;if(y==="response.completed"||y==="response.incomplete"||y==="response.failed"){if(!g.cut&&b(p.response))l=p.response;else if(b(p.response)){const x=p.response;e(x.status)&&(t.call.answer.status=e(x.status))}}else if(y==="response.created"||y==="response.in_progress"){const x=c(p.response);e(x.model)&&(t.call.model??=e(x.model)),e(x.id)&&(t.call.responseId??=e(x.id))}else y==="response.output_item.added"||y==="response.output_item.done"?b(p.item)&&(!o.has(w)||!g.cut)&&(r.set(w,{...r.get(w)??{},...p.item}),y.endsWith(".done")&&!g.cut&&o.add(w)):y==="response.output_text.delta"||y==="response.refusal.delta"?i.set(w,(i.get(w)??"")+(e(p.delta)??"")):y==="response.output_text.done"&&!g.cut&&e(p.text)!==void 0?i.set(w,e(p.text)):y==="response.function_call_arguments.delta"||y==="response.custom_tool_call_input.delta"||y==="response.mcp_call_arguments.delta"?u.set(w,(u.get(w)??"")+(e(p.delta)??"")):(y==="response.function_call_arguments.done"||y==="response.mcp_call_arguments.done")&&!g.cut&&e(p.arguments)!==void 0?u.set(w,e(p.arguments)):y==="response.reasoning_summary_text.delta"?m.set(w,(m.get(w)??"")+(e(p.delta)??"")):(y==="error"||y==="response.error")&&(t.call.answer.error=e(p.message)??e(c(p.error).message)??"the stream reported an error")}if(l){q(t,l);return}const d=[...r.entries()].sort(([f],[g])=>f-g).map(([f,g])=>{if(o.has(f))return g;const p=e(g.type);return p==="message"&&i.has(f)?{...g,content:[{type:"output_text",text:i.get(f)}]}:(p==="function_call"||p==="mcp_call")&&u.has(f)?{...g,arguments:u.get(f)}:p==="custom_tool_call"&&u.has(f)?{...g,input:u.get(f)}:p==="reasoning"&&m.has(f)?{...g,summary:[{type:"summary_text",text:m.get(f)}]}:g});for(const[f,g]of i)r.has(f)||d.push({type:"message",content:[{type:"output_text",text:g}]});E(t,d),t.call.answer.status||(t.call.answer.status=d.length?"completed":"unknown"),a&&n.push(`${a} of the stream\u2019s events were kept to their first 4 KB, so the answer was rebuilt from its pieces${t.call.usage?"":" \u2014 its token counts came in the last event, which was cut"}.`)}v(H,"Z"),_(H,"responsesStream");function R(t,s,n,l){const a=t.call;e(s.model)&&(a.model=e(s.model)),e(s.instructions)&&a.system.push({source:"instructions",text:e(s.instructions)});const r=s.input;if(typeof r=="string")a.turns.push({kind:"message",role:"user",text:r});else for(const i of k(r))b(i)&&Z(t,i);a.tools=D(s),a.settings=C(s,ne);const o=c(c(s.text).format);e(o.type)&&o.type!=="text"&&(a.answer.format={type:e(o.type),...e(o.name)?{name:e(o.name)}:{},...o.schema!==void 0?{schema:o.schema}:{},...typeof o.strict=="boolean"?{strict:o.strict}:{}}),T(t,n,l,i=>q(t,i),i=>H(t,i,l))}v(R,"D"),_(R,"responsesCall");const re=[["reasoning_effort","reasoning effort"],["max_completion_tokens","max completion tokens"],["max_tokens","max tokens"],["temperature","temperature"],["top_p","top_p"],["tool_choice","tool choice"],["parallel_tool_calls","parallel tool calls"],["n","n"],["seed","seed"],["stop","stop"],["presence_penalty","presence penalty"],["frequency_penalty","frequency penalty"],["verbosity","verbosity"],["store","store"],["service_tier","service tier"],["stream","stream"],["user","user"]];function K(t){const s=k(t.tool_calls).map(c).map(l=>{const a=c(l.function);return $(e(a.name)??e(c(l.custom).name)??"?",e(a.arguments)??e(c(l.custom).input)??"",{callId:e(l.id)})}),n=c(t.function_call);return e(n.name)&&s.push($(e(n.name),e(n.arguments)??"",{})),s}v(K,"G"),_(K,"chatCalls");function z(t,s){const n=t.call.answer;e(s.model)&&(t.call.model??=e(s.model)),e(s.id)&&(t.call.responseId??=e(s.id));const l=c(k(s.choices)[0]),a=c(l.message),r=I(a.content).text;r&&(n.text=r),e(a.refusal)&&(n.refusal=e(a.refusal));const o=e(a.reasoning_content)??e(a.reasoning);o&&(n.reasoning=o),n.toolCalls.push(...K(a)),e(l.finish_reason)&&(n.finish=e(l.finish_reason)),n.status=n.finish?"completed":n.status??"unknown";const i=c(s.error);e(i.message)&&(n.error=e(i.message));const u=c(s.usage);Object.keys(u).length&&(t.call.usage={...h(u.prompt_tokens)!==void 0?{input:h(u.prompt_tokens)}:{},...h(c(u.prompt_tokens_details).cached_tokens)!==void 0?{cached:h(c(u.prompt_tokens_details).cached_tokens)}:{},...h(c(u.prompt_tokens_details).cache_write_tokens)?{written:h(c(u.prompt_tokens_details).cache_write_tokens)}:{},...h(u.completion_tokens)!==void 0?{output:h(u.completion_tokens)}:{},...h(c(u.completion_tokens_details).reasoning_tokens)!==void 0?{reasoning:h(c(u.completion_tokens_details).reasoning_tokens)}:{},...h(u.total_tokens)!==void 0?{total:h(u.total_tokens)}:{}})}v(z,"L"),_(z,"chatAnswer");function G(t,s,n){let l="",a="",r="",o,i,u,m,d=0;const f=new Map;for(const p of s){const y=J(p);if(!y)continue;y.cut&&d++,u??=e(y.v.model),m??=e(y.v.id),b(y.v.usage)&&(i=y.v.usage),b(y.v.error)&&(t.call.answer.error=e(y.v.error.message)??"the stream reported an error");const w=c(k(y.v.choices)[0]),x=c(w.delta);l+=e(x.content)??"",a+=e(x.reasoning_content)??e(x.reasoning)??"",r+=e(x.refusal)??"";for(const S of k(x.tool_calls).map(c)){const L=h(S.index)??0,B=f.get(L)??{name:"",args:""},P=c(S.function);e(S.id)&&(B.id=e(S.id)),B.name+=e(P.name)??"",B.args+=e(P.arguments)??"",f.set(L,B)}e(w.finish_reason)&&(o=e(w.finish_reason))}const g={content:l,...a?{reasoning_content:a}:{},...r?{refusal:r}:{},tool_calls:[...f.entries()].sort(([p],[y])=>p-y).map(([,p])=>({id:p.id,function:{name:p.name,arguments:p.args}}))};z(t,{...u?{model:u}:{},...m?{id:m}:{},choices:[{message:g,...o?{finish_reason:o}:{}}],...i?{usage:i}:{}}),d&&n.push(`${d} of the stream\u2019s chunks were kept to their first 4 KB \u2014 the answer was joined from what was kept of them.`)}v(G,"V"),_(G,"chatStream");function Q(t,s,n,l){const a=t.call;e(s.model)&&(a.model=e(s.model));for(const o of k(s.messages).map(c)){const i=e(o.role)??"user",{text:u,attachments:m}=I(o.content);if(i==="system")a.system.push({source:"system message",text:u});else if(i==="tool")O(t,e(o.tool_call_id),u);else if(i==="function")O(t,void 0,u);else{(u||m.length||i!=="assistant")&&a.turns.push({kind:"message",role:i,text:u,...m.length?{attachments:m}:{}});for(const d of K(o))j(t,d)}}a.tools=[...k(s.tools).map(c),...k(s.functions).map(o=>({type:"function",function:o}))].map(o=>{const i=c(o.function),u=e(o.type)??"function",m=e(i.name)??e(c(o.custom).name)??u;return{name:m,...splitToolName(m),type:u,...e(i.description)??e(c(o.custom).description)?{description:e(i.description)??e(c(o.custom).description)}:{},...i.parameters!==void 0?{parameters:i.parameters}:{},...typeof i.strict=="boolean"?{strict:i.strict}:{},called:0}}),a.settings=C(s,re);const r=c(s.response_format);if(e(r.type)&&r.type!=="text"){const o=c(r.json_schema);a.answer.format={type:e(r.type),...e(o.name)?{name:e(o.name)}:{},...o.schema!==void 0?{schema:o.schema}:{},...typeof o.strict=="boolean"?{strict:o.strict}:{}}}T(t,n,l,o=>z(t,o),o=>G(t,o,l))}v(Q,"X"),_(Q,"chatCall");const ae=[["max_tokens","max tokens"],["thinking.budget_tokens","thinking budget"],["temperature","temperature"],["top_p","top_p"],["top_k","top_k"],["tool_choice","tool choice"],["stop_sequences","stop sequences"],["stream","stream"],["metadata.user_id","user"]];function V(t,s,n){if(typeof n=="string"){t.call.turns.push({kind:"message",role:s,text:n});return}const l=[],a=[],r=_(()=>{(l.length||a.length)&&t.call.turns.push({kind:"message",role:s,text:l.join(`
14
+
15
+ `),...a.length?{attachments:[...a]}:{}}),l.length=0,a.length=0},"flush");for(const o of k(n).map(c)){const i=e(o.type)??"";i==="text"?l.push(e(o.text)??""):i==="tool_use"||i==="server_tool_use"||i==="mcp_tool_use"?(r(),j(t,$(e(o.name)??"?",A(o.input),{callId:e(o.id),server:e(o.server_name)}))):i==="tool_result"||i==="mcp_tool_result"||/_tool_result$/.test(i)?(r(),O(t,e(o.tool_use_id),N(o.content),o.is_error===!0?"the tool reported an error":void 0)):i==="thinking"||i==="redacted_thinking"?(r(),t.call.turns.push({kind:"reasoning",...e(o.thinking)?{text:e(o.thinking)}:{}})):a.push(i==="image"?"image":i==="document"?`document ${e(o.title)??""}`.trim():i)}r()}v(V,"ee"),_(V,"anthropicBlocks");function F(t,s){const n=t.call.answer;e(s.model)&&(t.call.model??=e(s.model)),e(s.id)&&(t.call.responseId??=e(s.id));const l=[],a=[];for(const i of k(s.content).map(c)){const u=e(i.type);u==="text"?l.push(e(i.text)??""):u==="tool_use"||u==="server_tool_use"||u==="mcp_tool_use"?n.toolCalls.push($(e(i.name)??"?",A(i.input),{callId:e(i.id),server:e(i.server_name)})):u==="thinking"&&a.push(e(i.thinking)??"")}l.length&&(n.text=l.join(`
16
+
17
+ `)),a.length&&(n.reasoning=a.join(`
18
+
19
+ `)),e(s.stop_reason)&&(n.finish=e(s.stop_reason)),n.status=s.type==="error"?"failed":n.finish?"completed":n.status??"unknown";const r=c(s.error);e(r.message)&&(n.error=e(r.message));const o=c(s.usage);if(Object.keys(o).length){const i=h(o.input_tokens),u=h(o.cache_read_input_tokens),m=h(o.output_tokens);t.call.usage={...i!==void 0?{input:i+(u??0)+(h(o.cache_creation_input_tokens)??0)}:{},...u!==void 0?{cached:u}:{},...h(o.cache_creation_input_tokens)?{written:h(o.cache_creation_input_tokens)}:{},...m!==void 0?{output:m}:{}}}}v(F,"P"),_(F,"anthropicAnswer");function X(t,s,n){let l={};const a=new Map,r=new Map;let o={},i=0;for(const u of s){const m=J(u);if(!m)continue;m.cut&&i++;const d=m.v,f=e(d.type)??u.event,g=h(d.index)??0;if(f==="message_start")l=c(d.message),o={...c(l.usage)};else if(f==="content_block_start")a.set(g,{...c(d.content_block)});else if(f==="content_block_delta"){const p=c(d.delta),y=a.get(g)??{};e(p.text)!==void 0&&(y.text=(e(y.text)??"")+e(p.text)),e(p.thinking)!==void 0&&(y.thinking=(e(y.thinking)??"")+e(p.thinking)),e(p.partial_json)!==void 0&&r.set(g,(r.get(g)??"")+e(p.partial_json)),a.set(g,y)}else f==="message_delta"?(l={...l,...c(d.delta)},o={...o,...c(d.usage)}):f==="error"&&(t.call.answer.error=e(c(d.error).message)??"the stream reported an error")}for(const[u,m]of r){const d=a.get(u);d&&(d.input=parseJsonPrefix(m)?.value??m)}F(t,{...l,content:[...a.entries()].sort(([u],[m])=>u-m).map(([,u])=>u),usage:o}),i&&n.push(`${i} of the stream\u2019s events were kept to their first 4 KB \u2014 the answer was joined from what was kept of them.`)}v(X,"te"),_(X,"anthropicStream");function Y(t,s,n,l){const a=t.call;e(s.model)&&(a.model=e(s.model));const r=s.system;if(typeof r=="string"&&r)a.system.push({source:"system",text:r});else for(const o of k(r).map(c))e(o.text)&&a.system.push({source:"system",text:e(o.text)});for(const o of k(s.messages).map(c))V(t,e(o.role)??"user",o.content);a.tools=[...k(s.tools).map(c).map(o=>{const i=e(o.name)??e(o.type)??"tool";return{name:i,...splitToolName(i),type:e(o.type)??"custom",...e(o.description)?{description:e(o.description)}:{},...o.input_schema!==void 0?{parameters:o.input_schema}:{},called:0}}),...k(s.mcp_servers).map(c).map(o=>({name:e(o.name)??"mcp",server:e(o.name)??"mcp",tool:"every tool",type:"mcp",...e(o.url)?{description:e(o.url)}:{},called:0}))],a.settings=C(s,ae),T(t,n,l,o=>F(t,o),o=>X(t,o,l))}v(Y,"ne"),_(Y,"anthropicCall");function ee(t,s,n,l){const a=t.call;e(s.model)&&(a.model=e(s.model));const r=s.input,o=typeof r=="string"?[r]:k(r),i=o.slice(0,200).map(p=>typeof p=="string"?p:typeof p=="number"?`token ${p}`:Array.isArray(p)?`${p.length} tokens`:A(p)),u=o.length>0&&o.every(p=>typeof p=="number");a.embedding={inputs:u?[`${o.length} tokens`]:i,count:u?1:o.length,...h(s.dimensions)!==void 0?{dimensions:h(s.dimensions)}:{}},a.settings=C(s,[["dimensions","dimensions"],["encoding_format","encoding"],["user","user"]]);const m=c(M(n.response.body,"answer",[])),d=k(m.data);if(d.length){a.embedding.vectors=d.length;const p=c(d[0]).embedding;Array.isArray(p)&&a.embedding.dimensions===void 0&&(a.embedding.dimensions=p.length)}const f=c(m.usage);h(f.prompt_tokens)!==void 0&&(a.usage={input:h(f.prompt_tokens),...h(f.total_tokens)!==void 0?{total:h(f.total_tokens)}:{}}),a.answer.status=n.status!==void 0&&n.status<400?"completed":"failed";const g=c(m.error);e(g.message)&&(a.answer.error=e(g.message))}v(ee,"se"),_(ee,"embeddingsCall");function T(t,s,n,l,a){const r=s.response.stream;if(r){if(r.events.length&&r.events.every(i=>i.data===void 0)){n.push("The answer streamed, and only each event\u2019s name, time and size were kept \u2014 an AI provider\u2019s bodies are kept only when asked for: Settings \u203A Features \u203A Capture \u203A AI calls.");return}a(r.events),r.cut&&n.push(`The stream was cut after ${r.events.length.toLocaleString("en-US")} of its ${r.total.toLocaleString("en-US")} events \u2014 what came after is not shown.`);return}const o=M(s.response.body,"answer",n);b(o)?l(o):s.error&&(t.call.answer.error=s.error),t.call.answer.status||(t.call.answer.status=s.error||(s.status??0)>=400?"failed":"unknown")}v(T,"N"),_(T,"answerOf");function te(t){return t.api==="embeddings"?"embedding":t.answer.format&&/json/.test(t.answer.format.type)?"decision":t.tools.length||t.answer.toolCalls.length||t.turns.some(s=>s.kind==="tool")?"tools":t.turns.filter(s=>s.kind==="message"&&(s.role==="user"||s.role==="assistant")).length>1?"chat":"prompt"}v(te,"oe"),_(te,"shapeOf");export function aiCallOf(t){if(!t.ai)return;const s=aiApiOf(t.path);if(!s)return;const n=[],l={call:{provider:t.ai.provider,api:s,shape:"prompt",settings:[],system:[],turns:[],tools:[],answer:{toolCalls:[]},gaps:n},byCallId:new Map},a=c(M(t.request.body,"request",n));s==="responses"?R(l,a,t,n):s==="chat"?Q(l,a,t,n):s==="messages"?Y(l,a,t,n):ee(l,a,t,n);const r=l.call;if(r.answer.text!==void 0&&(r.answer.format?/json/.test(r.answer.format.type):/^\s*[{[]/.test(r.answer.text))){const i=parseJsonPrefix(r.answer.text);i&&(b(i.value)||Array.isArray(i.value))&&(r.answer.json=i.value)}!r.answer.finish&&r.answer.toolCalls.length&&(r.answer.finish="tool calls");const o=new Map;for(const i of r.turns)i.kind==="tool"&&o.set(i.name,(o.get(i.name)??0)+1);for(const i of r.answer.toolCalls)o.set(i.name,(o.get(i.name)??0)+1);for(const i of r.tools)i.called=o.get(i.name)??0;r.shape=te(r);for(const i of r.system)i.text.endsWith(CUT_MARK)&&(i.cut=!0);return r}v(aiCallOf,"aiCallOf"),_(aiCallOf,"aiCallOf");export const SHAPE_LABEL={prompt:"Single prompt",chat:"Conversation",tools:"Tool calling",decision:"Structured decision",embedding:"Embedding"};export function prettyJson(t){const s=t.trim()?parseJsonPrefix(t):void 0;return s&&(b(s.value)||Array.isArray(s.value))?{value:s.value,text:t}:{text:t}}v(prettyJson,"prettyJson"),_(prettyJson,"prettyJson");