@lensmcp/core 1.25.31 → 1.25.32

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
@@ -15,6 +15,7 @@ export * from './lib/flow-shape.js';
15
15
  export * from './lib/flow-segments.js';
16
16
  export * from './lib/flow-categories.js';
17
17
  export * from './lib/headless-flow.js';
18
+ export * from './lib/service-hop.js';
18
19
  export * from './lib/flows-config.js';
19
20
  export * from './lib/patterns.js';
20
21
  export * from './lib/thresholds.js';
package/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";export*from"./lib/common.js";export*from"./lib/ulid.js";export*from"./lib/event-bus.js";export*from"./lib/event-redactor.js";export*from"./lib/resource-store.js";export*from"./lib/answer-budget.js";export*from"./lib/graph-store.js";export*from"./lib/fingerprint.js";export*from"./lib/reducer-registry.js";export*from"./lib/story.js";export*from"./lib/route-template.js";export*from"./lib/sql-shape.js";export*from"./lib/http-shape.js";export*from"./lib/flow-shape.js";export*from"./lib/flow-segments.js";export*from"./lib/flow-categories.js";export*from"./lib/headless-flow.js";export*from"./lib/flows-config.js";export*from"./lib/patterns.js";export*from"./lib/thresholds.js";export*from"./lib/event-loop.js";export*from"./lib/graph-from-events.js";
1
+ "use strict";export*from"./lib/common.js";export*from"./lib/ulid.js";export*from"./lib/event-bus.js";export*from"./lib/event-redactor.js";export*from"./lib/resource-store.js";export*from"./lib/answer-budget.js";export*from"./lib/graph-store.js";export*from"./lib/fingerprint.js";export*from"./lib/reducer-registry.js";export*from"./lib/story.js";export*from"./lib/route-template.js";export*from"./lib/sql-shape.js";export*from"./lib/http-shape.js";export*from"./lib/flow-shape.js";export*from"./lib/flow-segments.js";export*from"./lib/flow-categories.js";export*from"./lib/headless-flow.js";export*from"./lib/service-hop.js";export*from"./lib/flows-config.js";export*from"./lib/patterns.js";export*from"./lib/thresholds.js";export*from"./lib/event-loop.js";export*from"./lib/graph-from-events.js";
@@ -10,6 +10,8 @@ export declare function areaOf(event: {
10
10
  category: string;
11
11
  title: string;
12
12
  kind?: string | undefined;
13
+ /** A call out's host: one under `.localhost` is a hop to the cluster's own services (service-hop.ts). */
14
+ host?: string | undefined;
13
15
  }): FlowArea;
14
16
  /** One chart-ready node per flow event: area-typed, titled, with start/duration/payload. */
15
17
  export interface FlowShapeOptions {
@@ -70,9 +72,11 @@ export declare function trimFlowEvent(event: BaseEvent, opts?: FlowShapeOptions)
70
72
  captured?: string[] | undefined;
71
73
  error?: string | undefined;
72
74
  outcome?: "returned" | "threw" | undefined;
73
- source: "gateway" | "ai" | "db" | "redis" | "queue" | "external" | "visual" | "react" | "memory" | "nx" | "vite" | "typescript" | "eslint" | "rollup" | "client-runtime" | "chrome" | "valtio" | "nestjs" | "nextjs" | "perf" | "security" | "lens" | "custom";
74
- category: "ai" | "db" | "visual" | "memory" | "security" | "build" | "typecheck" | "lint" | "cluster" | "agent" | "state" | "network" | "test" | "trace" | "runtime" | "render" | "backend" | "performance" | "deps";
75
- severity: "debug" | "info" | "warning" | "error" | "fatal";
75
+ calleeRequestId?: string | undefined;
76
+ hop?: true | undefined;
77
+ source: "ai" | "gateway" | "perf" | "memory" | "db" | "redis" | "queue" | "external" | "nestjs" | "valtio" | "nx" | "vite" | "typescript" | "eslint" | "rollup" | "client-runtime" | "chrome" | "react" | "nextjs" | "visual" | "security" | "lens" | "custom";
78
+ category: "ai" | "state" | "network" | "memory" | "db" | "visual" | "security" | "build" | "lint" | "typecheck" | "test" | "runtime" | "render" | "trace" | "backend" | "performance" | "deps" | "cluster" | "agent";
79
+ severity: "error" | "debug" | "info" | "warning" | "fatal";
76
80
  title: string;
77
81
  kind: string | undefined;
78
82
  area: FlowArea;
@@ -142,9 +146,11 @@ export declare function buildFlowDetail(rawEvents: BaseEvent[], opts?: FlowShape
142
146
  captured?: string[] | undefined;
143
147
  error?: string | undefined;
144
148
  outcome?: "returned" | "threw" | undefined;
145
- source: "gateway" | "ai" | "db" | "redis" | "queue" | "external" | "visual" | "react" | "memory" | "nx" | "vite" | "typescript" | "eslint" | "rollup" | "client-runtime" | "chrome" | "valtio" | "nestjs" | "nextjs" | "perf" | "security" | "lens" | "custom";
146
- category: "ai" | "db" | "visual" | "memory" | "security" | "build" | "typecheck" | "lint" | "cluster" | "agent" | "state" | "network" | "test" | "trace" | "runtime" | "render" | "backend" | "performance" | "deps";
147
- severity: "debug" | "info" | "warning" | "error" | "fatal";
149
+ calleeRequestId?: string | undefined;
150
+ hop?: true | undefined;
151
+ source: "ai" | "gateway" | "perf" | "memory" | "db" | "redis" | "queue" | "external" | "nestjs" | "valtio" | "nx" | "vite" | "typescript" | "eslint" | "rollup" | "client-runtime" | "chrome" | "react" | "nextjs" | "visual" | "security" | "lens" | "custom";
152
+ category: "ai" | "state" | "network" | "memory" | "db" | "visual" | "security" | "build" | "lint" | "typecheck" | "test" | "runtime" | "render" | "trace" | "backend" | "performance" | "deps" | "cluster" | "agent";
153
+ severity: "error" | "debug" | "info" | "warning" | "fatal";
148
154
  title: string;
149
155
  kind: string | undefined;
150
156
  area: FlowArea;
package/lib/flow-shape.js CHANGED
@@ -1 +1 @@
1
- "use strict";var qe=Object.defineProperty;var C=(i,m)=>qe(i,"name",{value:m,configurable:!0});var we=Object.defineProperty,v=C((i,m)=>we(i,"name",{value:m,configurable:!0}),"$");import{redactDeep as Me,redactText as oe,redactUrlParams as Ie}from"./redact.js";import{templatisePath as pe}from"./route-template.js";import{SQL_ACCOUNT_KIND as be,sqlAccountingOf as xe}from"./flow-segments.js";import{boundedClone as je}from"@lensmcp/protocol-types";export function areaOf(i){const{source:m,category:o,title:q,kind:c}=i;return m==="ai"?"ai":m==="perf"&&c==="event-loop-stall"?"services":c?.startsWith("memory")||c==="db-query"?"data":m==="gateway"||c==="gateway-request"?"gateway":c==="queue-process"||c==="queue-active"||c==="queue-completed"||c==="queue-failed"?"services":m==="db"||m==="redis"||m==="queue"||c==="redis-op"||c?.startsWith("queue-")||m==="fs"||c==="fs-ops"?"data":m==="exec"||c==="exec"?"services":m==="external"||c==="egress"?"network":m==="nestjs"||m==="nest"?/(?:Repository|Repo|Dao|Store|EntityManager|DataSource|QueryRunner|OrmClient)\./.test(q)?"data":"services":m==="valtio"?"state":o==="network"?"network":"browser"}C(areaOf,"areaOf"),v(areaOf,"areaOf");const fe=240,Se=new Set(["systemPrompt","prompt","prompts","messages","completion","completions","input","output","draft","text","content","result","arguments","params"]),Be=new Set(["tenantId","caller","userId","accountId","email","subject"]),Ae=8;function me(i){const m=ee(i,0);return je(Me({redacted:!0,...m}),{rawByteLimit:16*1024}).value}C(me,"vt"),v(me,"aiProjection");function ee(i,m){if(typeof i=="string")return he(i);if(i===null||typeof i!="object")return i;if(m>=Ae)return;if(Array.isArray(i))return i.slice(0,32).map(q=>ee(q,m+1));const o={};for(const[q,c]of Object.entries(i))if(!Be.has(q)){if(Se.has(q)){typeof c=="string"?o[`${q}Chars`]=c.length:Array.isArray(c)?o[`${q}Count`]=c.length:c!=null&&(o[`${q}Present`]=!0);continue}o[q]=ee(c,m+1)}return o}C(ee,"rt"),v(ee,"projectAi");function he(i){return i.length<=fe?oe(i):`${oe(i.slice(0,fe))}\u2026`}C(he,"jt"),v(he,"clipText");export function httpFacts(i,m){const o=v(M=>typeof M=="string"&&M.length>0?M:void 0,"str"),q=typeof m.status=="number"?m.status:void 0,c=v(M=>Ie(M.split("?")[0]??M)||"/","pathOf"),D=v((M,y,x={})=>({...M?{method:M.toUpperCase()}:{},...y?{rawPath:y,route:pe(y)}:{},...q!==void 0?{status:q}:{},...x}),"facts");if(i==="gateway-request"){const M=o(m.url);return D(o(m.method),M?c(M):void 0,{...o(m.host)?{host:o(m.host)}:{},...o(m.caller)?{caller:o(m.caller)}:{}})}if(i==="egress"){const M=o(m.path),y=o(m.exchangeId);return D(o(m.method)??"GET",M?c(M):void 0,{...o(m.host)?{host:o(m.host)}:{},...y?{exchangeId:y}:{}})}if(i==="fetch-start"||i==="fetch-done"||i==="fetch-error"){const M=o(m.href)??o(m.url);let y,x;if(M)try{const p=new URL(M,"http://relative.invalid");y=c(p.pathname),p.hostname!=="relative.invalid"&&(x=p.hostname)}catch{}return D(o(m.method)??"GET",y,x?{host:x}:{})}if(i==="ai-call")return{...q!==void 0?{status:q}:{},...m.stream===!0?{streamed:!0}:{}};const g=m.request;if(g&&typeof g=="object"){const M=g,y=o(M.route),x=o(M.method);if(!y&&!x)return{};const p=typeof M.status=="number"?M.status:void 0,R=y?c(y):void 0;return{...x?{method:x.toUpperCase()}:{},...R?{rawPath:R,route:R.includes(":")?R:pe(R)}:{},...p!==void 0?{status:p}:{}}}return{}}C(httpFacts,"httpFacts"),v(httpFacts,"httpFacts");export function trimFlowEvent(i,m={}){const o=i.raw??{},q=o.render??{},c=o.span??{},D=o.request??{},g=v(d=>typeof d=="number"?d:void 0,"num"),M=o.loop??{},y=o.effect??{},x=g(o.durationMs)??g(c.durationMs)??g(D.durationMs)??g(M.durationMs)??g(q.actualDurationMs)??g(y.durationMs),p=typeof o.kind=="string"?o.kind:void 0,R=p==="fetch-done"||p==="fetch-error",_=g(q.commitTime),z=p==="render"&&_!==void 0&&Math.abs(_-i.timestamp)<6e4,te=R?i.timestamp:z?_:g(o.startedAt)??g(c.startTime)??g(D.startedAt)??g(M.startedAt)??(x!==void 0?i.timestamp-x:i.timestamp);let j;if(typeof o.query=="string"){const d=Array.isArray(o.params)?` [${o.params.join(", ")}]`:"",f=typeof o.result=="string"?` \u2192 ${o.result}`:"";j=o.query+d+f}else if(typeof o.command=="string"){const d=typeof o.value=="string"?` \u2192 ${o.value}`:o.hit===!1?" \u2192 (miss)":"";j=o.command+d}else if(p==="state-update"){const d=o.update??{};let f="";try{f=JSON.stringify(d.preview)}catch{}j=`${d.storeId??""}.${d.path??""} = ${f}`}else if(p==="fs-ops"&&Array.isArray(o.paths))j=o.paths.slice(0,8).map(d=>{const f=d??{},B=typeof f.n=="number"&&f.n>1?` \xD7${f.n}`:"",T=typeof f.bytes=="number"&&f.bytes>0?` (${f.bytes>=1024?`${(f.bytes/1024).toFixed(1)} KB`:`${f.bytes} B`})`:"";return`${String(f.op??"?")} ${String(f.path??"?")}${B}${T}`}).join(" \xB7 ");else if(p==="exec"&&typeof o.cmd=="string")j=`${o.cmd} \u2192 ${o.code??o.signal??o.error??"?"}`;else if(p==="queue-completed"&&typeof o.returned=="string")j=`returned ${o.returned}`;else if(p==="queue-enqueued")j=`${o.job??"job"}#${o.jobId??"?"} \u2192 ${o.queue??""}${typeof o.data=="string"?" "+o.data:""}`;else if(p==="log"){const d=D.error??{},f=typeof d.stack=="string"?d.stack:typeof o.text=="string"?o.text:void 0;f&&f!==i.title&&(j=f)}let P,G,H,Q;if(p==="span"){const d=c.outcome;if(P=d==="threw"||d==="returned"?d:c.status==="error"?"threw":c.status==="ok"?"returned":void 0,P==="threw"){const T=c.error??{},L=typeof T.name=="string"?T.name:void 0,K=typeof T.message=="string"?T.message:typeof i.message=="string"?i.message:void 0;H=L,G=oe([L,K].filter(Boolean).join(": "))||void 0}const f=typeof c.args=="string"?c.args:void 0,B=typeof c.result=="string"?c.result:void 0;if(f!==void 0||B!==void 0){Q=[...f!==void 0?["args"]:[],...B!==void 0?["result"]:[]];const T=v(L=>L.length>ge?`${L.slice(0,ge)}\u2026`:L,"short");j=[f!==void 0?`args ${T(f)}`:"",B!==void 0?`returned ${T(B)}`:""].filter(Boolean).join(" \u2192 ")}}let U;if(p==="gateway-request")U=`${o.host??""}${String(o.url??"").split("?")[0]}`;else if(p==="fetch-start"||p==="fetch-done"||p==="fetch-error")try{const d=new URL(String(o.href??o.url??""));U=`${d.hostname}${d.pathname}`}catch{}let W;if(p==="gateway-request"&&Array.isArray(o.steps))W=o.steps.map(d=>{const f=[String(d.step)];return d.project&&f.push(String(d.project)),d.prefix&&f.push(String(d.prefix)),d.mode&&f.push(String(d.mode)+(d.ok===!1?" \u2717":d.ok===!0?" \u2713":"")),d.caller&&f.push("by "+String(d.caller)),d.pod&&f.push(String(d.pod)),typeof d.atMs=="number"&&f.push(`@${d.atMs}ms`),f.join(" ")}),j=W.map(d=>d.replace(/ @\d+ms$/,"")).join(" \u2192 ");else if(p==="egress")j=`${o.method??"GET"} ${o.host??""} \u2192 ${o.status??"?"}`;else if(i.source==="ai"){if(p==="ai-call"){const d=o.usage??{},f=d.promptTokens!==void 0||d.completionTokens!==void 0?` \xB7 ${g(d.promptTokens)??"?"}\u2192${g(d.completionTokens)??"?"} tok`:"",B=o.status!==void 0?` \xB7 ${String(o.status)}`:o.error?" \xB7 failed":"",T=g(o.toolCallCount)?` \xB7 ${g(o.toolCallCount)} tool call${g(o.toolCallCount)===1?"":"s"}`:"",L=g(o.durationMs)!==void 0?` \xB7 ${(g(o.durationMs)/1e3).toFixed(1)}s`:"";j=`${o.provider??""}/${o.model??"?"}${f}${B}${T}${L}${o.stream===!0?" \xB7 stream":""}`}else if(p==="ai-round"){const d=o.model??{},f=o.usage??{},B=Array.isArray(o.toolCalls)?o.toolCalls:[],T=`${g(f.promptTokens)??"?"}\u2192${g(f.completionTokens)??"?"} tok`,L=B.length?` \xB7 ${B.length} tool call${B.length===1?"":"s"}`:"",K=g(o.durationMs)!==void 0?` \xB7 ${(g(o.durationMs)/1e3).toFixed(1)}s`:"";j=`${d.provider??""}/${d.model??""} \xB7 ${T}${L}${K}`}else if(p==="ai-turn-start"){const d=Array.isArray(o.tools)?o.tools:[];j=`skill ${o.skill??""} \xB7 ${o.output??""}${d.length?" \xB7 tools: "+d.join(", "):""}`}else if(p==="ai-tool-call"){const d=o.call??{},f=o.result??{},B=g(o.durationMs)!==void 0?` \xB7 ${(g(o.durationMs)/1e3).toFixed(1)}s`:"";j=`${d.name??"tool"} ${f.error!=null?"\u2717 "+String(f.error):"\u2713"}${B}`}else if(p==="ai-turn-end"){const d=o.usage??{},f=g(o.rounds)!==void 0?` \xB7 ${g(o.rounds)} round${g(o.rounds)===1?"":"s"}`:"",B=g(o.durationMs)!==void 0?` \xB7 ${(g(o.durationMs)/1e3).toFixed(1)}s`:"";j=`${o.ok===!1?"FAILED":"ok"} \xB7 ${g(d.promptTokens)??"?"}+${g(d.completionTokens)??"?"} tok${f}${B}`}}let F;p==="db-query"&&typeof o.query=="string"?F=o.query:p==="redis-op"?F=`${o.op??""} ${String(o.key??"").replace(/\d+/g,"#")}`:p==="egress"?F=`${o.method??"GET"} ${o.host??""}`:p==="ai-call"?F=`ai ${o.provider??""} ${o.model??""} ${o.api??""}`:p==="queue-enqueued"?F=`enqueue ${o.queue??""}`:p==="span"&&typeof c.name=="string"?F=c.name:p==="memory-mutation"&&(F=i.title);const N=i.context??{},w=q.commitId??y.commitId??o.commitId,V=Array.isArray(q.why)?q.why.map(d=>typeof d?.type=="string"?d.type:void 0).filter(d=>!!d):[];return{id:i.id,t:i.timestamp,start:te,...F?{sig:F}:{},...U?{urlKey:U}:{},...httpFacts(p,o),...(p==="db-query"||p==="redis-op")&&typeof o.valuesId=="string"?{valuesId:o.valuesId}:{},...typeof o.queue=="string"?{queue:o.queue}:{},...o.jobId!=null?{jobId:String(o.jobId)}:{},source:i.source,category:i.category,severity:i.severity,title:P==="threw"&&typeof c.name=="string"?`${c.name} \u2014 threw${H?` ${H}`:""}${x!==void 0?` after ${x}ms`:""}`:i.title,kind:p,area:areaOf({source:i.source,category:i.category,title:i.title,kind:p}),...P?{outcome:P}:{},...G?{error:G}:{},...Q?{captured:Q}:{},...typeof c.role=="string"?{role:c.role}:{},...typeof c.spanId=="string"?{spanId:c.spanId}:{},...typeof c.parentSpanId=="string"?{parentSpanId:c.parentSpanId}:{},...p==="log"&&typeof o.level=="string"?{level:o.level}:{},...p==="log"&&typeof o.logger=="string"?{logger:o.logger}:{},...p==="log"&&typeof o.stream=="string"?{stream:o.stream}:{},...typeof N.requestId=="string"?{requestId:N.requestId}:{},...typeof N.parentRequestId=="string"?{parentRequestId:N.parentRequestId}:{},...typeof w=="string"?{commitId:w}:{},...typeof q.componentName=="string"?{component:q.componentName}:{},...typeof q.phase=="string"?{phase:q.phase}:{},...V.length?{why:V}:{},...p==="effect-run"&&typeof y.hookLogicalId=="string"?{hook:y.hookLogicalId}:{},...p==="effect-run"&&y.cleanupRan===!0?{cleanup:!0}:{},...p==="render-commit-truncated"?{renderTotal:g(o.total),renderPublished:g(o.published),...g(o.walkStoppedAt)!==void 0?{renderWalkStoppedAt:g(o.walkStoppedAt)}:{}}:{},...typeof N.projectName=="string"?{project:N.projectName}:typeof o.project=="string"?{project:o.project}:{},...x!==void 0?{durationMs:x}:{},...R&&g(o.startedAt)!==void 0?{fetchStartedAt:g(o.startedAt)}:{},...i.source==="ai"?{ai:m.includeRaw?o:me(o)}:{},...j?{detail:j}:{},...W?{steps:W}:{}}}C(trimFlowEvent,"trimFlowEvent"),v(trimFlowEvent,"trimFlowEvent");const ge=300;export const OUTLIVED_PARENT_MIN_MS=250;const Te=new Set(["egress","db-query","redis-op","ai-call","span","gateway-request","server-request"]),$e=1;function ye(i){return i==="flow-origin"?0:i?.endsWith("-start")?1:i==="gateway-request"?2:i==="server-request"?3:i==="span"?4:i==="render"||i==="render-commit-truncated"?6:i==="effect-run"?7:5}C(ye,"Rt"),v(ye,"kindRank");function J(i){return i.kind==="span"&&i.role==="controller"?3.5:i.kind==="queue-process"?3:ye(i.kind)}C(J,"Z"),v(J,"causalRank");export function buildFlowDetail(i,m={}){const o=xe(i),q=i.slice().filter(e=>{const r=e.raw??{},t=typeof r.kind=="string"?r.kind:"";if(t==="subscription-added"||t==="subscription-removed"||t==="hook-run"||t==="store-created"||t==="singleton-instance"||t===be)return!1;const n=r.span;return!(typeof n?.name=="string"&&n.name.startsWith("nest.request "))}).map(e=>trimFlowEvent(e,m)),c=new Set;for(let e=q.length-1;e>=0;e--)c.has(q[e].id)?q.splice(e,1):c.add(q[e].id);const D=new Map;for(const e of q)(e.kind==="gateway-request"||e.kind==="queue-process")&&e.requestId&&!D.has(e.requestId)&&D.set(e.requestId,e.start);const g=v(e=>e.source!=="react"&&e.source!=="valtio"&&e.requestId&&D.has(e.requestId)?D.get(e.requestId):e.start,"anchorOf");q.sort((e,r)=>g(e)-g(r)||e.start-r.start||J(e)-J(r)||(r.durationMs??0)-(e.durationMs??0)||e.t-r.t);const M=v(e=>(e.kind==="span"||e.kind==="server-request"||e.kind==="gateway-request"||e.kind==="queue-process")&&(e.durationMs??0)>0,"spanLike"),y=q;for(const e of y)e.group=e.source==="react"||e.source==="valtio"||e.source==="client-runtime"||e.source==="visual"?"browser":"server";const x=new Map;for(const e of y)if((e.kind==="gateway-request"||e.kind==="queue-process")&&e.requestId&&e.project){const r=x.get(e.requestId)??[];r.push({project:e.project,start:e.start,end:e.start+(e.durationMs??0)}),x.set(e.requestId,r)}const p=v((e,r)=>{const t=x.get(e);if(t?.length)return t.every(n=>n.project===t[0].project)?t[0].project:t.filter(n=>n.start<=r+1&&n.end>=r-1).sort((n,s)=>s.start-n.start)[0]?.project},"projectOf");for(const e of y)if(!e.project&&e.source==="nestjs"&&e.requestId){const r=p(e.requestId,e.start);r&&(e.project=r)}const R=new Map;for(const e of y)e.spanId&&R.set(e.spanId,e);const _=v((e,r)=>{let t=e.parentSpanId?R.get(e.parentSpanId):void 0;for(let n=0;t&&n<64;n++){if(t===r)return!0;t=t.parentSpanId?R.get(t.parentSpanId):void 0}return!1},"inside");for(const e of y){const r=e.parentSpanId?R.get(e.parentSpanId):void 0;if(r&&r!==e&&r.group===e.group&&r.requestId===e.requestId){e.parent=r.id;continue}let t;const n=e.requestId;for(const s of y){if(s===e||!M(s)||s.group!==e.group||s.requestId!==n)continue;const a=s.project,$=e.project;if(s.kind!=="gateway-request"&&e.kind!=="gateway-request"&&a!==void 0&&$!==void 0&&a!==$)continue;const k=s.durationMs??0,u=e.durationMs??0,l=Math.abs(s.start-e.start)<=$e,h=J(s),I=J(e),A=s.start<=e.start;if(!(s.start+k>e.start&&(A||l&&h<I)&&(!l||(h!==I?h<I:A&&k>u))))continue;const b=t?J(t):-1,O=t?Math.abs(s.start-t.start)<=$e:!1,E=t&&O&&h===b?_(s,t)?1:_(t,s)?-1:0:0;(!t||!O&&s.start>t.start||O&&h>b||E>0||E===0&&O&&h===b&&(s.durationMs??0)<(t.durationMs??0))&&(t=s)}t&&(e.parent=t.id)}for(const e of y)delete e.spanId,delete e.parentSpanId;{const e=new Set(y.map(a=>a.id)),r=new Set,t=new Map,n=[],s=v(a=>{const $=[a];for(;$.length;){const k=$.pop();n.push(k),r.add(k.id);const u=t.get(k.id);if(u){t.delete(k.id);for(let l=u.length-1;l>=0;l--)$.push(u[l])}}},"place");for(const a of y)if(a.parent&&a.parent!==a.id&&e.has(a.parent)&&!r.has(a.parent)){const $=t.get(a.parent)??[];$.push(a),t.set(a.parent,$)}else s(a);for(const a of y)r.has(a.id)||(n.push(a),r.add(a.id));for(let a=0;a<n.length;a++)y[a]=n[a]}const z=new Map;for(const e of y){if(!e.requestId)continue;const r=z.get(e.requestId);r?r.push(e):z.set(e.requestId,[e])}const te=new Map(y.map(e=>[e.id,e])),j=v((e,r)=>{const t=(z.get(e)??[]).filter(n=>M(n)&&n.start<=r&&n.start+(n.durationMs??0)>=r);if(t.length!==0)return t.sort((n,s)=>s.start-n.start||(n.durationMs??0)-(s.durationMs??0)),t[0]},"callerNodeFor");for(const e of y){if(!e.parentRequestId||e.kind!=="server-request"&&e.kind!=="gateway-request")continue;const r=e.parent?te.get(e.parent):void 0;if(r&&r.requestId===e.requestId)continue;const t=j(e.parentRequestId,e.start);t&&t!==e&&t.start<=e.start&&(e.parent=t.id)}const P=y,G=new Map;for(const e of P)e.kind==="gateway-request"&&e.requestId&&!G.has(e.requestId)&&G.set(e.requestId,e);for(const e of P)if(e.group==="server"&&!e.parent&&e.requestId&&e.kind!=="gateway-request"){const r=G.get(e.requestId);r&&r!==e&&r.start<=e.start&&(e.parent=r.id)}const H=new Set,Q=new Set,U=[];for(const e of P){if(e.kind!=="gateway-request")continue;if(!e.requestId){U.push(e);continue}const r=z.get(e.requestId)??[];if(r.filter(s=>s.kind==="gateway-request").sort((s,a)=>s.start-a.start)[0]!==e)continue;const t=r.find(s=>s.kind==="fetch-start"),n=r.find(s=>s.kind==="fetch-done"||s.kind==="fetch-error");if(!t&&!n){U.push(e);continue}t&&(e.fetchStartId=t.id,H.add(t.id)),n&&(e.fetchDoneId=n.id,Q.add(n.id))}const W=new Map;for(const e of P){if(!e.urlKey)continue;const r=W.get(e.urlKey)??{gws:[],starts:[],dones:[]};e.kind==="gateway-request"?U.includes(e)&&r.gws.push(e):e.kind==="fetch-start"?H.has(e.id)||r.starts.push(e):(e.kind==="fetch-done"||e.kind==="fetch-error")&&(Q.has(e.id)||r.dones.push(e)),W.set(e.urlKey,r)}for(const{gws:e,starts:r,dones:t}of W.values()){e.sort((s,a)=>s.start-a.start),r.sort((s,a)=>s.start-a.start);const n=new Set;e.forEach((s,a)=>{const $=r[a];$&&(s.fetchStartId=$.id);const k=$?$.t:s.start;let u,l=1/0;for(const h of t){if(n.has(h.id))continue;const I=h.fetchStartedAt??h.start-(h.durationMs??0),A=Math.abs(I-k);A<l&&(l=A,u=h)}u&&(s.fetchDoneId=u.id,n.add(u.id))})}const F=new Map,N=new Map;for(const e of P)!e.queue||!e.jobId||(e.kind==="queue-enqueued"&&F.set(`${e.queue}#${e.jobId}`,e),e.kind==="queue-process"&&N.set(`${e.queue}#${e.jobId}`,e));for(const e of P){if(!e.queue||!e.jobId)continue;const r=`${e.queue}#${e.jobId}`;if(e.kind==="queue-process"&&!e.parent){const t=F.get(r);t&&t!==e&&t.start<=e.start&&(e.parent=t.id)}else if(e.kind==="queue-active"||e.kind==="queue-completed"||e.kind==="queue-failed"){const t=N.get(r);t&&t!==e&&t.start<=e.start&&(e.parent=t.id)}}{const e=new Map(y.map(r=>[r.id,r]));for(const r of y){if(!r.parent||!Te.has(r.kind??"")||r.durationMs===void 0)continue;const t=e.get(r.parent);if(!t||t.kind!=="span"||!((t.durationMs??0)>0))continue;const n=Math.round(r.start+r.durationMs-(t.start+t.durationMs));if(n<OUTLIVED_PARENT_MIN_MS)continue;r.outlivedParentMs=n;const s=t.leftRunning??{count:0,longestMs:0};t.leftRunning={count:s.count+1,longestMs:Math.max(s.longestMs,n)}}for(const r of y){const t=[];r.leftRunning&&t.push(`ended before ${r.leftRunning.count===1?"a call":`${r.leftRunning.count} calls`} it started had finished \u2014 ${r.leftRunning.count===1?"it":"the longest"} ran on ${r.leftRunning.longestMs}ms (a timeout that gave up, or work not awaited)`),r.outlivedParentMs!==void 0&&t.push(`ran on ${r.outlivedParentMs}ms after the span that started it had ended`),t.length>0&&(r.detail=[r.detail,...t].filter(Boolean).join(" \xB7 "))}}let w=y;{const e=new Map;for(const t of w)t.kind==="fetch-start"&&e.set(t.id,t);const r=new Map;for(const t of w){if(t.kind!=="fetch-done"&&t.kind!=="fetch-error")continue;const n=[...e.values()].find($=>$.requestId&&$.requestId===t.requestId)?.id,s=n?e.get(n):void 0;if(!s)continue;const a=t.fetchStartedAt??s.start;s.title=t.title,s.severity=t.severity,s.durationMs=t.durationMs??Math.max(0,t.t-a),s.kind="fetch",t.detail&&!s.detail&&(s.detail=t.detail),r.set(t.id,s.id)}if(r.size>0){w=w.filter(t=>!r.has(t.id));for(const t of w)for(t.fetchDoneId&&r.has(t.fetchDoneId)&&(t.fetchDoneId=r.get(t.fetchDoneId));t.parent&&r.has(t.parent);)t.parent=r.get(t.parent)}}const V=v(e=>!!e.commitId&&(e.kind==="render"||e.kind==="effect-run"||e.kind==="render-commit-truncated"),"commitMember"),d=v(e=>[...new Set(e)],"uniq"),f=v(e=>d(e.map(r=>r.component).filter(r=>!!r)),"named"),B=v(e=>e.length<=2?e.join(" + "):`${e[0]} + ${e.length-1} more`,"nameList"),T=v(e=>{const r=new Map;for(const t of e)t.component&&r.set(t.component,(r.get(t.component)??0)+1);return[...r].map(([t,n])=>n>1?`${t} \xD7${n}`:t).join(", ")},"tally"),L=v(e=>{if(!e)return"effect";const r=/([^/\\]+?):(\d+)(?::\d+)?$/.exec(e);return r?`${r[1]}:${r[2]}`:e},"shortHook"),K=v(e=>`${L(e.hook)}${e.cleanup?" (cleanup)":""}`,"hookLabel"),ne=v(e=>{const r=Math.min(...e.map(n=>n.start)),t=Math.max(...e.map(n=>n.start+(n.durationMs??0)));return{start:r,durationMs:t-r}},"extent"),se=v(e=>(delete e.component,delete e.why,delete e.phase,delete e.hook,delete e.cleanup,delete e.renderTotal,delete e.renderPublished,e),"asGroup");function ae(e){const r=e[0],t=e.filter(S=>S.kind==="render"),n=e.filter(S=>S.kind==="effect-run"),s=e.find(S=>S.kind==="render-commit-truncated"),a=[...t].sort((S,Y)=>(Y.durationMs??0)-(S.durationMs??0)),$=a.filter(S=>(S.why??[]).some(Y=>Y!=="parent-render")),k=d($.flatMap(S=>(S.why??[]).filter(Y=>Y!=="parent-render"))),u=f($),l=t.filter(S=>S.phase==="mount"),h=t.filter(S=>S.phase!=="mount"&&!$.includes(S)),I=a[0]?.component??"view",A=t.length>0&&l.length===t.length,b=k.length?k.join(" + "):void 0,O=A?"mount":"update",E=u.length?`view update \xB7 ${B(u)} \u2190 ${b}`:A?`view mount \xB7 ${I}`:`view update \xB7 ${I}`,re=s?.renderTotal??t.length,ke=[l.length?`mounted ${T(l)}`:"",h.length?`re-rendered ${T(h)}`:"",!u.length&&!A&&t.length?"trigger not recorded \u2014 every captured render came from its parent":"",s?.renderWalkStoppedAt!==void 0?`at least ${re} renders (the walk stopped at ${s.renderWalkStoppedAt} fibers), ${s.renderPublished??t.length} recorded`:s?`${re} renders, ${s.renderPublished??t.length} recorded (per-commit cap)`:`${t.length} render${t.length===1?"":"s"}`,n.length?`${n.length} effect${n.length===1?"":"s"}: ${n.map(K).join(", ")}`:""].filter(Boolean).join(" \xB7 "),ce=f(t);return se({...r,...ne(e),kind:"view-update",title:E,detail:ke,commitId:r.commitId,viewPhase:O,...b?{cause:b}:{},causeComponents:u,components:ce,mounted:f(l),renderCount:re,...s?.renderWalkStoppedAt!==void 0?{renderCountAtLeast:!0}:{},recordedRenders:t.length,effects:n.length,effectHooks:n.map(K),memberIds:e.map(S=>S.id),sig:`commit:${O}:${b??"-"}:${u.join(",")}:${ce.slice().sort().join(",")}`,severity:e.some(S=>S.severity==="error")?"error":r.severity})}C(ae,"pt"),v(ae,"viewUpdateNode");function ie(e){const r=e[0],t=e.map(K);return se({...r,...ne(e),kind:"commit-effects",title:e.length===1?`effect \xB7 ${t[0]}`:`effects \xB7 ${t[0]} + ${e.length-1} more`,detail:t.join(", "),commitId:r.commitId,effects:e.length,effectHooks:t,memberIds:e.map(n=>n.id),sig:`effects:${t.slice().sort().join(",")}`})}C(ie,"mt"),v(ie,"effectsNode");const ve=v(e=>d(e.map(r=>r==="state-update"?"state update":r==="fetch-start"?"request":r==="flow-origin"?"interaction":"response")).join(" + "),"describeInputs");function de(e,r){const t=e[0],n=e[e.length-1],s=e.length,a=t.detail,$=Math.min(...e.map(h=>h.start)),k=Math.max(...e.map(h=>h.start+(h.durationMs??0))),u=Math.round(e.reduce((h,I)=>h+(I.durationMs??0),0)/s*10)/10,l=Math.round((k-$)*10)/10;t.count=s,t.avgMs=u,t.wallMs=l,t.durationMs=k-$,t.items=e.slice(0,12).map(h=>({start:h.start-$,...h.durationMs!=null?{durationMs:h.durationMs}:{}})),t.memberIds=e.flatMap(h=>h.memberIds??[h.id]),t.title=`\xD7${s} ${t.title}`,e.some(h=>h.severity==="error")&&(t.severity="error"),t.kind==="view-update"?t.detail=[`${s} commits, ${r.length?`one per ${ve(r)}`:"back to back"}`,`avg ${u}ms \xB7 wall ${l}ms`,a??""].filter(Boolean).join(" \xB7 "):t.kind==="state-update"&&a&&n.detail?t.detail=`${s} updates in a row \xB7 wall ${l}ms \xB7 first ${a} \xB7 last ${n.detail}`:t.detail=[`${s} in a row \xB7 wall ${l}ms`,a??""].filter(Boolean).join(" \xB7 ")}C(de,"ht"),v(de,"collapseRepeat");const Z=[],X=new Map,ue=new Set;for(let e=0;e<w.length;e++){if(ue.has(e))continue;const r=w[e];if(!V(r)){Z.push(r);continue}const t=[e];for(let a=e+1;a<w.length;a++){const $=w[a];if($.group==="browser"){if(!V($)||$.commitId!==r.commitId)break;t.push(a)}}for(const a of t)ue.add(a);const n=t.map(a=>w[a]),s=n.some(a=>a.kind!=="effect-run")?ae(n):ie(n);for(const a of n)a.id!==s.id&&X.set(a.id,s.id);Z.push(s)}if(X.size>0||Z.some(e=>e.kind==="view-update"||e.kind==="commit-effects")){w=Z;for(const e of w)for(;e.parent&&X.has(e.parent);)e.parent=X.get(e.parent)}{const e=new Map;for(const r of w)r.kind==="view-update"&&r.commitId&&!e.has(r.commitId)&&e.set(r.commitId,r);for(const r of w){if(r.kind!=="commit-effects"||r.parent||!r.commitId)continue;const t=e.get(r.commitId);t&&t.start<=r.start&&(r.parent=t.id)}}{const e=new Set(["state-update","fetch","fetch-start","fetch-done","fetch-error","flow-origin"]),r=w.map((u,l)=>u.group==="browser"?l:-1).filter(u=>u>=0),t=v(u=>`${u.kind}|${u.sig??u.title}`,"keyOf"),n=r.map(u=>t(w[u])),s=v((u,l,h)=>{for(let I=0;I<h;I++)if(n[u+I]!==n[l+I])return!1;return!0},"sameBlock"),a=v(u=>n[u].split("|")[0],"kindAt"),$=v(u=>{let l=0,h=1;for(let I=1;I<=4&&u+I<=n.length;I++){let A=1;for(;u+(A+1)*I<=n.length&&s(u,u+A*I,I);)A+=1;A>=3&&I*A>l*h&&(l=I,h=A)}return{k:l,r:h}},"bestBlockAt"),k=new Map;for(let u=0;u<n.length;){const{k:l,r:h}=$(u);if(l===0){u+=1;continue}const I=[...Array(l).keys()].find(b=>e.has(a(u+b)));if(I!==void 0&&I>0){const b=$(u+I);if(b.k===l&&b.r>=3&&b.r>=h-1){u+=I;continue}}const A=n.slice(u,u+l).map(b=>b.split("|")[0]).filter(b=>e.has(b));for(let b=0;b<l;b++){const O=[];for(let E=0;E<h;E++)O.push(w[r[u+b+E*l]]);de(O,A);for(const E of O.slice(1))k.set(E.id,O[0].id)}u+=l*h}if(k.size>0){w=w.filter(u=>!k.has(u.id));for(const u of w)for(;u.parent&&k.has(u.parent);)u.parent=k.get(u.parent)}}{const e=new Map(w.map(r=>[r.id,r]));for(const r of w){const t=new Set([r.id]);let n=r;for(;n?.parent;){if(t.has(n.parent)){n.parent=void 0;break}t.add(n.parent),n=e.get(n.parent)}}}const le=v(e=>{const r=new Map;for(const n of w){if(!n.sig||M(n)!==e||n.kind==="server-request"||n.kind==="gateway-request"||n.group==="browser")continue;const s=`${n.parent??"root"}|${n.kind}|${n.sig}`,a=r.get(s);a?a.push(n):r.set(s,[n])}const t=new Map;for(const n of r.values()){if(n.length<3)continue;n.sort((l,h)=>l.start-h.start);const s=n[0];let a=!1;for(let l=0;l+1<n.length;l++)if(n[l+1].start<n[l].start+(n[l].durationMs??0)){a=!0;break}const $=s.start,k=Math.max(...n.map(l=>l.start+(l.durationMs??0))),u=n.reduce((l,h)=>l+(h.durationMs??0),0);s.count=n.length,s.parallel=a,s.avgMs=Math.round(u/n.length*10)/10,s.wallMs=k-$,s.durationMs=k-$,s.title=`\xD7${n.length} ${s.title.replace(/\s*\((?:\d+ rows?, )?\d+ms\)$/,"")}`,s.detail=`${s.sig} \xD7${n.length} ${a?"\u2016 parallel":"\u21BB sequential loop"} \xB7 avg ${s.avgMs}ms \xB7 wall ${s.wallMs}ms`,s.items=n.slice(0,12).map(l=>({start:l.start-$,...l.durationMs!=null?{durationMs:l.durationMs}:{}})),s.memberIds=n.flatMap(l=>l.memberIds??[l.id]),n.some(l=>l.severity==="error")&&(s.severity="error");for(const l of n.slice(1))t.set(l.id,s.id)}if(t.size!==0){w=w.filter(n=>!t.has(n.id));for(const n of w)for(;n.parent&&t.has(n.parent);)n.parent=t.get(n.parent)}},"aggregatePass");le(!0),le(!1);for(const e of w){if(!M(e))continue;const r=e.durationMs??0;if(r<40)continue;const t=w.filter(k=>k.parent===e.id&&(k.durationMs??0)>0).map(k=>[k.start,k.start+(k.durationMs??0)]).sort((k,u)=>k[0]-u[0]);let n=0,s=-1,a=-1;for(const[k,u]of t)k>a?(a>s&&(n+=a-s),s=k,a=u):u>a&&(a=u);a>s&&(n+=a-s);const $=Math.max(0,r-n);($>=50||$/r>=.7&&r>=40)&&(e.selfMs=$,e.detail=`\u26A0 cpu-heavy: ${$}ms self-time of ${r}ms${e.detail?" \xB7 "+e.detail:""}`)}return{events:w,...o?{sql:o}:{}}}C(buildFlowDetail,"buildFlowDetail"),v(buildFlowDetail,"buildFlowDetail");
1
+ "use strict";var jt=Object.defineProperty;var R=(i,f)=>jt(i,"name",{value:f,configurable:!0});var St=Object.defineProperty,k=R((i,f)=>St(i,"name",{value:f,configurable:!0}),"I");import{redactDeep as Bt,redactText as nt,redactUrlParams as At}from"./redact.js";import{templatisePath as ht}from"./route-template.js";import{SQL_ACCOUNT_KIND as Rt,sqlAccountingOf as Tt}from"./flow-segments.js";import{isServiceHopHost as $t}from"./service-hop.js";import{boundedClone as Ct}from"@lensmcp/protocol-types";export function areaOf(i){const{source:f,category:s,title:M,kind:l}=i;return f==="ai"?"ai":f==="perf"&&l==="event-loop-stall"?"services":l?.startsWith("memory")||l==="db-query"?"data":f==="gateway"||l==="gateway-request"?"gateway":l==="queue-process"||l==="queue-active"||l==="queue-completed"||l==="queue-failed"?"services":f==="db"||f==="redis"||f==="queue"||l==="redis-op"||l?.startsWith("queue-")||f==="fs"||l==="fs-ops"?"data":f==="exec"||l==="exec"||l==="egress"&&$t(i.host)?"services":f==="external"||l==="egress"?"network":f==="nestjs"||f==="nest"?/(?:Repository|Repo|Dao|Store|EntityManager|DataSource|QueryRunner|OrmClient)\./.test(M)?"data":"services":f==="valtio"?"state":s==="network"?"network":"browser"}R(areaOf,"areaOf"),k(areaOf,"areaOf");const yt=240,Dt=new Set(["systemPrompt","prompt","prompts","messages","completion","completions","input","output","draft","text","content","result","arguments","params"]),Pt=new Set(["tenantId","caller","userId","accountId","email","subject"]),Nt=8;function vt(i){const f=rt(i,0);return Ct(Bt({redacted:!0,...f}),{rawByteLimit:16*1024}).value}R(vt,"Ct"),k(vt,"aiProjection");function rt(i,f){if(typeof i=="string")return kt(i);if(i===null||typeof i!="object")return i;if(f>=Nt)return;if(Array.isArray(i))return i.slice(0,32).map(M=>rt(M,f+1));const s={};for(const[M,l]of Object.entries(i))if(!Pt.has(M)){if(Dt.has(M)){typeof l=="string"?s[`${M}Chars`]=l.length:Array.isArray(l)?s[`${M}Count`]=l.length:l!=null&&(s[`${M}Present`]=!0);continue}s[M]=rt(l,f+1)}return s}R(rt,"it"),k(rt,"projectAi");function kt(i){return i.length<=yt?nt(i):`${nt(i.slice(0,yt))}\u2026`}R(kt,"Et"),k(kt,"clipText");export function httpFacts(i,f){const s=k(I=>typeof I=="string"&&I.length>0?I:void 0,"str"),M=typeof f.status=="number"?f.status:void 0,l=k(I=>At(I.split("?")[0]??I)||"/","pathOf"),P=k((I,y,j={})=>({...I?{method:I.toUpperCase()}:{},...y?{rawPath:y,route:ht(y)}:{},...M!==void 0?{status:M}:{},...j}),"facts");if(i==="gateway-request"){const I=s(f.url);return P(s(f.method),I?l(I):void 0,{...s(f.host)?{host:s(f.host)}:{},...s(f.caller)?{caller:s(f.caller)}:{}})}if(i==="egress"){const I=s(f.path),y=s(f.exchangeId);return P(s(f.method)??"GET",I?l(I):void 0,{...s(f.host)?{host:s(f.host)}:{},...y?{exchangeId:y}:{}})}if(i==="fetch-start"||i==="fetch-done"||i==="fetch-error"){const I=s(f.href)??s(f.url);let y,j;if(I)try{const p=new URL(I,"http://relative.invalid");y=l(p.pathname),p.hostname!=="relative.invalid"&&(j=p.hostname)}catch{}return P(s(f.method)??"GET",y,j?{host:j}:{})}if(i==="ai-call")return{...M!==void 0?{status:M}:{},...f.stream===!0?{streamed:!0}:{}};const g=f.request;if(g&&typeof g=="object"){const I=g,y=s(I.route),j=s(I.method);if(!y&&!j)return{};const p=typeof I.status=="number"?I.status:void 0,T=y?l(y):void 0;return{...j?{method:j.toUpperCase()}:{},...T?{rawPath:T,route:T.includes(":")?T:ht(T)}:{},...p!==void 0?{status:p}:{}}}return{}}R(httpFacts,"httpFacts"),k(httpFacts,"httpFacts");export function trimFlowEvent(i,f={}){const s=i.raw??{},M=s.render??{},l=s.span??{},P=s.request??{},g=k(d=>typeof d=="number"?d:void 0,"num"),I=s.loop??{},y=s.effect??{},j=g(s.durationMs)??g(l.durationMs)??g(P.durationMs)??g(I.durationMs)??g(M.actualDurationMs)??g(y.durationMs),p=typeof s.kind=="string"?s.kind:void 0,T=p==="fetch-done"||p==="fetch-error",G=g(M.commitTime),H=p==="render"&&G!==void 0&&Math.abs(G-i.timestamp)<6e4,V=T?i.timestamp:H?G:g(s.startedAt)??g(l.startTime)??g(P.startedAt)??g(I.startedAt)??(j!==void 0?i.timestamp-j:i.timestamp);let b;if(typeof s.query=="string"){const d=Array.isArray(s.params)?` [${s.params.join(", ")}]`:"",m=typeof s.result=="string"?` \u2192 ${s.result}`:"";b=s.query+d+m}else if(typeof s.command=="string"){const d=typeof s.value=="string"?` \u2192 ${s.value}`:s.hit===!1?" \u2192 (miss)":"";b=s.command+d}else if(p==="state-update"){const d=s.update??{};let m="";try{m=JSON.stringify(d.preview)}catch{}b=`${d.storeId??""}.${d.path??""} = ${m}`}else if(p==="fs-ops"&&Array.isArray(s.paths))b=s.paths.slice(0,8).map(d=>{const m=d??{},B=typeof m.n=="number"&&m.n>1?` \xD7${m.n}`:"",h=typeof m.bytes=="number"&&m.bytes>0?` (${m.bytes>=1024?`${(m.bytes/1024).toFixed(1)} KB`:`${m.bytes} B`})`:"";return`${String(m.op??"?")} ${String(m.path??"?")}${B}${h}`}).join(" \xB7 ");else if(p==="exec"&&typeof s.cmd=="string")b=`${s.cmd} \u2192 ${s.code??s.signal??s.error??"?"}`;else if(p==="queue-completed"&&typeof s.returned=="string")b=`returned ${s.returned}`;else if(p==="queue-enqueued")b=`${s.job??"job"}#${s.jobId??"?"} \u2192 ${s.queue??""}${typeof s.data=="string"?" "+s.data:""}`;else if(p==="log"){const d=P.error??{},m=typeof d.stack=="string"?d.stack:typeof s.text=="string"?s.text:void 0;m&&m!==i.title&&(b=m)}let L,W,Y,U;if(p==="span"){const d=l.outcome;if(L=d==="threw"||d==="returned"?d:l.status==="error"?"threw":l.status==="ok"?"returned":void 0,L==="threw"){const h=l.error??{},D=typeof h.name=="string"?h.name:void 0,K=typeof h.message=="string"?h.message:typeof i.message=="string"?i.message:void 0;Y=D,W=nt([D,K].filter(Boolean).join(": "))||void 0}const m=typeof l.args=="string"?l.args:void 0,B=typeof l.result=="string"?l.result:void 0;if(m!==void 0||B!==void 0){U=[...m!==void 0?["args"]:[],...B!==void 0?["result"]:[]];const h=k(D=>D.length>wt?`${D.slice(0,wt)}\u2026`:D,"short");b=[m!==void 0?`args ${h(m)}`:"",B!==void 0?`returned ${h(B)}`:""].filter(Boolean).join(" \u2192 ")}}let _;if(p==="gateway-request")_=`${s.host??""}${String(s.url??"").split("?")[0]}`;else if(p==="fetch-start"||p==="fetch-done"||p==="fetch-error")try{const d=new URL(String(s.href??s.url??""));_=`${d.hostname}${d.pathname}`}catch{}let O;if(p==="gateway-request"&&Array.isArray(s.steps))O=s.steps.map(d=>{const m=[String(d.step)];return d.project&&m.push(String(d.project)),d.prefix&&m.push(String(d.prefix)),d.mode&&m.push(String(d.mode)+(d.ok===!1?" \u2717":d.ok===!0?" \u2713":"")),d.caller&&m.push("by "+String(d.caller)),d.pod&&m.push(String(d.pod)),typeof d.atMs=="number"&&m.push(`@${d.atMs}ms`),m.join(" ")}),b=O.map(d=>d.replace(/ @\d+ms$/,"")).join(" \u2192 ");else if(p==="egress")b=`${s.method??"GET"} ${s.host??""} \u2192 ${s.status??"?"}`;else if(i.source==="ai"){if(p==="ai-call"){const d=s.usage??{},m=d.promptTokens!==void 0||d.completionTokens!==void 0?` \xB7 ${g(d.promptTokens)??"?"}\u2192${g(d.completionTokens)??"?"} tok`:"",B=s.status!==void 0?` \xB7 ${String(s.status)}`:s.error?" \xB7 failed":"",h=g(s.toolCallCount)?` \xB7 ${g(s.toolCallCount)} tool call${g(s.toolCallCount)===1?"":"s"}`:"",D=g(s.durationMs)!==void 0?` \xB7 ${(g(s.durationMs)/1e3).toFixed(1)}s`:"";b=`${s.provider??""}/${s.model??"?"}${m}${B}${h}${D}${s.stream===!0?" \xB7 stream":""}`}else if(p==="ai-round"){const d=s.model??{},m=s.usage??{},B=Array.isArray(s.toolCalls)?s.toolCalls:[],h=`${g(m.promptTokens)??"?"}\u2192${g(m.completionTokens)??"?"} tok`,D=B.length?` \xB7 ${B.length} tool call${B.length===1?"":"s"}`:"",K=g(s.durationMs)!==void 0?` \xB7 ${(g(s.durationMs)/1e3).toFixed(1)}s`:"";b=`${d.provider??""}/${d.model??""} \xB7 ${h}${D}${K}`}else if(p==="ai-turn-start"){const d=Array.isArray(s.tools)?s.tools:[];b=`skill ${s.skill??""} \xB7 ${s.output??""}${d.length?" \xB7 tools: "+d.join(", "):""}`}else if(p==="ai-tool-call"){const d=s.call??{},m=s.result??{},B=g(s.durationMs)!==void 0?` \xB7 ${(g(s.durationMs)/1e3).toFixed(1)}s`:"";b=`${d.name??"tool"} ${m.error!=null?"\u2717 "+String(m.error):"\u2713"}${B}`}else if(p==="ai-turn-end"){const d=s.usage??{},m=g(s.rounds)!==void 0?` \xB7 ${g(s.rounds)} round${g(s.rounds)===1?"":"s"}`:"",B=g(s.durationMs)!==void 0?` \xB7 ${(g(s.durationMs)/1e3).toFixed(1)}s`:"";b=`${s.ok===!1?"FAILED":"ok"} \xB7 ${g(d.promptTokens)??"?"}+${g(d.completionTokens)??"?"} tok${m}${B}`}}let C;p==="db-query"&&typeof s.query=="string"?C=s.query:p==="redis-op"?C=`${s.op??""} ${String(s.key??"").replace(/\d+/g,"#")}`:p==="egress"?C=`${s.method??"GET"} ${s.host??""}`:p==="ai-call"?C=`ai ${s.provider??""} ${s.model??""} ${s.api??""}`:p==="queue-enqueued"?C=`enqueue ${s.queue??""}`:p==="span"&&typeof l.name=="string"?C=l.name:p==="memory-mutation"&&(C=i.title);const E=i.context??{},Z=M.commitId??y.commitId??s.commitId,z=Array.isArray(M.why)?M.why.map(d=>typeof d?.type=="string"?d.type:void 0).filter(d=>!!d):[];return{id:i.id,t:i.timestamp,start:V,...C?{sig:C}:{},..._?{urlKey:_}:{},...httpFacts(p,s),...(p==="db-query"||p==="redis-op")&&typeof s.valuesId=="string"?{valuesId:s.valuesId}:{},...typeof s.queue=="string"?{queue:s.queue}:{},...s.jobId!=null?{jobId:String(s.jobId)}:{},source:i.source,category:i.category,severity:i.severity,title:L==="threw"&&typeof l.name=="string"?`${l.name} \u2014 threw${Y?` ${Y}`:""}${j!==void 0?` after ${j}ms`:""}`:i.title,kind:p,area:areaOf({source:i.source,category:i.category,title:i.title,kind:p,...typeof s.host=="string"?{host:s.host}:{}}),...p==="egress"&&$t(typeof s.host=="string"?s.host:void 0)?{hop:!0}:{},...p==="egress"&&typeof s.calleeRequestId=="string"?{calleeRequestId:s.calleeRequestId}:{},...L?{outcome:L}:{},...W?{error:W}:{},...U?{captured:U}:{},...typeof l.role=="string"?{role:l.role}:{},...typeof l.spanId=="string"?{spanId:l.spanId}:{},...typeof l.parentSpanId=="string"?{parentSpanId:l.parentSpanId}:{},...p==="log"&&typeof s.level=="string"?{level:s.level}:{},...p==="log"&&typeof s.logger=="string"?{logger:s.logger}:{},...p==="log"&&typeof s.stream=="string"?{stream:s.stream}:{},...typeof E.requestId=="string"?{requestId:E.requestId}:{},...typeof E.parentRequestId=="string"?{parentRequestId:E.parentRequestId}:{},...typeof Z=="string"?{commitId:Z}:{},...typeof M.componentName=="string"?{component:M.componentName}:{},...typeof M.phase=="string"?{phase:M.phase}:{},...z.length?{why:z}:{},...p==="effect-run"&&typeof y.hookLogicalId=="string"?{hook:y.hookLogicalId}:{},...p==="effect-run"&&y.cleanupRan===!0?{cleanup:!0}:{},...p==="render-commit-truncated"?{renderTotal:g(s.total),renderPublished:g(s.published),...g(s.walkStoppedAt)!==void 0?{renderWalkStoppedAt:g(s.walkStoppedAt)}:{}}:{},...typeof E.projectName=="string"?{project:E.projectName}:typeof s.project=="string"?{project:s.project}:{},...j!==void 0?{durationMs:j}:{},...T&&g(s.startedAt)!==void 0?{fetchStartedAt:g(s.startedAt)}:{},...i.source==="ai"?{ai:f.includeRaw?s:vt(s)}:{},...b?{detail:b}:{},...O?{steps:O}:{}}}R(trimFlowEvent,"trimFlowEvent"),k(trimFlowEvent,"trimFlowEvent");const wt=300;export const OUTLIVED_PARENT_MIN_MS=250;const Ot=new Set(["egress","db-query","redis-op","ai-call","span","gateway-request","server-request"]),at=1;function Mt(i){return i==="flow-origin"?0:i?.endsWith("-start")?1:i==="gateway-request"?2:i==="server-request"?3:i==="span"?4:i==="render"||i==="render-commit-truncated"?6:i==="effect-run"?7:5}R(Mt,"Nt"),k(Mt,"kindRank");function J(i){return i.kind==="span"&&i.role==="controller"?3.5:i.kind==="queue-process"?3:Mt(i.kind)}R(J,"Z"),k(J,"causalRank");export function buildFlowDetail(i,f={}){const s=Tt(i),M=i.slice().filter(t=>{const r=t.raw??{},e=typeof r.kind=="string"?r.kind:"";if(e==="subscription-added"||e==="subscription-removed"||e==="hook-run"||e==="store-created"||e==="singleton-instance"||e===Rt)return!1;const o=r.span;return!(typeof o?.name=="string"&&o.name.startsWith("nest.request "))}).map(t=>trimFlowEvent(t,f)),l=new Set;for(let t=M.length-1;t>=0;t--)l.has(M[t].id)?M.splice(t,1):l.add(M[t].id);const P=new Map;for(const t of M)(t.kind==="gateway-request"||t.kind==="queue-process")&&t.requestId&&!P.has(t.requestId)&&P.set(t.requestId,t.start);const g=k(t=>t.source!=="react"&&t.source!=="valtio"&&t.requestId&&P.has(t.requestId)?P.get(t.requestId):t.start,"anchorOf");M.sort((t,r)=>g(t)-g(r)||t.start-r.start||J(t)-J(r)||(r.durationMs??0)-(t.durationMs??0)||t.t-r.t);const I=k(t=>(t.kind==="span"||t.kind==="server-request"||t.kind==="gateway-request"||t.kind==="queue-process")&&(t.durationMs??0)>0,"spanLike"),y=M;for(const t of y)t.group=t.source==="react"||t.source==="valtio"||t.source==="client-runtime"||t.source==="visual"?"browser":"server";const j=new Map;for(const t of y)if((t.kind==="gateway-request"||t.kind==="queue-process")&&t.requestId&&t.project){const r=j.get(t.requestId)??[];r.push({project:t.project,start:t.start,end:t.start+(t.durationMs??0)}),j.set(t.requestId,r)}const p=k((t,r)=>{const e=j.get(t);if(e?.length)return e.every(o=>o.project===e[0].project)?e[0].project:e.filter(o=>o.start<=r+1&&o.end>=r-1).sort((o,n)=>n.start-o.start)[0]?.project},"projectOf");for(const t of y)if(!t.project&&t.source==="nestjs"&&t.requestId){const r=p(t.requestId,t.start);r&&(t.project=r)}const T=new Map;for(const t of y)t.spanId&&T.set(t.spanId,t);const G=k((t,r)=>{let e=t.parentSpanId?T.get(t.parentSpanId):void 0;for(let o=0;e&&o<64;o++){if(e===r)return!0;e=e.parentSpanId?T.get(e.parentSpanId):void 0}return!1},"inside"),H=new Map(y.map((t,r)=>[t,r])),V=new Map,b=k(t=>{let r=V.get(t.group);r||V.set(t.group,r=new Map);const e=t.requestId;let o=r.get(e);return o||r.set(e,o={spans:[],next:0,alive:[]}),o},"legOf");for(const t of y)I(t)&&Number.isFinite(t.start)&&b(t).spans.push(t);for(const t of V.values())for(const r of t.values())r.spans.sort((e,o)=>e.start-o.start||H.get(e)-H.get(o));const L=k(t=>Number.isFinite(t.start)?t.start:1/0,"startOf");for(const t of y.slice().sort((r,e)=>L(r)-L(e))){const r=t.parentSpanId?T.get(t.parentSpanId):void 0;if(r&&r!==t&&r.group===t.group&&r.requestId===t.requestId){t.parent=r.id;continue}if(!Number.isFinite(t.start))continue;const e=b(t);for(;e.next<e.spans.length&&e.spans[e.next].start<=t.start+at;)e.alive.push(e.spans[e.next++]);e.alive=e.alive.filter(a=>a.start+(a.durationMs??0)>t.start);const o=e.alive.length>1?e.alive.slice().sort((a,$)=>H.get(a)-H.get($)):e.alive;let n;for(const a of o){if(a===t)continue;const $=a.project,w=t.project;if(a.kind!=="gateway-request"&&t.kind!=="gateway-request"&&$!==void 0&&w!==void 0&&$!==w)continue;const u=a.durationMs??0,c=t.durationMs??0,v=Math.abs(a.start-t.start)<=at,q=J(a),A=J(t),x=a.start<=t.start;if(!(a.start+u>t.start&&(x||v&&q<A)&&(!v||(q!==A?q<A:x&&u>c))))continue;const F=n?J(n):-1,N=n?Math.abs(a.start-n.start)<=at:!1,Q=n&&N&&q===F?G(a,n)?1:G(n,a)?-1:0:0;(!n||!N&&a.start>n.start||N&&q>F||Q>0||Q===0&&N&&q===F&&(a.durationMs??0)<(n.durationMs??0))&&(n=a)}n&&(t.parent=n.id)}for(const t of y)delete t.spanId,delete t.parentSpanId;{const t=new Set(y.map(a=>a.id)),r=new Set,e=new Map,o=[],n=k(a=>{const $=[a];for(;$.length;){const w=$.pop();o.push(w),r.add(w.id);const u=e.get(w.id);if(u){e.delete(w.id);for(let c=u.length-1;c>=0;c--)$.push(u[c])}}},"place");for(const a of y)if(a.parent&&a.parent!==a.id&&t.has(a.parent)&&!r.has(a.parent)){const $=e.get(a.parent)??[];$.push(a),e.set(a.parent,$)}else n(a);for(const a of y)r.has(a.id)||(o.push(a),r.add(a.id));for(let a=0;a<o.length;a++)y[a]=o[a]}const W=new Map;for(const t of y){if(!t.requestId)continue;const r=W.get(t.requestId);r?r.push(t):W.set(t.requestId,[t])}const Y=new Map(y.map(t=>[t.id,t])),U=new Map;for(const t of y)t.calleeRequestId&&!U.has(t.calleeRequestId)&&U.set(t.calleeRequestId,t);const _=k((t,r)=>{const e=(W.get(t)??[]).filter(o=>I(o)&&o.start<=r&&o.start+(o.durationMs??0)>=r);if(e.length!==0)return e.sort((o,n)=>n.start-o.start||(o.durationMs??0)-(n.durationMs??0)),e[0]},"callerNodeFor");for(const t of y){if(!t.parentRequestId||t.kind!=="server-request"&&t.kind!=="gateway-request")continue;const r=t.parent?Y.get(t.parent):void 0;if(r&&r.requestId===t.requestId)continue;const e=t.requestId?U.get(t.requestId):void 0,o=e&&e.start<=t.start?e:_(t.parentRequestId,t.start);o&&o!==t&&o.start<=t.start&&(t.parent=o.id)}const O=y,C=new Map;for(const t of O)t.kind==="gateway-request"&&t.requestId&&!C.has(t.requestId)&&C.set(t.requestId,t);for(const t of O)if(t.group==="server"&&!t.parent&&t.requestId&&t.kind!=="gateway-request"){const r=C.get(t.requestId);r&&r!==t&&r.start<=t.start&&(t.parent=r.id)}const E=new Set,Z=new Set,z=[];for(const t of O){if(t.kind!=="gateway-request")continue;if(!t.requestId){z.push(t);continue}const r=W.get(t.requestId)??[];if(r.filter(n=>n.kind==="gateway-request").sort((n,a)=>n.start-a.start)[0]!==t)continue;const e=r.find(n=>n.kind==="fetch-start"),o=r.find(n=>n.kind==="fetch-done"||n.kind==="fetch-error");if(!e&&!o){z.push(t);continue}e&&(t.fetchStartId=e.id,E.add(e.id)),o&&(t.fetchDoneId=o.id,Z.add(o.id))}const d=new Map;for(const t of O){if(!t.urlKey)continue;const r=d.get(t.urlKey)??{gws:[],starts:[],dones:[]};t.kind==="gateway-request"?z.includes(t)&&r.gws.push(t):t.kind==="fetch-start"?E.has(t.id)||r.starts.push(t):(t.kind==="fetch-done"||t.kind==="fetch-error")&&(Z.has(t.id)||r.dones.push(t)),d.set(t.urlKey,r)}for(const{gws:t,starts:r,dones:e}of d.values()){t.sort((n,a)=>n.start-a.start),r.sort((n,a)=>n.start-a.start);const o=new Set;t.forEach((n,a)=>{const $=r[a];$&&(n.fetchStartId=$.id);const w=$?$.t:n.start;let u,c=1/0;for(const v of e){if(o.has(v.id))continue;const q=v.fetchStartedAt??v.start-(v.durationMs??0),A=Math.abs(q-w);A<c&&(c=A,u=v)}u&&(n.fetchDoneId=u.id,o.add(u.id))})}const m=new Map,B=new Map;for(const t of O)!t.queue||!t.jobId||(t.kind==="queue-enqueued"&&m.set(`${t.queue}#${t.jobId}`,t),t.kind==="queue-process"&&B.set(`${t.queue}#${t.jobId}`,t));for(const t of O){if(!t.queue||!t.jobId)continue;const r=`${t.queue}#${t.jobId}`;if(t.kind==="queue-process"&&!t.parent){const e=m.get(r);e&&e!==t&&e.start<=t.start&&(t.parent=e.id)}else if(t.kind==="queue-active"||t.kind==="queue-completed"||t.kind==="queue-failed"){const e=B.get(r);e&&e!==t&&e.start<=t.start&&(t.parent=e.id)}}{const t=new Map(y.map(r=>[r.id,r]));for(const r of y){if(!r.parent||!Ot.has(r.kind??"")||r.durationMs===void 0)continue;const e=t.get(r.parent);if(!e||e.kind!=="span"||!((e.durationMs??0)>0))continue;const o=Math.round(r.start+r.durationMs-(e.start+e.durationMs));if(o<OUTLIVED_PARENT_MIN_MS)continue;r.outlivedParentMs=o;const n=e.leftRunning??{count:0,longestMs:0};e.leftRunning={count:n.count+1,longestMs:Math.max(n.longestMs,o)}}for(const r of y){const e=[];r.leftRunning&&e.push(`ended before ${r.leftRunning.count===1?"a call":`${r.leftRunning.count} calls`} it started had finished \u2014 ${r.leftRunning.count===1?"it":"the longest"} ran on ${r.leftRunning.longestMs}ms (a timeout that gave up, or work not awaited)`),r.outlivedParentMs!==void 0&&e.push(`ran on ${r.outlivedParentMs}ms after the span that started it had ended`),e.length>0&&(r.detail=[r.detail,...e].filter(Boolean).join(" \xB7 "))}}let h=y;{const t=new Map;for(const e of h)e.kind==="fetch-start"&&t.set(e.id,e);const r=new Map;for(const e of h){if(e.kind!=="fetch-done"&&e.kind!=="fetch-error")continue;const o=[...t.values()].find($=>$.requestId&&$.requestId===e.requestId)?.id,n=o?t.get(o):void 0;if(!n)continue;const a=e.fetchStartedAt??n.start;n.title=e.title,n.severity=e.severity,n.durationMs=e.durationMs??Math.max(0,e.t-a),n.kind="fetch",e.detail&&!n.detail&&(n.detail=e.detail),r.set(e.id,n.id)}if(r.size>0){h=h.filter(e=>!r.has(e.id));for(const e of h)for(e.fetchDoneId&&r.has(e.fetchDoneId)&&(e.fetchDoneId=r.get(e.fetchDoneId));e.parent&&r.has(e.parent);)e.parent=r.get(e.parent)}}const D=k(t=>!!t.commitId&&(t.kind==="render"||t.kind==="effect-run"||t.kind==="render-commit-truncated"),"commitMember"),K=k(t=>[...new Set(t)],"uniq"),st=k(t=>K(t.map(r=>r.component).filter(r=>!!r)),"named"),qt=k(t=>t.length<=2?t.join(" + "):`${t[0]} + ${t.length-1} more`,"nameList"),it=k(t=>{const r=new Map;for(const e of t)e.component&&r.set(e.component,(r.get(e.component)??0)+1);return[...r].map(([e,o])=>o>1?`${e} \xD7${o}`:e).join(", ")},"tally"),It=k(t=>{if(!t)return"effect";const r=/([^/\\]+?):(\d+)(?::\d+)?$/.exec(t);return r?`${r[1]}:${r[2]}`:t},"shortHook"),ot=k(t=>`${It(t.hook)}${t.cleanup?" (cleanup)":""}`,"hookLabel"),dt=k(t=>{const r=Math.min(...t.map(o=>o.start)),e=Math.max(...t.map(o=>o.start+(o.durationMs??0)));return{start:r,durationMs:e-r}},"extent"),ut=k(t=>(delete t.component,delete t.why,delete t.phase,delete t.hook,delete t.cleanup,delete t.renderTotal,delete t.renderPublished,t),"asGroup");function ct(t){const r=t[0],e=t.filter(S=>S.kind==="render"),o=t.filter(S=>S.kind==="effect-run"),n=t.find(S=>S.kind==="render-commit-truncated"),a=[...e].sort((S,X)=>(X.durationMs??0)-(S.durationMs??0)),$=a.filter(S=>(S.why??[]).some(X=>X!=="parent-render")),w=K($.flatMap(S=>(S.why??[]).filter(X=>X!=="parent-render"))),u=st($),c=e.filter(S=>S.phase==="mount"),v=e.filter(S=>S.phase!=="mount"&&!$.includes(S)),q=a[0]?.component??"view",A=e.length>0&&c.length===e.length,x=w.length?w.join(" + "):void 0,F=A?"mount":"update",N=u.length?`view update \xB7 ${qt(u)} \u2190 ${x}`:A?`view mount \xB7 ${q}`:`view update \xB7 ${q}`,Q=n?.renderTotal??e.length,xt=[c.length?`mounted ${it(c)}`:"",v.length?`re-rendered ${it(v)}`:"",!u.length&&!A&&e.length?"trigger not recorded \u2014 every captured render came from its parent":"",n?.renderWalkStoppedAt!==void 0?`at least ${Q} renders (the walk stopped at ${n.renderWalkStoppedAt} fibers), ${n.renderPublished??e.length} recorded`:n?`${Q} renders, ${n.renderPublished??e.length} recorded (per-commit cap)`:`${e.length} render${e.length===1?"":"s"}`,o.length?`${o.length} effect${o.length===1?"":"s"}: ${o.map(ot).join(", ")}`:""].filter(Boolean).join(" \xB7 "),gt=st(e);return ut({...r,...dt(t),kind:"view-update",title:N,detail:xt,commitId:r.commitId,viewPhase:F,...x?{cause:x}:{},causeComponents:u,components:gt,mounted:st(c),renderCount:Q,...n?.renderWalkStoppedAt!==void 0?{renderCountAtLeast:!0}:{},recordedRenders:e.length,effects:o.length,effectHooks:o.map(ot),memberIds:t.map(S=>S.id),sig:`commit:${F}:${x??"-"}:${u.join(",")}:${gt.slice().sort().join(",")}`,severity:t.some(S=>S.severity==="error")?"error":r.severity})}R(ct,"It"),k(ct,"viewUpdateNode");function lt(t){const r=t[0],e=t.map(ot);return ut({...r,...dt(t),kind:"commit-effects",title:t.length===1?`effect \xB7 ${e[0]}`:`effects \xB7 ${e[0]} + ${t.length-1} more`,detail:e.join(", "),commitId:r.commitId,effects:t.length,effectHooks:e,memberIds:t.map(o=>o.id),sig:`effects:${e.slice().sort().join(",")}`})}R(lt,"kt"),k(lt,"effectsNode");const bt=k(t=>K(t.map(r=>r==="state-update"?"state update":r==="fetch-start"?"request":r==="flow-origin"?"interaction":"response")).join(" + "),"describeInputs");function pt(t,r){const e=t[0],o=t[t.length-1],n=t.length,a=e.detail,$=Math.min(...t.map(v=>v.start)),w=Math.max(...t.map(v=>v.start+(v.durationMs??0))),u=Math.round(t.reduce((v,q)=>v+(q.durationMs??0),0)/n*10)/10,c=Math.round((w-$)*10)/10;e.count=n,e.avgMs=u,e.wallMs=c,e.durationMs=w-$,e.items=t.slice(0,12).map(v=>({start:v.start-$,...v.durationMs!=null?{durationMs:v.durationMs}:{}})),e.memberIds=t.flatMap(v=>v.memberIds??[v.id]),e.title=`\xD7${n} ${e.title}`,t.some(v=>v.severity==="error")&&(e.severity="error"),e.kind==="view-update"?e.detail=[`${n} commits, ${r.length?`one per ${bt(r)}`:"back to back"}`,`avg ${u}ms \xB7 wall ${c}ms`,a??""].filter(Boolean).join(" \xB7 "):e.kind==="state-update"&&a&&o.detail?e.detail=`${n} updates in a row \xB7 wall ${c}ms \xB7 first ${a} \xB7 last ${o.detail}`:e.detail=[`${n} in a row \xB7 wall ${c}ms`,a??""].filter(Boolean).join(" \xB7 ")}R(pt,"qt"),k(pt,"collapseRepeat");const tt=[],et=new Map,ft=new Set;for(let t=0;t<h.length;t++){if(ft.has(t))continue;const r=h[t];if(!D(r)){tt.push(r);continue}const e=[t];for(let a=t+1;a<h.length;a++){const $=h[a];if($.group==="browser"){if(!D($)||$.commitId!==r.commitId)break;e.push(a)}}for(const a of e)ft.add(a);const o=e.map(a=>h[a]),n=o.some(a=>a.kind!=="effect-run")?ct(o):lt(o);for(const a of o)a.id!==n.id&&et.set(a.id,n.id);tt.push(n)}if(et.size>0||tt.some(t=>t.kind==="view-update"||t.kind==="commit-effects")){h=tt;for(const t of h)for(;t.parent&&et.has(t.parent);)t.parent=et.get(t.parent)}{const t=new Map;for(const r of h)r.kind==="view-update"&&r.commitId&&!t.has(r.commitId)&&t.set(r.commitId,r);for(const r of h){if(r.kind!=="commit-effects"||r.parent||!r.commitId)continue;const e=t.get(r.commitId);e&&e.start<=r.start&&(r.parent=e.id)}}{const t=new Set(["state-update","fetch","fetch-start","fetch-done","fetch-error","flow-origin"]),r=h.map((u,c)=>u.group==="browser"?c:-1).filter(u=>u>=0),e=k(u=>`${u.kind}|${u.sig??u.title}`,"keyOf"),o=r.map(u=>e(h[u])),n=k((u,c,v)=>{for(let q=0;q<v;q++)if(o[u+q]!==o[c+q])return!1;return!0},"sameBlock"),a=k(u=>o[u].split("|")[0],"kindAt"),$=k(u=>{let c=0,v=1;for(let q=1;q<=4&&u+q<=o.length;q++){let A=1;for(;u+(A+1)*q<=o.length&&n(u,u+A*q,q);)A+=1;A>=3&&q*A>c*v&&(c=q,v=A)}return{k:c,r:v}},"bestBlockAt"),w=new Map;for(let u=0;u<o.length;){const{k:c,r:v}=$(u);if(c===0){u+=1;continue}const q=[...Array(c).keys()].find(x=>t.has(a(u+x)));if(q!==void 0&&q>0){const x=$(u+q);if(x.k===c&&x.r>=3&&x.r>=v-1){u+=q;continue}}const A=o.slice(u,u+c).map(x=>x.split("|")[0]).filter(x=>t.has(x));for(let x=0;x<c;x++){const F=[];for(let N=0;N<v;N++)F.push(h[r[u+x+N*c]]);pt(F,A);for(const N of F.slice(1))w.set(N.id,F[0].id)}u+=c*v}if(w.size>0){h=h.filter(u=>!w.has(u.id));for(const u of h)for(;u.parent&&w.has(u.parent);)u.parent=w.get(u.parent)}}{const t=new Map(h.map(r=>[r.id,r]));for(const r of h){const e=new Set([r.id]);let o=r;for(;o?.parent;){if(e.has(o.parent)){o.parent=void 0;break}e.add(o.parent),o=t.get(o.parent)}}}const mt=k(t=>{const r=new Map;for(const o of h){if(!o.sig||I(o)!==t||o.kind==="server-request"||o.kind==="gateway-request"||o.group==="browser")continue;const n=`${o.parent??"root"}|${o.kind}|${o.sig}`,a=r.get(n);a?a.push(o):r.set(n,[o])}const e=new Map;for(const o of r.values()){if(o.length<3)continue;o.sort((c,v)=>c.start-v.start);const n=o[0];let a=!1;for(let c=0;c+1<o.length;c++)if(o[c+1].start<o[c].start+(o[c].durationMs??0)){a=!0;break}const $=n.start,w=Math.max(...o.map(c=>c.start+(c.durationMs??0))),u=o.reduce((c,v)=>c+(v.durationMs??0),0);n.count=o.length,n.parallel=a,n.avgMs=Math.round(u/o.length*10)/10,n.wallMs=w-$,n.durationMs=w-$,n.title=`\xD7${o.length} ${n.title.replace(/\s*\((?:\d+ rows?, )?\d+ms\)$/,"")}`,n.detail=`${n.sig} \xD7${o.length} ${a?"\u2016 parallel":"\u21BB sequential loop"} \xB7 avg ${n.avgMs}ms \xB7 wall ${n.wallMs}ms`,n.items=o.slice(0,12).map(c=>({start:c.start-$,...c.durationMs!=null?{durationMs:c.durationMs}:{}})),n.memberIds=o.flatMap(c=>c.memberIds??[c.id]),o.some(c=>c.severity==="error")&&(n.severity="error");for(const c of o.slice(1))e.set(c.id,n.id)}if(e.size!==0){h=h.filter(o=>!e.has(o.id));for(const o of h)for(;o.parent&&e.has(o.parent);)o.parent=e.get(o.parent)}},"aggregatePass");mt(!0),mt(!1);for(const t of h){if(!I(t))continue;const r=t.durationMs??0;if(r<40)continue;const e=h.filter(w=>w.parent===t.id&&(w.durationMs??0)>0).map(w=>[w.start,w.start+(w.durationMs??0)]).sort((w,u)=>w[0]-u[0]);let o=0,n=-1,a=-1;for(const[w,u]of e)w>a?(a>n&&(o+=a-n),n=w,a=u):u>a&&(a=u);a>n&&(o+=a-n);const $=Math.max(0,r-o);($>=50||$/r>=.7&&r>=40)&&(t.selfMs=$,t.detail=`\u26A0 cpu-heavy: ${$}ms self-time of ${r}ms${t.detail?" \xB7 "+t.detail:""}`)}return{events:h,...s?{sql:s}:{}}}R(buildFlowDetail,"buildFlowDetail"),k(buildFlowDetail,"buildFlowDetail");
@@ -1,15 +1,20 @@
1
1
  import type { HttpExchange } from '@lensmcp/protocol-types';
2
2
  export type { HttpExchange, HttpExchangeBody, HttpExchangeStreamEvent } from '@lensmcp/protocol-types';
3
+ export { isServiceHopHost, LENS_REQUEST_ID_HEADER } from './service-hop.js';
3
4
  export declare const HTTP_EXCHANGE_DIR = "http";
4
5
  /**
5
6
  * Where an exchange is kept, each place pruned on its own budget: a third-party call (`http/`), a request a service
6
7
  * served (`http/in/`), and a call between the cluster's own services through the gateway (`http/hops/`, a host under
7
8
  * `.localhost`). One budget for all let FoodGuard's ~2,000 service-to-service calls an hour push its OpenAI calls out.
9
+ * An AI provider's call (`http/ai/`) is kept, not pruned — the prompts and answers reviews are made of — in a place of
10
+ * its own, so pruning the others never has to open a file to tell one apart.
8
11
  */
9
- export type HttpExchangePlace = 'calls' | 'in' | 'hops';
12
+ export type HttpExchangePlace = 'calls' | 'in' | 'hops' | 'ai';
10
13
  export declare const HTTP_EXCHANGE_PLACES: readonly HttpExchangePlace[];
11
14
  /** The place an exchange belongs in. */
12
- export declare function exchangePlace(x: Pick<HttpExchange, 'side' | 'host'>): HttpExchangePlace;
15
+ export declare function exchangePlace(x: Pick<HttpExchange, 'side' | 'host'> & {
16
+ ai?: unknown;
17
+ }): HttpExchangePlace;
13
18
  export declare const isHttpExchangeId: (value: unknown) => value is string;
14
19
  export declare function httpExchangeDir(busFile: string, place?: HttpExchangePlace): string;
15
20
  export declare function httpExchangeFile(busFile: string, id: string, place?: HttpExchangePlace): string;
@@ -1 +1 @@
1
- "use strict";var i=Object.defineProperty;var n=(t,e)=>i(t,"name",{value:e,configurable:!0});var s=Object.defineProperty,a=n((t,e)=>s(t,"name",{value:e,configurable:!0}),"r");import*as h from"node:fs";import{dirname as p,join as o}from"node:path";export const HTTP_EXCHANGE_DIR="http",HTTP_EXCHANGE_PLACES=["calls","in","hops"];export function exchangePlace(t){return t.side==="ingress"?"in":/(^|\.)localhost$/i.test(t.host)?"hops":"calls"}n(exchangePlace,"exchangePlace"),a(exchangePlace,"exchangePlace");const l=/^hx_[0-9a-z]{8,40}$/;export const isHttpExchangeId=a(t=>typeof t=="string"&&l.test(t),"isHttpExchangeId");export function httpExchangeDir(t,e="calls"){const r=o(p(t),HTTP_EXCHANGE_DIR);return e==="calls"?r:o(r,e)}n(httpExchangeDir,"httpExchangeDir"),a(httpExchangeDir,"httpExchangeDir");export function httpExchangeFile(t,e,r="calls"){if(!isHttpExchangeId(e))throw new Error("invalid http exchange id");return o(httpExchangeDir(t,r),`${e}.json`)}n(httpExchangeFile,"httpExchangeFile"),a(httpExchangeFile,"httpExchangeFile");export function readHttpExchange(t,e){if(isHttpExchangeId(e))for(const r of HTTP_EXCHANGE_PLACES)try{const c=JSON.parse(h.readFileSync(httpExchangeFile(t,e,r),"utf8"));if(c&&c.schemaVersion===1&&c.id===e)return c}catch{}}n(readHttpExchange,"readHttpExchange"),a(readHttpExchange,"readHttpExchange");
1
+ "use strict";var i=Object.defineProperty;var n=(t,e)=>i(t,"name",{value:e,configurable:!0});var s=Object.defineProperty,a=n((t,e)=>s(t,"name",{value:e,configurable:!0}),"n");import*as p from"node:fs";import{dirname as h,join as c}from"node:path";export{isServiceHopHost,LENS_REQUEST_ID_HEADER}from"./service-hop.js";export const HTTP_EXCHANGE_DIR="http",HTTP_EXCHANGE_PLACES=["calls","ai","in","hops"];export function exchangePlace(t){return t.side==="ingress"?"in":t.ai?"ai":/(^|\.)localhost$/i.test(t.host)?"hops":"calls"}n(exchangePlace,"exchangePlace"),a(exchangePlace,"exchangePlace");const l=/^hx_[0-9a-z]{8,40}$/;export const isHttpExchangeId=a(t=>typeof t=="string"&&l.test(t),"isHttpExchangeId");export function httpExchangeDir(t,e="calls"){const r=c(h(t),HTTP_EXCHANGE_DIR);return e==="calls"?r:c(r,e)}n(httpExchangeDir,"httpExchangeDir"),a(httpExchangeDir,"httpExchangeDir");export function httpExchangeFile(t,e,r="calls"){if(!isHttpExchangeId(e))throw new Error("invalid http exchange id");return c(httpExchangeDir(t,r),`${e}.json`)}n(httpExchangeFile,"httpExchangeFile"),a(httpExchangeFile,"httpExchangeFile");export function readHttpExchange(t,e){if(isHttpExchangeId(e))for(const r of HTTP_EXCHANGE_PLACES)try{const o=JSON.parse(p.readFileSync(httpExchangeFile(t,e,r),"utf8"));if(o&&o.schemaVersion===1&&o.id===e)return o}catch{}}n(readHttpExchange,"readHttpExchange"),a(readHttpExchange,"readHttpExchange");
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A call to one of the cluster's own services, not a third party: a host under `.localhost` — a name below it, never
3
+ * bare `localhost`, which is any process on a port (a local model's `localhost:11434` is still a provider of its own).
4
+ *
5
+ * Every `*.localhost` name is loopback by definition (RFC 6761), and it is where the dev gateway serves the cluster —
6
+ * `app.foodguard.localhost`, `internal.api.foodguard.localhost`, a worktree's `internal.api.gate1.foodguard.localhost`.
7
+ * So a service calling one is a hop through the gateway to another service (or to itself): the gateway routes it and
8
+ * the callee serves it. It was drawn as an External API — in a flow's «External APIs» row, on its Map as an external
9
+ * box — beside OpenAI and Stripe.
10
+ *
11
+ * No dependencies: the egress tap, the flow build and the catalogs share it.
12
+ */
13
+ export declare function isServiceHopHost(host: string | undefined): boolean;
14
+ /**
15
+ * The lens's own id for the request a hop opens, minted by the caller's egress tap (`<caller's request id>.<n>`). The
16
+ * gateway and the callee's lens prefer it to `x-request-id`, which belongs to the app: one that forwards its own id to
17
+ * the services it calls — a trace id it keeps for a whole chain of requests — gave every hop the same id, and the lens
18
+ * filed the callee's work under whichever request first carried it.
19
+ */
20
+ export declare const LENS_REQUEST_ID_HEADER = "x-lensmcp-request-id";
@@ -0,0 +1 @@
1
+ "use strict";var o=Object.defineProperty;var r=(e,t)=>o(e,"name",{value:t,configurable:!0});var s=Object.defineProperty,c=r((e,t)=>s(e,"name",{value:t,configurable:!0}),"t");export function isServiceHopHost(e){return e?/.\.localhost$/i.test(e.replace(/\.$/,"")):!1}r(isServiceHopHost,"isServiceHopHost"),c(isServiceHopHost,"isServiceHopHost");export const LENS_REQUEST_ID_HEADER="x-lensmcp-request-id";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/core",
3
- "version": "1.25.31",
3
+ "version": "1.25.32",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -84,7 +84,7 @@
84
84
  }
85
85
  },
86
86
  "dependencies": {
87
- "@lensmcp/protocol-types": "1.25.31",
87
+ "@lensmcp/protocol-types": "1.25.32",
88
88
  "tslib": "^2.3.0"
89
89
  },
90
90
  "license": "Apache-2.0",