@lensmcp/protocol-types 1.25.31 → 1.25.33

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,9 @@ 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-playground.js';
20
23
  export * from './lib/pod-memory.js';
21
24
  export * from './lib/perf-rules.js';
22
25
  export * from './lib/sources.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-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";
@@ -0,0 +1,172 @@
1
+ import type { HttpExchange } from './http-exchange.js';
2
+ import { type AiApi, type AiShape } from './ai-call.js';
3
+ /**
4
+ * 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
5
+ * (one turn of an agent's tool loop, its rounds) it is part of, the action it was for, its flow.
6
+ *
7
+ * Names come from what the app said, in order: its own labels (OpenAI `metadata` — FoodGuard's conductor sends
8
+ * `agent`, `run`, `act`, `round`, `attempt`), the `x-lensmcp-agent` / `-run` / `-action` headers, else what the call
9
+ * itself says — the system prompt's title (`# Critical Limits Analyst — …`, `You are a compliance gate analyst …`),
10
+ * the structured answer's schema (`product_flow`), the tools' MCP server (`conductor__…`) — else the service. A run the
11
+ * app did not name is found from the calls themselves: one continuing another's answer (`previous_response_id`), or
12
+ * carrying the results of the tools another's answer called. What a person renames or merges ({@link AiNames}) wins.
13
+ *
14
+ * Pure, and shared: the server indexes the kept exchanges with it, the page groups by it.
15
+ */
16
+ /** Where a name came from: renamed by a person, said by the app, or read off the call. */
17
+ export type AiNameSource = 'renamed' | 'metadata' | 'header' | 'prompt' | 'schema' | 'tools' | 'service';
18
+ /** How the call's run was found: the app named it, it continues an answer, it carries tool results — or it is alone. */
19
+ export type AiRunSource = 'metadata' | 'header' | 'chain' | 'tools' | 'alone';
20
+ export interface AiNamed {
21
+ /** Stable across the calls it names (`agent:onboarding-chat-conductor`); what a rename or a merge is keyed by. */
22
+ key: string;
23
+ name: string;
24
+ from: AiNameSource;
25
+ }
26
+ export interface AiCallRow {
27
+ /** The exchange's id (`hx_…`): the call's page is `/ai/calls/<id>`. */
28
+ id: string;
29
+ at: number;
30
+ durationMs?: number;
31
+ status?: number;
32
+ /** The provider said it failed, or no answer came. */
33
+ failed?: true;
34
+ service: string;
35
+ flowId?: string;
36
+ requestId?: string;
37
+ provider: string;
38
+ host: string;
39
+ api: AiApi;
40
+ shape: AiShape;
41
+ model?: string;
42
+ streamed?: true;
43
+ tokens?: {
44
+ input?: number;
45
+ cached?: number;
46
+ output?: number;
47
+ reasoning?: number;
48
+ };
49
+ /** 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. */
50
+ costUsd?: number;
51
+ agent: AiNamed;
52
+ run: {
53
+ key: string;
54
+ from: AiRunSource;
55
+ };
56
+ /** Its place in its run, from 1 — set when the rows are linked ({@link linkAiRuns}). */
57
+ step?: number;
58
+ /** The app's own round and attempt (`metadata.round`, `metadata.attempt`). */
59
+ round?: number;
60
+ attempt?: number;
61
+ action?: AiNamed;
62
+ /** The structured answer's schema name. */
63
+ schema?: string;
64
+ /** The tools it was offered, by name (at most {@link MAX_TOOLS}). */
65
+ tools?: string[];
66
+ /** The tools its answer called, by name. */
67
+ called?: string[];
68
+ /** The ids of the tool calls in its answer: the next call of its run carries their results. */
69
+ callIds?: string[];
70
+ /** The ids of tool calls whose results it carries: what joins it to the call that asked for them. */
71
+ answers?: string[];
72
+ responseId?: string;
73
+ previousResponseId?: string;
74
+ /** The app's own labels, as strings (at most {@link MAX_META} of them). */
75
+ meta?: Record<string, string>;
76
+ /** A body was cut by the capture: some of the call is not readable. */
77
+ cut?: true;
78
+ /** What it was last given and what it answered, a line each — only where content may be shown. */
79
+ asked?: string;
80
+ said?: string;
81
+ }
82
+ /** A person's names for groups, kept in `.lensmcp/ai-names/names.json` and committed: the team sees the same names. */
83
+ export interface AiNames {
84
+ version: 1;
85
+ /** A group's key (`agent:…`, `action:…`) → its name. */
86
+ names: Record<string, string>;
87
+ /** A group's key → the key it was merged into: its calls are shown as that group's. */
88
+ merge: Record<string, string>;
89
+ }
90
+ export declare const EMPTY_AI_NAMES: AiNames;
91
+ /** `GET /api/ai/calls?from=&to=&rev=` — the AI calls the lens kept, in a window, newest first, named and in their runs. */
92
+ export interface AiCallsView {
93
+ /** Changes when a call, or a name, does: sent back as `rev`, an unchanged answer is `{ same: true }`. */
94
+ rev: string;
95
+ at: number;
96
+ rows: AiCallRow[];
97
+ /** Calls kept in all — the window may hold fewer — and when the first of them was made. */
98
+ total: number;
99
+ oldest?: number;
100
+ /** The kept calls are still being read for the first time: how far. */
101
+ reading?: {
102
+ done: number;
103
+ total: number;
104
+ };
105
+ names: AiNames;
106
+ /** What was said is in the rows (`asked`, `said`): a loopback-bound dashboard. */
107
+ content: boolean;
108
+ }
109
+ /** `GET /api/ai/call?id=` — one call: its row, the other calls of its run, and the exchange itself. */
110
+ export interface AiCallView {
111
+ row: AiCallRow;
112
+ /** Its run's calls by step, itself among them. */
113
+ run: AiCallRow[];
114
+ exchange?: HttpExchange;
115
+ /** Why the exchange is not here: a dashboard not bound to loopback, or a file no longer kept. */
116
+ withheld?: string;
117
+ names: AiNames;
118
+ }
119
+ /** `POST /api/ai/names` — rename a group (an empty name takes the rename back), merge one into another, or undo a merge. */
120
+ export type AiNamesChange = {
121
+ op: 'rename';
122
+ key: string;
123
+ name: string;
124
+ } | {
125
+ op: 'merge';
126
+ key: string;
127
+ into: string;
128
+ } | {
129
+ op: 'unmerge';
130
+ key: string;
131
+ };
132
+ export declare const MAX_TOOLS = 40;
133
+ export declare const MAX_META = 24;
134
+ /** A key out of a name: lower case, words joined by `-` (`Critical Limits Analyst` → `critical-limits-analyst`). */
135
+ export declare const aiKeyOf: (kind: "agent" | "action", name: string) => string;
136
+ /** A name as a person reads it: `onboarding-chat-conductor` → `Onboarding chat conductor`; one already written for people stays. */
137
+ export declare function humanName(s: string): string;
138
+ /**
139
+ * What a system prompt calls the one it instructs: its heading (`# Critical Limits Analyst — establish …` →
140
+ * `Critical Limits Analyst`), or who it says the model is (`You are a compliance gate analyst for …` →
141
+ * `Compliance gate analyst`). `undefined` when it says neither — its first words are not a name.
142
+ */
143
+ export declare function promptTitle(text: string): string | undefined;
144
+ /**
145
+ * A prompt that names no one, by how it opens — its first words (`You look at ONE product and say …`): two prompts
146
+ * that open alike are one agent, until a person names or merges it.
147
+ */
148
+ export declare function promptOpening(text: string): string | undefined;
149
+ /** An action's name without the ids in it: `ask:products:material.source-adoption.01a163b4-…::509c` → `ask:products:material.source-adoption`. */
150
+ export declare function actionName(s: string): string;
151
+ /**
152
+ * The row of one exchange — `undefined` when it is not an AI provider's call. `content: false` leaves out what was
153
+ * said (`asked`, `said`) for a dashboard not bound to loopback, as the Request & response panel does.
154
+ */
155
+ export declare function aiCallRow(x: HttpExchange, opts: {
156
+ content: boolean;
157
+ }): AiCallRow | undefined;
158
+ /**
159
+ * Joins the calls the app did not put in a run to the run they continue — the call whose answer they continue
160
+ * (`previous_response_id`) or whose tool calls they carry the results of — and numbers each run's calls by time
161
+ * (`step`). Rows are changed in place; any order in, the same result.
162
+ */
163
+ export declare function linkAiRuns(rows: AiCallRow[]): AiCallRow[];
164
+ /** Follows a merge to the group it ended in (a chain of merges, never a loop). */
165
+ export declare function mergedKey(names: AiNames, key: string): string;
166
+ /**
167
+ * A person's renames and merges over the rows: a merged group's calls take the key and name of the group they were
168
+ * merged into; a renamed group its new name (`from: 'renamed'`). Rows are changed in place.
169
+ */
170
+ export declare function applyAiNames(rows: AiCallRow[], names: AiNames): AiCallRow[];
171
+ /** A names file's content, read leniently: anything not a name or a merge is dropped. */
172
+ export declare function aiNamesOf(v: unknown): AiNames;
@@ -0,0 +1,3 @@
1
+ "use strict";var N=Object.defineProperty;var l=(e,n)=>N(e,"name",{value:n,configurable:!0});var B=Object.defineProperty,i=l((e,n)=>B(e,"name",{value:n,configurable:!0}),"a");import{aiCallOf as _}from"./ai-call.js";export const EMPTY_AI_NAMES={version:1,names:{},merge:{}},MAX_TOOLS=40,MAX_META=24;const R=160,x="unknown service";export const aiKeyOf=i((e,n)=>`${e}:${n.normalize("NFKD").toLowerCase().replace(/[^a-z0-9:.]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"unnamed"}`,"aiKeyOf");export function humanName(e){const n=e.trim();if(/[A-Z]/.test(n)||/ /.test(n))return n;const t=n.replace(/[-_.]+/g," ").trim();return t?t[0].toUpperCase()+t.slice(1):n}l(humanName,"humanName"),i(humanName,"humanName");const d=i((e,n=R)=>{if(!e)return;const t=e.replace(/\s+/g," ").trim();if(t)return t.length>n?`${t.slice(0,n-1)}\u2026`:t},"line");export function promptTitle(e){const n=e.split(`
2
+ `).map(o=>o.trim()).find(Boolean)??"",t=/^#{1,6}\s+(.+)$/.exec(n);if(t){const o=t[1].split(/\s+[—–]\s+|\s+-\s+|:\s/)[0].replace(/[*_`]/g,"").trim();return o?o.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(n);if(r){const o=r[1].trim().split(/\s+/);if(o.length>6||/^(?:going|about|given|now|here|responsible|able|allowed|expected|tasked|asked|helping|working|being|not)$/i.test(o[0]))return;const s=o.join(" ");return s[0].toUpperCase()+s.slice(1)}}l(promptTitle,"promptTitle"),i(promptTitle,"promptTitle");export function promptOpening(e){const n=e.split(`
3
+ `).map(r=>r.trim()).find(Boolean);if(!n)return;const t=n.replace(/^[#>*\s-]+/,"").split(/\s+/).filter(Boolean);if(!(t.length<3))return t.length>7?`${t.slice(0,7).join(" ")} \u2026`:t.join(" ")}l(promptOpening,"promptOpening"),i(promptOpening,"promptOpening");export function actionName(e){const n=e.split("::")[0],t=/^(?:[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 n.split(":").map(r=>r.split(".").filter(o=>!t.test(o)).join(".")).filter((r,o,s)=>r||o<s.length-1).join(":").slice(0,120)}l(actionName,"actionName"),i(actionName,"actionName");const h=i((e,n)=>e.request.headers.find(([t])=>t.toLowerCase()===n)?.[1],"header"),I=i(e=>e!==void 0&&/^\d{1,6}$/.test(e.trim())?Number(e.trim()):void 0,"intOf"),k=i((e,...n)=>{for(const t of n)if(e[t]?.trim())return e[t].trim()},"first");function O(e){const n={};for(const t of e.settings)if(t.name.startsWith("metadata.")){if(Object.keys(n).length>=MAX_META)break;n[t.name.slice(9)]=t.value.slice(0,200)}return n}l(O,"A"),i(O,"metaOf");function j(e,n,t){const r=i((p,g)=>({key:aiKeyOf("agent",p),name:humanName(p),from:g}),"named"),o=k(t,"agent","agent_name","agentName","assistant");if(o)return r(o,"metadata");const s=h(e,"x-lensmcp-agent");if(s?.trim())return r(s.trim(),"header");const a=n.system.map(p=>promptTitle(p.text)).find(Boolean);if(a)return r(a,"prompt");const u=n.answer.format?.name;if(u)return r(u,"schema");const f=[...n.tools,...n.answer.toolCalls].map(p=>p.server).find(Boolean);if(f)return r(f,"tools");const m=n.system.map(p=>promptOpening(p.text)).find(Boolean);return m?{key:aiKeyOf("agent",`prompt ${m.replace(/ …$/,"")}`),name:m,from:"prompt"}:r(`${e.service??x} ${n.api==="embeddings"?"embeddings":n.model??n.api}`,"service")}l(j,"T"),i(j,"agentOf");function C(e,n,t){const r=i((a,u)=>({key:aiKeyOf("action",a),name:a,from:u}),"named"),o=k(t,"act","action","task","step","operation");if(o&&actionName(o))return r(actionName(o),"metadata");const s=h(e,"x-lensmcp-action");if(s?.trim())return r(actionName(s.trim()),"header");if(n.answer.format?.name)return r(n.answer.format.name,"schema")}l(C,"_"),i(C,"actionOf");function A(e){if(e.kind==="message")return d(e.text);if(e.kind==="tool")return d(e.output!==void 0?`${e.tool} \u2192 ${e.output}`:`${e.tool}(${e.arguments})`)}l(A,"B"),i(A,"turnLine");function y(e){const n=e.answer;return n.error?d(`error: ${n.error}`):n.refusal?d(`refused: ${n.refusal}`):n.toolCalls.length?d(n.toolCalls.map(t=>`${t.tool}(${t.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}`:""}`:d(n.text)}l(y,"O"),i(y,"saidLine");export function aiCallRow(e,n){const t=_(e);if(!t||!e.ai)return;const r=O(t),o=new Map(t.settings.map(c=>[c.name,c.value])),s=k(r,"run","run_id","runId"),a=h(e,"x-lensmcp-run")?.trim(),u=t.turns.filter(c=>c.kind==="tool").filter(c=>c.output!==void 0&&c.callId).map(c=>c.callId),f=t.answer.toolCalls.map(c=>c.callId).filter(c=>!!c),m=t.usage,p=m?{...m.input!==void 0?{input:m.input}:{},...m.cached?{cached:m.cached}:{},...m.output!==void 0?{output:m.output}:{},...m.reasoning?{reasoning:m.reasoning}:{}}:void 0,g=C(e,t,r),v=I(r.round),w=I(r.attempt),$=o.get("continues response"),b=[...t.turns].reverse().map(A).find(Boolean),M=t.answer.status==="failed"||!!t.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}:{},...M?{failed:!0}:{},service:e.service??x,...e.flowId?{flowId:e.flowId}:{},...e.requestId?{requestId:e.requestId}:{},provider:e.ai.provider,host:e.host,api:t.api,shape:t.shape,...t.model?{model:t.model}:{},...e.response.stream?{streamed:!0}:{},...p&&Object.keys(p).length?{tokens:p}:{},agent:j(e,t,r),run:s?{key:`run:${s}`,from:"metadata"}:a?{key:`run:${a}`,from:"header"}:{key:`run:${e.id}`,from:"alone"},...v!==void 0?{round:v}:{},...w!==void 0?{attempt:w}:{},...g?{action:g}:{},...t.answer.format?.name?{schema:t.answer.format.name}:{},...t.tools.length?{tools:t.tools.slice(0,MAX_TOOLS).map(c=>c.name)}:{},...t.answer.toolCalls.length?{called:t.answer.toolCalls.map(c=>c.name)}:{},...f.length?{callIds:f}:{},...u.length?{answers:u}:{},...t.responseId?{responseId:t.responseId}:{},...$?{previousResponseId:$}:{},...Object.keys(r).length?{meta:r}:{},...t.gaps.some(c=>/kept to its first|cut/.test(c))?{cut:!0}:{},...n.content&&b?{asked:b}:{},...n.content&&y(t)?{said:y(t)}:{}}}l(aiCallRow,"aiCallRow"),i(aiCallRow,"aiCallRow");export function linkAiRuns(e){const n=[...e].sort((s,a)=>s.at-a.at||s.id.localeCompare(a.id)),t=new Map,r=new Map;for(const s of n){if(s.run.from==="alone"){const a=s.previousResponseId?t.get(s.previousResponseId):void 0,u=a?void 0:s.answers?.map(m=>r.get(m)).find(Boolean),f=a??u;if(f){const m=a?"chain":"tools";s.run={key:f.run.key,from:f.run.from==="alone"||f.run.from===m?m:f.run.from},f.run.from==="alone"&&(f.run={key:f.run.key,from:m})}}s.responseId&&t.set(s.responseId,s);for(const a of s.callIds??[])r.set(a,s)}const o=new Map;for(const s of n){const a=(o.get(s.run.key)??0)+1;o.set(s.run.key,a),s.step=a}return e}l(linkAiRuns,"linkAiRuns"),i(linkAiRuns,"linkAiRuns");export function mergedKey(e,n){let t=n;for(let r=0;r<8&&e.merge[t]&&e.merge[t]!==n;r++)t=e.merge[t];return t}l(mergedKey,"mergedKey"),i(mergedKey,"mergedKey");export function applyAiNames(e,n){if(!Object.keys(n.names).length&&!Object.keys(n.merge).length)return e;const t=new Map;for(const o of e)t.has(o.agent.key)||t.set(o.agent.key,o.agent),o.action&&!t.has(o.action.key)&&t.set(o.action.key,o.action);const r=i(o=>{const s=mergedKey(n,o.key),a=n.names[s];if(a)return{key:s,name:a,from:"renamed"};if(s===o.key)return o;const u=t.get(s);return{key:s,name:u?.name??humanName(s.replace(/^[a-z]+:/,"")),from:u?.from??o.from}},"resolve");for(const o of e)o.agent=r(o.agent),o.action&&(o.action=r(o.action));return e}l(applyAiNames,"applyAiNames"),i(applyAiNames,"applyAiNames");export function aiNamesOf(e){const n=e&&typeof e=="object"?e:{},t=i(r=>{const o={};if(r&&typeof r=="object"&&!Array.isArray(r))for(const[s,a]of Object.entries(r))/^(agent|action):/.test(s)&&typeof a=="string"&&a.trim()&&(o[s]=a.trim().slice(0,120));return o},"pick");return{version:1,names:t(n.names),merge:t(n.merge)}}l(aiNamesOf,"aiNamesOf"),i(aiNamesOf,"aiNamesOf");
@@ -0,0 +1,144 @@
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
+ output?: number;
112
+ reasoning?: number;
113
+ total?: number;
114
+ }
115
+ /** Where a cut string ends, in what is shown. */
116
+ export declare const CUT_MARK = "\u2026";
117
+ /**
118
+ * JSON that may have been cut anywhere — a body the capture kept to its first 64 KB, a stream event kept to its first
119
+ * 4 KB: what parses of it, closed where it stopped. A string cut in the middle keeps what arrived (marked `…`); a key, a
120
+ * number or a literal cut in the middle is dropped with its member. `undefined` when it is not JSON at all.
121
+ */
122
+ export declare function parseJsonPrefix(text: string): {
123
+ value: unknown;
124
+ cut: boolean;
125
+ } | undefined;
126
+ /** Which API a call went to, by its path. */
127
+ export declare function aiApiOf(path: string): AiApi | undefined;
128
+ /**
129
+ * A tool's server and its own name. A hosted MCP tool names its server (`server_label`); a client that hands MCP tools to
130
+ * the model as functions names them `server__tool` (`conductor__fill_question`), or `mcp__server__tool`.
131
+ */
132
+ export declare function splitToolName(name: string, server?: string): {
133
+ server?: string;
134
+ tool: string;
135
+ };
136
+ /** The call, read — or `undefined` when the exchange is not an AI provider's call this reads. */
137
+ export declare function aiCallOf(x: HttpExchange): AiCall | undefined;
138
+ /** How each kind of call reads, for a person. */
139
+ export declare const SHAPE_LABEL: Record<AiShape, string>;
140
+ /** A JSON text pretty — or as it was, when it is not JSON. */
141
+ export declare function prettyJson(text: string): {
142
+ value?: unknown;
143
+ text: string;
144
+ };
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,g=v((t,s)=>oe(t,"name",{value:s,configurable:!0}),"d");const b=g(t=>!!t&&typeof t=="object"&&!Array.isArray(t),"isObj"),e=g(t=>typeof t=="string"?t:void 0,"str"),y=g(t=>typeof t=="number"&&Number.isFinite(t)?t:void 0,"num"),k=g(t=>Array.isArray(t)?t:[],"arr"),c=g(t=>b(t)?t:{},"obj"),A=g(t=>typeof t=="string"?t:t===void 0?"":JSON.stringify(t),"json"),U=g(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=g(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 _=m;for(;_<o&&!`,}]
3
+ \r `.includes(t[_]);)_++;if(_>=o)break;i(_),m=_-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"),g(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"),g(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"),g(splitToolName,"splitToolName");const $=g((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"),g(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"),g(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"),g(I,"contentOf");const N=g(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"),g(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"),g(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"),g(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=g(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"),g(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"),g(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={...y(r.input_tokens)!==void 0?{input:y(r.input_tokens)}:{},...y(c(r.input_tokens_details).cached_tokens)!==void 0?{cached:y(c(r.input_tokens_details).cached_tokens)}:{},...y(r.output_tokens)!==void 0?{output:y(r.output_tokens)}:{},...y(c(r.output_tokens_details).reasoning_tokens)!==void 0?{reasoning:y(c(r.output_tokens_details).reasoning_tokens)}:{},...y(r.total_tokens)!==void 0?{total:y(r.total_tokens)}:{}})}v(q,"z"),g(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"),g(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 _=J(f);if(!_)continue;_.cut&&a++;const p=_.v,h=e(p.type)??f.event,w=y(p.output_index)??0;if(h==="response.completed"||h==="response.incomplete"||h==="response.failed"){if(!_.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(h==="response.created"||h==="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 h==="response.output_item.added"||h==="response.output_item.done"?b(p.item)&&(!o.has(w)||!_.cut)&&(r.set(w,{...r.get(w)??{},...p.item}),h.endsWith(".done")&&!_.cut&&o.add(w)):h==="response.output_text.delta"||h==="response.refusal.delta"?i.set(w,(i.get(w)??"")+(e(p.delta)??"")):h==="response.output_text.done"&&!_.cut&&e(p.text)!==void 0?i.set(w,e(p.text)):h==="response.function_call_arguments.delta"||h==="response.custom_tool_call_input.delta"||h==="response.mcp_call_arguments.delta"?u.set(w,(u.get(w)??"")+(e(p.delta)??"")):(h==="response.function_call_arguments.done"||h==="response.mcp_call_arguments.done")&&!_.cut&&e(p.arguments)!==void 0?u.set(w,e(p.arguments)):h==="response.reasoning_summary_text.delta"?m.set(w,(m.get(w)??"")+(e(p.delta)??"")):(h==="error"||h==="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],[_])=>f-_).map(([f,_])=>{if(o.has(f))return _;const p=e(_.type);return p==="message"&&i.has(f)?{..._,content:[{type:"output_text",text:i.get(f)}]}:(p==="function_call"||p==="mcp_call")&&u.has(f)?{..._,arguments:u.get(f)}:p==="custom_tool_call"&&u.has(f)?{..._,input:u.get(f)}:p==="reasoning"&&m.has(f)?{..._,summary:[{type:"summary_text",text:m.get(f)}]}:_});for(const[f,_]of i)r.has(f)||d.push({type:"message",content:[{type:"output_text",text:_}]});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"),g(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"),g(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"),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={...y(u.prompt_tokens)!==void 0?{input:y(u.prompt_tokens)}:{},...y(c(u.prompt_tokens_details).cached_tokens)!==void 0?{cached:y(c(u.prompt_tokens_details).cached_tokens)}:{},...y(u.completion_tokens)!==void 0?{output:y(u.completion_tokens)}:{},...y(c(u.completion_tokens_details).reasoning_tokens)!==void 0?{reasoning:y(c(u.completion_tokens_details).reasoning_tokens)}:{},...y(u.total_tokens)!==void 0?{total:y(u.total_tokens)}:{}})}v(z,"L"),g(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 h=J(p);if(!h)continue;h.cut&&d++,u??=e(h.v.model),m??=e(h.v.id),b(h.v.usage)&&(i=h.v.usage),b(h.v.error)&&(t.call.answer.error=e(h.v.error.message)??"the stream reported an error");const w=c(k(h.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=y(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 _={content:l,...a?{reasoning_content:a}:{},...r?{refusal:r}:{},tool_calls:[...f.entries()].sort(([p],[h])=>p-h).map(([,p])=>({id:p.id,function:{name:p.name,arguments:p.args}}))};z(t,{...u?{model:u}:{},...m?{id:m}:{},choices:[{message:_,...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(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"),g(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=g(()=>{(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"),g(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=y(o.input_tokens),u=y(o.cache_read_input_tokens),m=y(o.output_tokens);t.call.usage={...i!==void 0?{input:i+(u??0)+(y(o.cache_creation_input_tokens)??0)}:{},...u!==void 0?{cached:u}:{},...m!==void 0?{output:m}:{}}}}v(F,"P"),g(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,_=y(d.index)??0;if(f==="message_start")l=c(d.message),o={...c(l.usage)};else if(f==="content_block_start")a.set(_,{...c(d.content_block)});else if(f==="content_block_delta"){const p=c(d.delta),h=a.get(_)??{};e(p.text)!==void 0&&(h.text=(e(h.text)??"")+e(p.text)),e(p.thinking)!==void 0&&(h.thinking=(e(h.thinking)??"")+e(p.thinking)),e(p.partial_json)!==void 0&&r.set(_,(r.get(_)??"")+e(p.partial_json)),a.set(_,h)}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"),g(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"),g(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,...y(s.dimensions)!==void 0?{dimensions:y(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);y(f.prompt_tokens)!==void 0&&(a.usage={input:y(f.prompt_tokens),...y(f.total_tokens)!==void 0?{total:y(f.total_tokens)}:{}}),a.answer.status=n.status!==void 0&&n.status<400?"completed":"failed";const _=c(m.error);e(_.message)&&(a.answer.error=e(_.message))}v(ee,"se"),g(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"),g(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"),g(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"),g(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"),g(prettyJson,"prettyJson");
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The AI calls page's playground (planning/ai-calls D1, D4): a kept call sent again — to another model, its prompt,
3
+ * input or tools changed — on the lens's own keys, paid from the click allowance. A tool the model calls gets the
4
+ * result recorded in the original run (or one a person typed); nothing reaches the running app.
5
+ */
6
+ /** A tool's result the playground gives back: recorded in the call's run, or typed by a person. */
7
+ export interface AiToolResult {
8
+ name: string;
9
+ /** The arguments it answers, as sent (JSON); absent on a person's: it answers any call of that tool. */
10
+ arguments?: string;
11
+ output: string;
12
+ }
13
+ /** `GET /api/ai/playground?id=` — what the playground can do with one call. */
14
+ export interface AiPlaygroundInfo {
15
+ /** The models the lens's keys can send to, the call's own first; the cost of a step estimated from the call's tokens. */
16
+ models: Array<{
17
+ model: string;
18
+ provider: 'openai' | 'anthropic';
19
+ estimateUsd?: number;
20
+ own?: true;
21
+ }>;
22
+ /** The call's request as it was sent (the Responses API body), to start from. */
23
+ body?: Record<string, unknown>;
24
+ /** Every tool result its run recorded, oldest first. */
25
+ recorded: Array<AiToolResult & {
26
+ at: number;
27
+ }>;
28
+ /** Today's spend on clicks, and the allowance. */
29
+ spentTodayUsd: number;
30
+ capUsd: number;
31
+ /** Why the playground cannot send this call. */
32
+ unavailable?: string;
33
+ }
34
+ /** `POST /api/ai/playground` — one run of the playground, on one model. */
35
+ export interface AiPlaygroundRun {
36
+ id: string;
37
+ model: string;
38
+ /** The request as edited (the Responses API body); absent: as it was sent. */
39
+ body?: Record<string, unknown>;
40
+ /** Results a person typed: theirs before the recorded ones. */
41
+ results?: AiToolResult[];
42
+ /** Steps of the tool loop at most: 1–8 (default 6). */
43
+ maxSteps?: number;
44
+ }
45
+ export interface AiPlaygroundStep {
46
+ /** The provider's API, its request and its answer as sent and received — read like a kept call. */
47
+ api: 'responses' | 'messages';
48
+ sent: Record<string, unknown>;
49
+ response: Record<string, unknown>;
50
+ model: string;
51
+ usage: {
52
+ input: number;
53
+ output: number;
54
+ cacheRead?: number;
55
+ };
56
+ costUsd?: number;
57
+ latencyMs: number;
58
+ /** The tools it called this step: each with the result given back, and where that came from. */
59
+ tools: Array<{
60
+ name: string;
61
+ arguments: string;
62
+ output?: string;
63
+ from: 'recorded' | 'edited' | 'missing';
64
+ }>;
65
+ }
66
+ export interface AiPlaygroundAnswer {
67
+ model: string;
68
+ provider: 'openai' | 'anthropic';
69
+ /** The key that paid. */
70
+ keyLabel?: string;
71
+ steps: AiPlaygroundStep[];
72
+ /** It answered; a tool's result is missing (type one, run again); the step limit; a call failed. */
73
+ stopped: 'answered' | 'missing-result' | 'max-steps' | 'failed';
74
+ error?: string;
75
+ costUsd: number;
76
+ }
@@ -0,0 +1 @@
1
+ "use strict";export{};
@@ -148,8 +148,8 @@ export declare const MemoryOwnerAttrsSchema: z.ZodObject<{
148
148
  }, z.core.$strip>;
149
149
  export type MemoryOwnerAttrs = z.infer<typeof MemoryOwnerAttrsSchema>;
150
150
  export declare const MemoryMutationOperationSchema: z.ZodEnum<{
151
- push: "push";
152
151
  set: "set";
152
+ push: "push";
153
153
  add: "add";
154
154
  delete: "delete";
155
155
  clear: "clear";
@@ -161,8 +161,8 @@ export type MemoryMutationOperation = z.infer<typeof MemoryMutationOperationSche
161
161
  export declare const MemoryMutationAttrsSchema: z.ZodObject<{
162
162
  ownerNodeId: z.ZodString;
163
163
  operation: z.ZodEnum<{
164
- push: "push";
165
164
  set: "set";
165
+ push: "push";
166
166
  add: "add";
167
167
  delete: "delete";
168
168
  clear: "clear";
@@ -255,9 +255,9 @@ export declare const VisualRuleTimingSchema: z.ZodEnum<{
255
255
  }>;
256
256
  export type VisualRuleTiming = z.infer<typeof VisualRuleTimingSchema>;
257
257
  export declare const VisualViolationSeveritySchema: z.ZodEnum<{
258
+ error: "error";
258
259
  info: "info";
259
260
  warning: "warning";
260
- error: "error";
261
261
  }>;
262
262
  export type VisualViolationSeverity = z.infer<typeof VisualViolationSeveritySchema>;
263
263
  export declare const VisualViolationAttrsSchema: z.ZodObject<{
@@ -274,9 +274,9 @@ export declare const VisualViolationAttrsSchema: z.ZodObject<{
274
274
  responsive: "responsive";
275
275
  }>;
276
276
  severity: z.ZodEnum<{
277
+ error: "error";
277
278
  info: "info";
278
279
  warning: "warning";
279
- error: "error";
280
280
  }>;
281
281
  timing: z.ZodEnum<{
282
282
  always: "always";
package/lib/context.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { z } from 'zod';
2
2
  export declare const FlowOriginTypeSchema: z.ZodEnum<{
3
- request: "request";
4
3
  "user-click": "user-click";
5
4
  "user-input": "user-input";
6
5
  keyboard: "keyboard";
@@ -26,6 +25,7 @@ export declare const FlowOriginTypeSchema: z.ZodEnum<{
26
25
  "page-load": "page-load";
27
26
  "form-submit": "form-submit";
28
27
  "input-change": "input-change";
28
+ request: "request";
29
29
  job: "job";
30
30
  }>;
31
31
  export type FlowOriginType = z.infer<typeof FlowOriginTypeSchema>;
@@ -38,7 +38,6 @@ export declare const TraceContextSchema: z.ZodObject<{
38
38
  url: z.ZodOptional<z.ZodString>;
39
39
  flowId: z.ZodOptional<z.ZodString>;
40
40
  originType: z.ZodOptional<z.ZodEnum<{
41
- request: "request";
42
41
  "user-click": "user-click";
43
42
  "user-input": "user-input";
44
43
  keyboard: "keyboard";
@@ -64,6 +63,7 @@ export declare const TraceContextSchema: z.ZodObject<{
64
63
  "page-load": "page-load";
65
64
  "form-submit": "form-submit";
66
65
  "input-change": "input-change";
66
+ request: "request";
67
67
  job: "job";
68
68
  }>>;
69
69
  originNodeId: z.ZodOptional<z.ZodString>;
@@ -35,6 +35,8 @@ export interface DashHello {
35
35
  workspace: DashWorkspace;
36
36
  /** The log ring's current sequence — resume `/api/logs?since=` from here. */
37
37
  logSeq: number;
38
+ /** The lensmcp this dashboard runs — read at its start, so an upgrade after it started does not change it. */
39
+ version?: string;
38
40
  }
39
41
  /**
40
42
  * Whether the dev gateway in front of this workspace is serving.
@@ -485,6 +487,13 @@ export interface DashFlowNode {
485
487
  streamed?: boolean;
486
488
  /** A call out whose request and response were captured (`/api/exchange?id=`). */
487
489
  exchangeId?: string;
490
+ /**
491
+ * A call out to one of the cluster's own hosts (`*.localhost`): a hop through the gateway to another service, drawn
492
+ * in the calling service's row — not an External API.
493
+ */
494
+ hop?: true;
495
+ /** On a {@link hop}: the id of the request it opened (`x-lensmcp-request-id`) — the callee's leg hangs off it. */
496
+ calleeRequestId?: string;
488
497
  /**
489
498
  * A SQL run's every parameter and its result rows (`/api/sql-values?id=`), or a cache command's every argument and
490
499
  * its reply (`cav_`), kept beside the bus.
@@ -3563,8 +3572,23 @@ export interface DashAgentEvent {
3563
3572
  /** The answer's size. */
3564
3573
  bytes?: number;
3565
3574
  isError?: boolean;
3566
- /** What the answer said first, a line (a tool's first text; a read's MIME type). */
3575
+ /** What the answer said first, a line (a tool's first text; a read's MIME type) — a failed call's error message. */
3567
3576
  result?: string;
3577
+ /** Why it failed, when it threw: the error the agent was answered with. */
3578
+ failure?: DashAgentFailure;
3579
+ }
3580
+ /** The error a failed call threw — its class, the lens's reason (`NOT_FOUND`, `UNAVAILABLE`…), message and stack. */
3581
+ export interface DashAgentFailure {
3582
+ /** The error's class: `PublicMcpError`, `InvalidInputError`, `ExecutionTimeoutError`, `Error`… */
3583
+ type: string;
3584
+ /** Its MCP error code, when it has one (`NOT_FOUND`, `UNAVAILABLE`, `REFUSED`, `INVALID_INPUT`…). */
3585
+ status?: string;
3586
+ /** The message, redacted — at most 1 KB. */
3587
+ message: string;
3588
+ /** Its first stack frames, redacted. */
3589
+ stack?: string;
3590
+ /** The SDK's id for it (`err_…`), as the agent was told it. */
3591
+ errorId?: string;
3568
3592
  }
3569
3593
  /** One MCP session — one agent connected through the lensmcp plugin and the shim to this lens. */
3570
3594
  export interface DashAgentSession {
@@ -3604,6 +3628,9 @@ export interface DashAgentSession {
3604
3628
  errors: number;
3605
3629
  lastMs?: number;
3606
3630
  p95Ms?: number;
3631
+ /** The lensmcp the lens server runs and the shim's — each as it loaded it at its start; absent when it did not say. */
3632
+ serverVersion?: string;
3633
+ shimVersion?: string;
3607
3634
  };
3608
3635
  /** Newest last, at most 500. */
3609
3636
  events: DashAgentEvent[];
@@ -3613,3 +3640,29 @@ export interface DashAgentSessions {
3613
3640
  /** Live first, then by last seen; at most 20 per server. */
3614
3641
  sessions: DashAgentSession[];
3615
3642
  }
3643
+ /** One process of the lens: the dashboard, the gateway, a lens MCP server, an agent's shim. */
3644
+ export interface DashRunningVersion {
3645
+ role: 'dashboard' | 'gateway' | 'mcp-server' | 'shim';
3646
+ /** As a person names it: `Dashboard`, `Gateway`, `Lens MCP server`, `Claude Code's shim`. */
3647
+ label: string;
3648
+ pid?: number;
3649
+ /** The lensmcp it loaded at its start — absent when it does not say (a lensmcp from before 1.25.32). */
3650
+ version?: string;
3651
+ startedAt?: number;
3652
+ /**
3653
+ * `current`: it runs the installed lensmcp. `stale`: it runs an older one — it says so, or it started before the
3654
+ * installed one was put in place. `unknown`: neither can be told.
3655
+ */
3656
+ state: 'current' | 'stale' | 'unknown';
3657
+ /** One line on how that was decided, and what loads the installed one. */
3658
+ note: string;
3659
+ }
3660
+ export interface DashVersions {
3661
+ at: number;
3662
+ /** The lensmcp in this workspace's node_modules now, and when it was put there. */
3663
+ installed?: {
3664
+ version: string;
3665
+ at?: number;
3666
+ };
3667
+ processes: DashRunningVersion[];
3668
+ }
package/lib/edge.d.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  import { z } from 'zod';
2
2
  export declare const EdgeAxisSchema: z.ZodEnum<{
3
- state: "state";
4
- network: "network";
5
- data: "data";
6
3
  db: "db";
7
- visual: "visual";
8
4
  memory: "memory";
5
+ visual: "visual";
9
6
  render: "render";
7
+ state: "state";
8
+ network: "network";
10
9
  backend: "backend";
11
10
  performance: "performance";
12
11
  lifecycle: "lifecycle";
13
12
  ownership: "ownership";
14
13
  caused: "caused";
15
14
  async: "async";
15
+ data: "data";
16
16
  }>;
17
17
  export type EdgeAxis = z.infer<typeof EdgeAxisSchema>;
18
18
  export declare const TraceEdgeSchema: z.ZodObject<{
@@ -20,19 +20,19 @@ export declare const TraceEdgeSchema: z.ZodObject<{
20
20
  from: z.ZodString;
21
21
  to: z.ZodString;
22
22
  axis: z.ZodEnum<{
23
- state: "state";
24
- network: "network";
25
- data: "data";
26
23
  db: "db";
27
- visual: "visual";
28
24
  memory: "memory";
25
+ visual: "visual";
29
26
  render: "render";
27
+ state: "state";
28
+ network: "network";
30
29
  backend: "backend";
31
30
  performance: "performance";
32
31
  lifecycle: "lifecycle";
33
32
  ownership: "ownership";
34
33
  caused: "caused";
35
34
  async: "async";
35
+ data: "data";
36
36
  }>;
37
37
  type: z.ZodString;
38
38
  timestamp: z.ZodNumber;
@@ -63,3 +63,51 @@ export interface EventLoopsView {
63
63
  persistence: 'disk' | 'memory';
64
64
  retentionDays: number;
65
65
  }
66
+ /**
67
+ * A loop's delay over one short window — `[end of the window (epoch ms), mean, p99, max (ms), utilization (0–1)]`.
68
+ * A Node pod measures one every 5 s (`LENSMCP_EVENT_LOOP_STEP_MS`) and carries every one since its last report in the
69
+ * next `event-loop-lag` event (`raw.samples`): the history costs the bus no event of its own.
70
+ */
71
+ export type EventLoopSample = [at: number, meanMs: number, p99Ms: number, maxMs: number, utilization: number];
72
+ /**
73
+ * One bucket of a loop's samples — `[bucket start (epoch ms), mean, p99, max, utilization]`. max and p99 are the
74
+ * bucket's highest, so a spike survives any bucket width; mean and utilization are its average.
75
+ */
76
+ export type EventLoopPoint = [at: number, meanMs: number, p99Ms: number, maxMs: number, utilization: number];
77
+ /** One loop — a process lifetime, so a restart or a hot swap starts a new series — over the asked window. */
78
+ export interface EventLoopSeries {
79
+ loop: EventLoopIdentity;
80
+ /** The first and the last sample the lens holds of this loop (all time, not only the window). */
81
+ firstAt: number;
82
+ lastAt: number;
83
+ points: EventLoopPoint[];
84
+ }
85
+ /** The stall incidents of one loop in one bucket, for a marker: what a click opens is read from `/api/event-loops`. */
86
+ export interface EventLoopStallMark {
87
+ loopId: string;
88
+ /** The loop's service, so a loop with stalls and no samples (a browser page) still gets its chart. */
89
+ project: string;
90
+ runtime: EventLoopIdentity['runtime'];
91
+ /** The bucket's start, as the points'. */
92
+ at: number;
93
+ count: number;
94
+ longestMs: number;
95
+ /** One of them was not responding when last observed. */
96
+ unresponsive: boolean;
97
+ }
98
+ /** `/api/event-loops/history`: every loop's delay over a window, bucketed, with its stalls. */
99
+ export interface EventLoopHistory {
100
+ from: number;
101
+ to: number;
102
+ /** The bucket width. */
103
+ stepMs: number;
104
+ /** What the buckets were made of: the 5 s samples (kept `samplesHours`), or the 5-minute rollups (kept `retentionDays`). */
105
+ source: 'samples' | 'rollups';
106
+ series: EventLoopSeries[];
107
+ /** More loops had samples in the window than are listed: the most recently seen are. */
108
+ seriesTruncated?: boolean;
109
+ stalls: EventLoopStallMark[];
110
+ persistence: 'disk' | 'memory';
111
+ samplesHours: number;
112
+ retentionDays: number;
113
+ }
package/lib/event.d.ts CHANGED
@@ -1,14 +1,5 @@
1
1
  import { z } from 'zod';
2
2
  export declare const EventSourceSchema: z.ZodEnum<{
3
- gateway: "gateway";
4
- ai: "ai";
5
- db: "db";
6
- redis: "redis";
7
- queue: "queue";
8
- external: "external";
9
- visual: "visual";
10
- react: "react";
11
- memory: "memory";
12
3
  nx: "nx";
13
4
  vite: "vite";
14
5
  typescript: "typescript";
@@ -16,42 +7,51 @@ export declare const EventSourceSchema: z.ZodEnum<{
16
7
  rollup: "rollup";
17
8
  "client-runtime": "client-runtime";
18
9
  chrome: "chrome";
10
+ react: "react";
19
11
  valtio: "valtio";
20
12
  nestjs: "nestjs";
21
13
  nextjs: "nextjs";
14
+ db: "db";
15
+ redis: "redis";
16
+ queue: "queue";
17
+ memory: "memory";
18
+ visual: "visual";
22
19
  perf: "perf";
23
20
  security: "security";
21
+ gateway: "gateway";
22
+ external: "external";
23
+ ai: "ai";
24
24
  lens: "lens";
25
25
  custom: "custom";
26
26
  }>;
27
27
  export type EventSource = z.infer<typeof EventSourceSchema>;
28
28
  export declare const EventCategorySchema: z.ZodEnum<{
29
- build: "build";
30
- typecheck: "typecheck";
31
- lint: "lint";
32
- cluster: "cluster";
33
- agent: "agent";
34
- state: "state";
35
- network: "network";
36
- ai: "ai";
37
29
  db: "db";
38
- test: "test";
39
- visual: "visual";
40
30
  memory: "memory";
41
- trace: "trace";
31
+ visual: "visual";
42
32
  security: "security";
33
+ ai: "ai";
34
+ build: "build";
35
+ lint: "lint";
36
+ typecheck: "typecheck";
37
+ test: "test";
43
38
  runtime: "runtime";
44
39
  render: "render";
40
+ trace: "trace";
41
+ state: "state";
42
+ network: "network";
45
43
  backend: "backend";
46
44
  performance: "performance";
47
45
  deps: "deps";
46
+ cluster: "cluster";
47
+ agent: "agent";
48
48
  }>;
49
49
  export type EventCategory = z.infer<typeof EventCategorySchema>;
50
50
  export declare const SeveritySchema: z.ZodEnum<{
51
+ error: "error";
51
52
  debug: "debug";
52
53
  info: "info";
53
54
  warning: "warning";
54
- error: "error";
55
55
  fatal: "fatal";
56
56
  }>;
57
57
  export type Severity = z.infer<typeof SeveritySchema>;
@@ -60,15 +60,6 @@ export declare const BaseEventSchema: z.ZodObject<{
60
60
  sessionId: z.ZodString;
61
61
  timestamp: z.ZodNumber;
62
62
  source: z.ZodEnum<{
63
- gateway: "gateway";
64
- ai: "ai";
65
- db: "db";
66
- redis: "redis";
67
- queue: "queue";
68
- external: "external";
69
- visual: "visual";
70
- react: "react";
71
- memory: "memory";
72
63
  nx: "nx";
73
64
  vite: "vite";
74
65
  typescript: "typescript";
@@ -76,40 +67,49 @@ export declare const BaseEventSchema: z.ZodObject<{
76
67
  rollup: "rollup";
77
68
  "client-runtime": "client-runtime";
78
69
  chrome: "chrome";
70
+ react: "react";
79
71
  valtio: "valtio";
80
72
  nestjs: "nestjs";
81
73
  nextjs: "nextjs";
74
+ db: "db";
75
+ redis: "redis";
76
+ queue: "queue";
77
+ memory: "memory";
78
+ visual: "visual";
82
79
  perf: "perf";
83
80
  security: "security";
81
+ gateway: "gateway";
82
+ external: "external";
83
+ ai: "ai";
84
84
  lens: "lens";
85
85
  custom: "custom";
86
86
  }>;
87
87
  category: z.ZodEnum<{
88
- build: "build";
89
- typecheck: "typecheck";
90
- lint: "lint";
91
- cluster: "cluster";
92
- agent: "agent";
93
- state: "state";
94
- network: "network";
95
- ai: "ai";
96
88
  db: "db";
97
- test: "test";
98
- visual: "visual";
99
89
  memory: "memory";
100
- trace: "trace";
90
+ visual: "visual";
101
91
  security: "security";
92
+ ai: "ai";
93
+ build: "build";
94
+ lint: "lint";
95
+ typecheck: "typecheck";
96
+ test: "test";
102
97
  runtime: "runtime";
103
98
  render: "render";
99
+ trace: "trace";
100
+ state: "state";
101
+ network: "network";
104
102
  backend: "backend";
105
103
  performance: "performance";
106
104
  deps: "deps";
105
+ cluster: "cluster";
106
+ agent: "agent";
107
107
  }>;
108
108
  severity: z.ZodEnum<{
109
+ error: "error";
109
110
  debug: "debug";
110
111
  info: "info";
111
112
  warning: "warning";
112
- error: "error";
113
113
  fatal: "fatal";
114
114
  }>;
115
115
  context: z.ZodObject<{
@@ -121,7 +121,6 @@ export declare const BaseEventSchema: z.ZodObject<{
121
121
  url: z.ZodOptional<z.ZodString>;
122
122
  flowId: z.ZodOptional<z.ZodString>;
123
123
  originType: z.ZodOptional<z.ZodEnum<{
124
- request: "request";
125
124
  "user-click": "user-click";
126
125
  "user-input": "user-input";
127
126
  keyboard: "keyboard";
@@ -147,6 +146,7 @@ export declare const BaseEventSchema: z.ZodObject<{
147
146
  "page-load": "page-load";
148
147
  "form-submit": "form-submit";
149
148
  "input-change": "input-change";
149
+ request: "request";
150
150
  job: "job";
151
151
  }>>;
152
152
  originNodeId: z.ZodOptional<z.ZodString>;
package/lib/node.d.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  import { z } from 'zod';
2
2
  export declare const TraceNodeTypeSchema: z.ZodEnum<{
3
- session: "session";
4
- route: "route";
5
- page: "page";
6
3
  effect: "effect";
7
4
  timer: "timer";
8
5
  "hmr-update": "hmr-update";
6
+ route: "route";
9
7
  render: "render";
10
8
  promise: "promise";
9
+ session: "session";
11
10
  "browser-context": "browser-context";
12
11
  tab: "tab";
13
12
  renderer: "renderer";
14
13
  router: "router";
14
+ page: "page";
15
15
  component: "component";
16
16
  element: "element";
17
17
  slot: "slot";
@@ -64,9 +64,9 @@ export declare const TraceNodeTypeSchema: z.ZodEnum<{
64
64
  }>;
65
65
  export type TraceNodeType = z.infer<typeof TraceNodeTypeSchema>;
66
66
  export declare const LifecycleSchema: z.ZodEnum<{
67
+ created: "created";
67
68
  active: "active";
68
69
  completed: "completed";
69
- created: "created";
70
70
  disposed: "disposed";
71
71
  errored: "errored";
72
72
  }>;
@@ -75,18 +75,18 @@ export declare const TraceNodeSchema: z.ZodObject<{
75
75
  id: z.ZodString;
76
76
  parentTraceNodeId: z.ZodOptional<z.ZodString>;
77
77
  type: z.ZodEnum<{
78
- session: "session";
79
- route: "route";
80
- page: "page";
81
78
  effect: "effect";
82
79
  timer: "timer";
83
80
  "hmr-update": "hmr-update";
81
+ route: "route";
84
82
  render: "render";
85
83
  promise: "promise";
84
+ session: "session";
86
85
  "browser-context": "browser-context";
87
86
  tab: "tab";
88
87
  renderer: "renderer";
89
88
  router: "router";
89
+ page: "page";
90
90
  component: "component";
91
91
  element: "element";
92
92
  slot: "slot";
@@ -141,9 +141,9 @@ export declare const TraceNodeSchema: z.ZodObject<{
141
141
  instanceId: z.ZodOptional<z.ZodString>;
142
142
  generation: z.ZodOptional<z.ZodNumber>;
143
143
  lifecycle: z.ZodEnum<{
144
+ created: "created";
144
145
  active: "active";
145
146
  completed: "completed";
146
- created: "created";
147
147
  disposed: "disposed";
148
148
  errored: "errored";
149
149
  }>;
@@ -156,7 +156,6 @@ export declare const TraceNodeSchema: z.ZodObject<{
156
156
  url: z.ZodOptional<z.ZodString>;
157
157
  flowId: z.ZodOptional<z.ZodString>;
158
158
  originType: z.ZodOptional<z.ZodEnum<{
159
- request: "request";
160
159
  "user-click": "user-click";
161
160
  "user-input": "user-input";
162
161
  keyboard: "keyboard";
@@ -182,6 +181,7 @@ export declare const TraceNodeSchema: z.ZodObject<{
182
181
  "page-load": "page-load";
183
182
  "form-submit": "form-submit";
184
183
  "input-change": "input-change";
184
+ request: "request";
185
185
  job: "job";
186
186
  }>>;
187
187
  originNodeId: z.ZodOptional<z.ZodString>;
@@ -47,9 +47,9 @@ export type ProducerState = z.infer<typeof ProducerStateSchema>;
47
47
  /** Outcome of a producer's most recent run. */
48
48
  export declare const ProducerRunStatusSchema: z.ZodEnum<{
49
49
  error: "error";
50
+ running: "running";
50
51
  clean: "clean";
51
52
  failing: "failing";
52
- running: "running";
53
53
  }>;
54
54
  export type ProducerRunStatus = z.infer<typeof ProducerRunStatusSchema>;
55
55
  /**
@@ -66,9 +66,9 @@ export declare const ProducerReportSchema: z.ZodObject<{
66
66
  lastReportedAt: z.ZodNumber;
67
67
  lastRunStatus: z.ZodEnum<{
68
68
  error: "error";
69
+ running: "running";
69
70
  clean: "clean";
70
71
  failing: "failing";
71
- running: "running";
72
72
  }>;
73
73
  lastRunDurationMs: z.ZodOptional<z.ZodNumber>;
74
74
  errorCount: z.ZodNumber;
@@ -93,9 +93,9 @@ export declare const ProducerHealthSchema: z.ZodObject<{
93
93
  lastReportedAt: z.ZodNumber;
94
94
  lastRunStatus: z.ZodEnum<{
95
95
  error: "error";
96
+ running: "running";
96
97
  clean: "clean";
97
98
  failing: "failing";
98
- running: "running";
99
99
  }>;
100
100
  lastRunDurationMs: z.ZodOptional<z.ZodNumber>;
101
101
  errorCount: z.ZodNumber;
package/lib/replay.d.ts CHANGED
@@ -129,6 +129,8 @@ export type DashReplayStoryItem = {
129
129
  valuesId?: string;
130
130
  /** A call out's captured request and response. */
131
131
  exchangeId?: string;
132
+ /** A call to one of the cluster's own services through the gateway (`DashFlowNode.hop`), not a call out. */
133
+ hop?: true;
132
134
  /** An AI call's prompt and completion — `ai_get_call { callId }`. */
133
135
  aiCallId?: string;
134
136
  /** The lane's node a system item tells (`DashFlowNode.id`) — what the dashboard's player lights. */
@@ -14,18 +14,18 @@ export declare const ResourceEnvelopeSchema: z.ZodObject<{
14
14
  export type ResourceEnvelope = z.infer<typeof ResourceEnvelopeSchema>;
15
15
  export declare const AgentStatusKindSchema: z.ZodEnum<{
16
16
  warning: "warning";
17
- starting: "starting";
18
17
  clean: "clean";
19
18
  failing: "failing";
19
+ starting: "starting";
20
20
  }>;
21
21
  export type AgentStatusKind = z.infer<typeof AgentStatusKindSchema>;
22
22
  export declare const AgentBlockingItemSchema: z.ZodObject<{
23
23
  source: z.ZodString;
24
24
  severity: z.ZodEnum<{
25
+ error: "error";
25
26
  debug: "debug";
26
27
  info: "info";
27
28
  warning: "warning";
28
- error: "error";
29
29
  fatal: "fatal";
30
30
  }>;
31
31
  title: z.ZodString;
@@ -50,36 +50,36 @@ export type AgentBlockingItem = z.infer<typeof AgentBlockingItemSchema>;
50
50
  export declare const AgentChecksSchema: z.ZodObject<{
51
51
  typecheck: z.ZodOptional<z.ZodEnum<{
52
52
  unknown: "unknown";
53
- failed: "failed";
54
53
  passed: "passed";
54
+ failed: "failed";
55
55
  }>>;
56
56
  lint: z.ZodOptional<z.ZodEnum<{
57
57
  unknown: "unknown";
58
- failed: "failed";
59
58
  passed: "passed";
59
+ failed: "failed";
60
60
  }>>;
61
61
  build: z.ZodOptional<z.ZodEnum<{
62
62
  unknown: "unknown";
63
- failed: "failed";
64
63
  passed: "passed";
64
+ failed: "failed";
65
65
  }>>;
66
66
  runtime: z.ZodOptional<z.ZodEnum<{
67
67
  warning: "warning";
68
68
  unknown: "unknown";
69
- failed: "failed";
70
69
  passed: "passed";
70
+ failed: "failed";
71
71
  }>>;
72
72
  visual: z.ZodOptional<z.ZodEnum<{
73
73
  warning: "warning";
74
74
  unknown: "unknown";
75
- failed: "failed";
76
75
  passed: "passed";
76
+ failed: "failed";
77
77
  }>>;
78
78
  memory: z.ZodOptional<z.ZodEnum<{
79
79
  warning: "warning";
80
80
  unknown: "unknown";
81
- failed: "failed";
82
81
  passed: "passed";
82
+ failed: "failed";
83
83
  }>>;
84
84
  }, z.core.$strip>;
85
85
  /**
@@ -114,19 +114,19 @@ export declare const AgentCurrentStatusSchema: z.ZodObject<{
114
114
  sessionId: z.ZodString;
115
115
  status: z.ZodEnum<{
116
116
  warning: "warning";
117
- starting: "starting";
118
117
  clean: "clean";
119
118
  failing: "failing";
119
+ starting: "starting";
120
120
  }>;
121
121
  activeTab: z.ZodOptional<z.ZodString>;
122
122
  tabsWithIssues: z.ZodOptional<z.ZodArray<z.ZodString>>;
123
123
  blocking: z.ZodArray<z.ZodObject<{
124
124
  source: z.ZodString;
125
125
  severity: z.ZodEnum<{
126
+ error: "error";
126
127
  debug: "debug";
127
128
  info: "info";
128
129
  warning: "warning";
129
- error: "error";
130
130
  fatal: "fatal";
131
131
  }>;
132
132
  title: z.ZodString;
@@ -150,10 +150,10 @@ export declare const AgentCurrentStatusSchema: z.ZodObject<{
150
150
  warnings: z.ZodDefault<z.ZodArray<z.ZodObject<{
151
151
  source: z.ZodString;
152
152
  severity: z.ZodEnum<{
153
+ error: "error";
153
154
  debug: "debug";
154
155
  info: "info";
155
156
  warning: "warning";
156
- error: "error";
157
157
  fatal: "fatal";
158
158
  }>;
159
159
  title: z.ZodString;
@@ -178,36 +178,36 @@ export declare const AgentCurrentStatusSchema: z.ZodObject<{
178
178
  checks: z.ZodOptional<z.ZodObject<{
179
179
  typecheck: z.ZodOptional<z.ZodEnum<{
180
180
  unknown: "unknown";
181
- failed: "failed";
182
181
  passed: "passed";
182
+ failed: "failed";
183
183
  }>>;
184
184
  lint: z.ZodOptional<z.ZodEnum<{
185
185
  unknown: "unknown";
186
- failed: "failed";
187
186
  passed: "passed";
187
+ failed: "failed";
188
188
  }>>;
189
189
  build: z.ZodOptional<z.ZodEnum<{
190
190
  unknown: "unknown";
191
- failed: "failed";
192
191
  passed: "passed";
192
+ failed: "failed";
193
193
  }>>;
194
194
  runtime: z.ZodOptional<z.ZodEnum<{
195
195
  warning: "warning";
196
196
  unknown: "unknown";
197
- failed: "failed";
198
197
  passed: "passed";
198
+ failed: "failed";
199
199
  }>>;
200
200
  visual: z.ZodOptional<z.ZodEnum<{
201
201
  warning: "warning";
202
202
  unknown: "unknown";
203
- failed: "failed";
204
203
  passed: "passed";
204
+ failed: "failed";
205
205
  }>>;
206
206
  memory: z.ZodOptional<z.ZodEnum<{
207
207
  warning: "warning";
208
208
  unknown: "unknown";
209
- failed: "failed";
210
209
  passed: "passed";
210
+ failed: "failed";
211
211
  }>>;
212
212
  }, z.core.$strip>>;
213
213
  server: z.ZodOptional<z.ZodObject<{
@@ -327,10 +327,10 @@ declare const CheckEntrySchema: z.ZodObject<{
327
327
  code: z.ZodOptional<z.ZodString>;
328
328
  rule: z.ZodOptional<z.ZodString>;
329
329
  severity: z.ZodOptional<z.ZodEnum<{
330
+ error: "error";
330
331
  debug: "debug";
331
332
  info: "info";
332
333
  warning: "warning";
333
- error: "error";
334
334
  fatal: "fatal";
335
335
  }>>;
336
336
  file: z.ZodOptional<z.ZodString>;
@@ -356,9 +356,9 @@ export declare const TypecheckCurrentSchema: z.ZodObject<{
356
356
  lastReportedAt: z.ZodNumber;
357
357
  lastRunStatus: z.ZodEnum<{
358
358
  error: "error";
359
+ running: "running";
359
360
  clean: "clean";
360
361
  failing: "failing";
361
- running: "running";
362
362
  }>;
363
363
  lastRunDurationMs: z.ZodOptional<z.ZodNumber>;
364
364
  errorCount: z.ZodNumber;
@@ -395,9 +395,9 @@ export declare const TypecheckErrorsSchema: z.ZodObject<{
395
395
  lastReportedAt: z.ZodNumber;
396
396
  lastRunStatus: z.ZodEnum<{
397
397
  error: "error";
398
+ running: "running";
398
399
  clean: "clean";
399
400
  failing: "failing";
400
- running: "running";
401
401
  }>;
402
402
  lastRunDurationMs: z.ZodOptional<z.ZodNumber>;
403
403
  errorCount: z.ZodNumber;
@@ -416,10 +416,10 @@ export declare const TypecheckErrorsSchema: z.ZodObject<{
416
416
  code: z.ZodOptional<z.ZodString>;
417
417
  rule: z.ZodOptional<z.ZodString>;
418
418
  severity: z.ZodOptional<z.ZodEnum<{
419
+ error: "error";
419
420
  debug: "debug";
420
421
  info: "info";
421
422
  warning: "warning";
422
- error: "error";
423
423
  fatal: "fatal";
424
424
  }>>;
425
425
  file: z.ZodOptional<z.ZodString>;
@@ -446,9 +446,9 @@ export declare const LintCurrentSchema: z.ZodObject<{
446
446
  lastReportedAt: z.ZodNumber;
447
447
  lastRunStatus: z.ZodEnum<{
448
448
  error: "error";
449
+ running: "running";
449
450
  clean: "clean";
450
451
  failing: "failing";
451
- running: "running";
452
452
  }>;
453
453
  lastRunDurationMs: z.ZodOptional<z.ZodNumber>;
454
454
  errorCount: z.ZodNumber;
@@ -487,9 +487,9 @@ export declare const BuildCurrentSchema: z.ZodObject<{
487
487
  lastReportedAt: z.ZodNumber;
488
488
  lastRunStatus: z.ZodEnum<{
489
489
  error: "error";
490
+ running: "running";
490
491
  clean: "clean";
491
492
  failing: "failing";
492
- running: "running";
493
493
  }>;
494
494
  lastRunDurationMs: z.ZodOptional<z.ZodNumber>;
495
495
  errorCount: z.ZodNumber;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/protocol-types",
3
- "version": "1.25.31",
3
+ "version": "1.25.33",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",