@lensmcp/node-instrumentation 1.25.24 → 1.25.26

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/lib/ai-store.d.ts CHANGED
@@ -9,12 +9,11 @@ export declare function fitAiContent(src: AiCallContent): {
9
9
  json: string;
10
10
  content: AiCallContent;
11
11
  };
12
- /** Delete one call's file — used when its event was dropped, so nothing is left pointing nowhere. */
13
- export declare function removeAiContent(id: string): void;
14
12
  /**
15
13
  * Persist one call. Best-effort: returns `undefined` on any failure so the tap can emit the event
16
14
  * without a `contentRef` rather than lose the metadata. Written to a temp name and renamed, so a
17
- * reader never sees half a file.
15
+ * reader never sees half a file. Captured AI records are retained without automatic pruning,
16
+ * independently of the event bus. The former LENSMCP_AI_MAX_FILES limit is intentionally ignored.
18
17
  */
19
18
  export declare function writeAiContent(src: AiCallContent): {
20
19
  id: string;
package/lib/ai-store.js CHANGED
@@ -1 +1 @@
1
- "use strict";var g=Object.defineProperty;var l=(e,r)=>g(e,"name",{value:r,configurable:!0});var m=Object.defineProperty,i=l((e,r)=>m(e,"name",{value:r,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactAiText=void 0,exports.newAiCallId=newAiCallId,exports.fitAiContent=fitAiContent,exports.removeAiContent=removeAiContent,exports.writeAiContent=writeAiContent;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),ai_content_1=require("@lensmcp/core/ai-content"),redact_1=require("@lensmcp/core/redact"),emit_1=require("./emit"),mkdirSync=nodeFs.mkdirSync,writeFileSync=nodeFs.writeFileSync,renameSync=nodeFs.renameSync,readdirSync=nodeFs.readdirSync,unlinkSync=nodeFs.unlinkSync;exports.redactAiText=redact_1.redactSecretText;const intEnv=i((e,r,o,t)=>{const s=Number(process.env[e]);return Number.isFinite(s)&&s>=o&&s<=t?Math.floor(s):r},"intEnv"),maxFieldChars=i(()=>intEnv("LENSMCP_AI_MAX_CHARS",32*1024,256,4*1024*1024),"maxFieldChars"),maxFileBytes=i(()=>intEnv("LENSMCP_AI_MAX_BYTES",256*1024,4*1024,16*1024*1024),"maxFileBytes"),maxFiles=i(()=>intEnv("LENSMCP_AI_MAX_FILES",200,1,1e4),"maxFiles");function newAiCallId(){const e=Date.now().toString(36).padStart(9,"0"),r=Math.random().toString(36).slice(2,8).padEnd(6,"0");return`ai_${e}${r}`}l(newAiCallId,"newAiCallId"),i(newAiCallId,"newAiCallId");const PRE_CLIP_FACTOR=4,MAX_MESSAGES=400;function sanitize(e,r){const o={cut:e.truncated===!0},t=i((n,u=r)=>{if(n===void 0)return;const a=u*PRE_CLIP_FACTOR;let d=n;return d.length>a&&(o.cut=!0,d=d.slice(0,a)),(0,exports.redactAiText)(d)},"clean"),s={...e,request:{...e.request,...e.request.system!==void 0?{system:t(e.request.system)}:{},messages:e.request.messages.slice(0,MAX_MESSAGES*4).map(n=>({role:n.role,content:t(n.content)})),...e.request.tools?{tools:e.request.tools.map(n=>({name:t(n.name,200),...n.description?{description:t(n.description,512)}:{}}))}:{}},...e.response?{response:{...e.response,...e.response.completion!==void 0?{completion:t(e.response.completion)}:{},...e.response.reasoning!==void 0?{reasoning:t(e.response.reasoning)}:{},...e.response.error!==void 0?{error:t(e.response.error,2048)}:{},...e.response.toolCalls?{toolCalls:e.response.toolCalls.map(n=>({...n.id?{id:n.id}:{},name:t(n.name,200),...n.arguments!==void 0?{arguments:t(n.arguments,8192)}:{}}))}:{}}}:{}};return e.request.messages.length>MAX_MESSAGES*4&&(o.cut=!0),o.cut&&(s.truncated=!0),s}l(sanitize,"sanitize"),i(sanitize,"sanitize");function clipTo(e,r,o){const t={cut:e.truncated===!0},s=i((c,p=r)=>{if(c!==void 0)return c.length>p&&(t.cut=!0),(0,redact_1.clip)(c,p)},"c"),n=e.request.messages;let u=n;if(n.length>o){const c=Math.min(2,o);u=[...n.slice(0,c),...n.slice(n.length-(o-c))],t.cut=!0}const a=e.response,d={...e,request:{...e.request,...e.request.system!==void 0?{system:s(e.request.system)}:{},messages:u.map(c=>({role:c.role,content:s(c.content)}))},...a?{response:{...a,...a.completion!==void 0?{completion:s(a.completion)}:{},...a.reasoning!==void 0?{reasoning:s(a.reasoning)}:{},...a.toolCalls?{toolCalls:a.toolCalls.map(c=>({...c,...c.arguments!==void 0?{arguments:s(c.arguments,Math.min(r,8192))}:{}}))}:{}}}:{}};return t.cut?d.truncated=!0:delete d.truncated,d}l(clipTo,"clipTo"),i(clipTo,"clipTo");function fitAiContent(e){const r=maxFileBytes();let o=maxFieldChars(),t=MAX_MESSAGES;const s=sanitize(e,o);let n=clipTo(s,o,t),u=JSON.stringify(n);for(;u.length>r&&(o>256||t>2);)o>256?o=Math.max(256,Math.floor(o/2)):t=Math.max(2,Math.floor(t/2)),n=clipTo(s,o,t),u=JSON.stringify(n);return{json:u,content:n}}l(fitAiContent,"fitAiContent"),i(fitAiContent,"fitAiContent");let writes=0;function prune(e){try{const r=readdirSync(e).filter(t=>/^ai_[0-9a-z]+\.json$/.test(t)).sort(),o=r.length-maxFiles();for(let t=0;t<o;t+=1)try{unlinkSync(`${e}/${r[t]}`)}catch{}}catch{}}l(prune,"prune"),i(prune,"prune");function removeAiContent(e){try{unlinkSync((0,ai_content_1.aiContentFile)(emit_1.EVENT_FILE,e))}catch{}}l(removeAiContent,"removeAiContent"),i(removeAiContent,"removeAiContent");function writeAiContent(e){try{const r=(0,ai_content_1.aiContentDir)(emit_1.EVENT_FILE);mkdirSync(r,{recursive:!0,mode:448});const{json:o,content:t}=fitAiContent(e),s=`${r}/${e.id}.json`;return writeFileSync(`${s}.tmp`,o,{mode:384}),renameSync(`${s}.tmp`,s),writes+=1,(writes===1||writes%20===0)&&prune(r),{id:e.id,truncated:t.truncated===!0}}catch{return}}l(writeAiContent,"writeAiContent"),i(writeAiContent,"writeAiContent");
1
+ "use strict";var g=Object.defineProperty;var d=(e,s)=>g(e,"name",{value:s,configurable:!0});var m=Object.defineProperty,c=d((e,s)=>m(e,"name",{value:s,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactAiText=void 0,exports.newAiCallId=newAiCallId,exports.fitAiContent=fitAiContent,exports.writeAiContent=writeAiContent;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),ai_content_1=require("@lensmcp/core/ai-content"),redact_1=require("@lensmcp/core/redact"),emit_1=require("./emit"),mkdirSync=nodeFs.mkdirSync,writeFileSync=nodeFs.writeFileSync,renameSync=nodeFs.renameSync;exports.redactAiText=redact_1.redactSecretText;const intEnv=c((e,s,o,n)=>{const r=Number(process.env[e]);return Number.isFinite(r)&&r>=o&&r<=n?Math.floor(r):s},"intEnv"),maxFieldChars=c(()=>intEnv("LENSMCP_AI_MAX_CHARS",32*1024,256,4*1024*1024),"maxFieldChars"),maxFileBytes=c(()=>intEnv("LENSMCP_AI_MAX_BYTES",256*1024,4*1024,16*1024*1024),"maxFileBytes");function newAiCallId(){const e=Date.now().toString(36).padStart(9,"0"),s=Math.random().toString(36).slice(2,8).padEnd(6,"0");return`ai_${e}${s}`}d(newAiCallId,"newAiCallId"),c(newAiCallId,"newAiCallId");const PRE_CLIP_FACTOR=4,MAX_MESSAGES=400;function sanitize(e,s){const o={cut:e.truncated===!0},n=c((t,l=s)=>{if(t===void 0)return;const a=l*PRE_CLIP_FACTOR;let u=t;return u.length>a&&(o.cut=!0,u=u.slice(0,a)),(0,exports.redactAiText)(u)},"clean"),r={...e,request:{...e.request,...e.request.system!==void 0?{system:n(e.request.system)}:{},messages:e.request.messages.slice(0,MAX_MESSAGES*4).map(t=>({role:t.role,content:n(t.content)})),...e.request.tools?{tools:e.request.tools.map(t=>({name:n(t.name,200),...t.description?{description:n(t.description,512)}:{}}))}:{}},...e.response?{response:{...e.response,...e.response.completion!==void 0?{completion:n(e.response.completion)}:{},...e.response.reasoning!==void 0?{reasoning:n(e.response.reasoning)}:{},...e.response.error!==void 0?{error:n(e.response.error,2048)}:{},...e.response.toolCalls?{toolCalls:e.response.toolCalls.map(t=>({...t.id?{id:t.id}:{},name:n(t.name,200),...t.arguments!==void 0?{arguments:n(t.arguments,8192)}:{}}))}:{}}}:{}};return e.request.messages.length>MAX_MESSAGES*4&&(o.cut=!0),o.cut&&(r.truncated=!0),r}d(sanitize,"sanitize"),c(sanitize,"sanitize");function clipTo(e,s,o){const n={cut:e.truncated===!0},r=c((i,p=s)=>{if(i!==void 0)return i.length>p&&(n.cut=!0),(0,redact_1.clip)(i,p)},"c"),t=e.request.messages;let l=t;if(t.length>o){const i=Math.min(2,o);l=[...t.slice(0,i),...t.slice(t.length-(o-i))],n.cut=!0}const a=e.response,u={...e,request:{...e.request,...e.request.system!==void 0?{system:r(e.request.system)}:{},messages:l.map(i=>({role:i.role,content:r(i.content)}))},...a?{response:{...a,...a.completion!==void 0?{completion:r(a.completion)}:{},...a.reasoning!==void 0?{reasoning:r(a.reasoning)}:{},...a.toolCalls?{toolCalls:a.toolCalls.map(i=>({...i,...i.arguments!==void 0?{arguments:r(i.arguments,Math.min(s,8192))}:{}}))}:{}}}:{}};return n.cut?u.truncated=!0:delete u.truncated,u}d(clipTo,"clipTo"),c(clipTo,"clipTo");function fitAiContent(e){const s=maxFileBytes();let o=maxFieldChars(),n=MAX_MESSAGES;const r=sanitize(e,o);let t=clipTo(r,o,n),l=JSON.stringify(t);for(;l.length>s&&(o>256||n>2);)o>256?o=Math.max(256,Math.floor(o/2)):n=Math.max(2,Math.floor(n/2)),t=clipTo(r,o,n),l=JSON.stringify(t);return{json:l,content:t}}d(fitAiContent,"fitAiContent"),c(fitAiContent,"fitAiContent");function writeAiContent(e){try{const s=(0,ai_content_1.aiContentDir)(emit_1.EVENT_FILE);mkdirSync(s,{recursive:!0,mode:448});const{json:o,content:n}=fitAiContent(e),r=`${s}/${e.id}.json`;return writeFileSync(`${r}.tmp`,o,{mode:384}),renameSync(`${r}.tmp`,r),{id:e.id,truncated:n.truncated===!0}}catch{return}}d(writeAiContent,"writeAiContent"),c(writeAiContent,"writeAiContent");
package/lib/emit.d.ts CHANGED
@@ -46,9 +46,8 @@ export interface FlowCtx {
46
46
  originType?: string;
47
47
  }
48
48
  /**
49
- * Returns whether the event was actually written. Almost every caller ignores it; a producer that
50
- * wrote something ELSE to go with the event (the AI tap's sidecar file) needs to know when the flood
51
- * guard dropped the event, or it leaves that file behind with nothing pointing at it.
49
+ * Returns whether the event was actually written. Captured AI records are retained independently,
50
+ * including when their metadata event is suppressed or the event bus is unavailable.
52
51
  */
53
52
  export declare function lensEmit(source: string, severity: LensSeverity, title: string, raw: Record<string, unknown>, fingerprint?: string, flowCtx?: FlowCtx,
54
53
  /** Event category. Defaults to `cluster` — what every tap emitted before this existed. */
package/lib/emit.js CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";var m=Object.defineProperty;var p=(r,s)=>m(r,"name",{value:s,configurable:!0});var a=Object.defineProperty,i=p((r,s)=>a(r,"name",{value:s,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.EVENT_FILE=exports.PROJECT=exports.EVENT_MAX_FILES=exports.EVENT_MAX_BYTES=void 0,exports.maybeRotateEventFile=maybeRotateEventFile,exports.isLensWriting=isLensWriting,exports.lensEmit=lensEmit,exports.lensEmitOutcome=lensEmitOutcome,exports.lensLog=lensLog;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),node_path_1=require("node:path"),bus_rotation_1=require("@lensmcp/core/bus-rotation"),flood_guard_1=require("./flood-guard"),flow_context_1=require("./flow-context"),appendFileSync=nodeFs.appendFileSync,mkdirSync=nodeFs.mkdirSync,statSync=nodeFs.statSync,renameSync=nodeFs.renameSync,unlinkSync=nodeFs.unlinkSync,openSync=nodeFs.openSync,closeSync=nodeFs.closeSync,BUS_CAPS=(0,bus_rotation_1.busCapsFromEnv)();exports.EVENT_MAX_BYTES=BUS_CAPS.maxBytes,exports.EVENT_MAX_FILES=BUS_CAPS.maxFiles;const rotators=new Map,BUS_FS={statSync,renameSync,unlinkSync,openSync,closeSync};function maybeRotateEventFile(r,s,e){if(e!==void 0){const n=e.maxBytes??exports.EVENT_MAX_BYTES,t=e.maxFiles??exports.EVENT_MAX_FILES;(0,bus_rotation_1.createBusRotator)(r,{maxBytes:n,maxFiles:t,fs:BUS_FS,checkEveryBytes:0})(s);return}let o=rotators.get(r);o||(o=(0,bus_rotation_1.createBusRotator)(r,{maxBytes:exports.EVENT_MAX_BYTES,maxFiles:exports.EVENT_MAX_FILES,fs:BUS_FS}),rotators.set(r,o)),o(s)}p(maybeRotateEventFile,"maybeRotateEventFile"),i(maybeRotateEventFile,"maybeRotateEventFile"),exports.PROJECT=process.env.LENSMCP_PROJECT||process.env.SERVICE_NAME||"app",exports.EVENT_FILE=process.env.LENSMCP_EVENT_FILE||`${process.cwd()}/.lensmcp/events.jsonl`;let writing=!1,dirReady=!1;const emitGuard=(0,flood_guard_1.createEventFloodGuard)({burst:30,perSec:5}),logGuard=(0,flood_guard_1.createEventFloodGuard)({burst:120,perSec:20});function isLensWriting(){return writing}p(isLensWriting,"isLensWriting"),i(isLensWriting,"isLensWriting");function lensEmit(r,s,e,o,n,t,c="cluster"){return lensEmitOutcome(r,s,e,o,n,t,c)==="written"}p(lensEmit,"lensEmit"),i(lensEmit,"lensEmit");function lensEmitOutcome(r,s,e,o,n,t,c="cluster",l=emitGuard){try{writing=!0;const E=n??`${r}:${String(o.name??r)}`,d=l.admit(E);if(!d.ok)return"suppressed";dirReady||(mkdirSync((0,node_path_1.dirname)(exports.EVENT_FILE),{recursive:!0}),dirReady=!0);const u=JSON.stringify({id:Date.now().toString(36)+Math.random().toString(36).slice(2,10),sessionId:"pending",timestamp:Date.now(),source:r,category:c,severity:s,context:{sessionId:"pending",projectName:exports.PROJECT,...t?.flowId?{flowId:t.flowId}:{},...t?.requestId?{requestId:t.requestId}:{},...t?.runId?{runId:t.runId}:{},...t?.originNodeId?{originNodeId:t.originNodeId}:{},...t?.originType?{originType:t.originType}:{}},fingerprint:E,title:e,raw:{project:exports.PROJECT,...o,...d.suppressed>0?{floodSuppressed:d.suppressed}:{}}})+`
2
- `;return maybeRotateEventFile(exports.EVENT_FILE,u.length),appendFileSync(exports.EVENT_FILE,u),"written"}catch{return"failed"}finally{writing=!1}}p(lensEmitOutcome,"lensEmitOutcome"),i(lensEmitOutcome,"lensEmitOutcome");function lensLog(r,s,e){try{writing=!0;const o=e?.bucket!==void 0?`log:${exports.PROJECT}:${e.bucket}`:r==="error"||r==="fatal"?`log:error:${exports.PROJECT}`:`log:${exports.PROJECT}`,n=e?.bucket!==void 0?{ok:!0,suppressed:e.suppressed??0}:logGuard.admit(o);if(!n.ok)return!1;dirReady||(mkdirSync((0,node_path_1.dirname)(exports.EVENT_FILE),{recursive:!0}),dirReady=!0);const t=(0,flow_context_1.flowContext)(),c=JSON.stringify({id:Date.now().toString(36)+Math.random().toString(36).slice(2,10),sessionId:"pending",timestamp:Date.now(),source:"nestjs",category:"runtime",severity:r,context:{sessionId:"pending",projectName:exports.PROJECT,...t?.flowId?{flowId:t.flowId}:{},...t?.requestId?{requestId:t.requestId}:{},...t?.runId?{runId:t.runId}:{}},fingerprint:o,title:s,raw:{project:exports.PROJECT,kind:"log",...e?.stack?{request:{error:{message:s,stack:e.stack}}}:{},...e?.text?{text:e.text}:{},...e?.continues?{continues:!0}:{},...e?.stream?{stream:e.stream}:{},...e?.level?{level:e.level}:{},...e?.logger?{logger:e.logger}:{},...n.suppressed>0?{floodSuppressed:n.suppressed}:{}}})+`
3
- `;return maybeRotateEventFile(exports.EVENT_FILE,c.length),appendFileSync(exports.EVENT_FILE,c),!0}catch{return!1}finally{writing=!1}}p(lensLog,"lensLog"),i(lensLog,"lensLog");
1
+ "use strict";var m=Object.defineProperty;var i=(r,s)=>m(r,"name",{value:s,configurable:!0});var a=Object.defineProperty,c=i((r,s)=>a(r,"name",{value:s,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.EVENT_FILE=exports.PROJECT=exports.EVENT_MAX_FILES=exports.EVENT_MAX_BYTES=void 0,exports.maybeRotateEventFile=maybeRotateEventFile,exports.isLensWriting=isLensWriting,exports.lensEmit=lensEmit,exports.lensEmitOutcome=lensEmitOutcome,exports.lensLog=lensLog;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),node_path_1=require("node:path"),bus_rotation_1=require("@lensmcp/core/bus-rotation"),flood_guard_1=require("./flood-guard"),flow_context_1=require("./flow-context"),loop_identity_1=require("./loop-identity"),appendFileSync=nodeFs.appendFileSync,mkdirSync=nodeFs.mkdirSync,statSync=nodeFs.statSync,renameSync=nodeFs.renameSync,unlinkSync=nodeFs.unlinkSync,openSync=nodeFs.openSync,closeSync=nodeFs.closeSync,BUS_CAPS=(0,bus_rotation_1.busCapsFromEnv)();exports.EVENT_MAX_BYTES=BUS_CAPS.maxBytes,exports.EVENT_MAX_FILES=BUS_CAPS.maxFiles;const rotators=new Map,BUS_FS={statSync,renameSync,unlinkSync,openSync,closeSync};function maybeRotateEventFile(r,s,e){if(e!==void 0){const n=e.maxBytes??exports.EVENT_MAX_BYTES,t=e.maxFiles??exports.EVENT_MAX_FILES;(0,bus_rotation_1.createBusRotator)(r,{maxBytes:n,maxFiles:t,fs:BUS_FS,checkEveryBytes:0})(s);return}let o=rotators.get(r);o||(o=(0,bus_rotation_1.createBusRotator)(r,{maxBytes:exports.EVENT_MAX_BYTES,maxFiles:exports.EVENT_MAX_FILES,fs:BUS_FS}),rotators.set(r,o)),o(s)}i(maybeRotateEventFile,"maybeRotateEventFile"),c(maybeRotateEventFile,"maybeRotateEventFile"),exports.PROJECT=process.env.LENSMCP_PROJECT||process.env.SERVICE_NAME||"app",exports.EVENT_FILE=process.env.LENSMCP_EVENT_FILE||`${process.cwd()}/.lensmcp/events.jsonl`;let writing=!1,dirReady=!1;const emitGuard=(0,flood_guard_1.createEventFloodGuard)({burst:30,perSec:5}),logGuard=(0,flood_guard_1.createEventFloodGuard)({burst:120,perSec:20});function isLensWriting(){return writing}i(isLensWriting,"isLensWriting"),c(isLensWriting,"isLensWriting");function lensEmit(r,s,e,o,n,t,p="cluster"){return lensEmitOutcome(r,s,e,o,n,t,p)==="written"}i(lensEmit,"lensEmit"),c(lensEmit,"lensEmit");function lensEmitOutcome(r,s,e,o,n,t,p="cluster",u=emitGuard){try{writing=!0;const E=n??`${r}:${String(o.name??r)}`,d=u.admit(E);if(!d.ok)return"suppressed";dirReady||(mkdirSync((0,node_path_1.dirname)(exports.EVENT_FILE),{recursive:!0}),dirReady=!0);const l=JSON.stringify({id:Date.now().toString(36)+Math.random().toString(36).slice(2,10),sessionId:"pending",timestamp:Date.now(),source:r,category:p,severity:s,context:{sessionId:"pending",projectName:exports.PROJECT,...t?.flowId?{flowId:t.flowId}:{},...t?.requestId?{requestId:t.requestId}:{},...t?.runId?{runId:t.runId}:{},...t?.originNodeId?{originNodeId:t.originNodeId}:{},...t?.originType?{originType:t.originType}:{}},fingerprint:E,title:e,raw:{project:exports.PROJECT,...o,loopId:loop_identity_1.NODE_LOOP.id,...d.suppressed>0?{floodSuppressed:d.suppressed}:{}}})+`
2
+ `;return maybeRotateEventFile(exports.EVENT_FILE,l.length),appendFileSync(exports.EVENT_FILE,l),"written"}catch{return"failed"}finally{writing=!1}}i(lensEmitOutcome,"lensEmitOutcome"),c(lensEmitOutcome,"lensEmitOutcome");function lensLog(r,s,e){try{writing=!0;const o=e?.bucket!==void 0?`log:${exports.PROJECT}:${e.bucket}`:r==="error"||r==="fatal"?`log:error:${exports.PROJECT}`:`log:${exports.PROJECT}`,n=e?.bucket!==void 0?{ok:!0,suppressed:e.suppressed??0}:logGuard.admit(o);if(!n.ok)return!1;dirReady||(mkdirSync((0,node_path_1.dirname)(exports.EVENT_FILE),{recursive:!0}),dirReady=!0);const t=(0,flow_context_1.flowContext)(),p=JSON.stringify({id:Date.now().toString(36)+Math.random().toString(36).slice(2,10),sessionId:"pending",timestamp:Date.now(),source:"nestjs",category:"runtime",severity:r,context:{sessionId:"pending",projectName:exports.PROJECT,...t?.flowId?{flowId:t.flowId}:{},...t?.requestId?{requestId:t.requestId}:{},...t?.runId?{runId:t.runId}:{}},fingerprint:o,title:s,raw:{project:exports.PROJECT,kind:"log",...e?.stack?{request:{error:{message:s,stack:e.stack}}}:{},...e?.text?{text:e.text}:{},...e?.continues?{continues:!0}:{},...e?.stream?{stream:e.stream}:{},...e?.level?{level:e.level}:{},...e?.logger?{logger:e.logger}:{},...n.suppressed>0?{floodSuppressed:n.suppressed}:{}}})+`
3
+ `;return maybeRotateEventFile(exports.EVENT_FILE,p.length),appendFileSync(exports.EVENT_FILE,p),!0}catch{return!1}finally{writing=!1}}i(lensLog,"lensLog"),c(lensLog,"lensLog");
@@ -30,7 +30,7 @@ export declare const DEFAULT_HTTP_MAX_FILES = 10000;
30
30
  export declare const DEFAULT_HTTP_MAX_AGE_MS: number;
31
31
  /** When an exchange was written — its id starts with the time (`newExchangeId`), so no file needs a stat. */
32
32
  export declare const exchangeTime: (file: string) => number | undefined;
33
- /** Oldest first, by name (= by time): beyond the count, and older than the age. */
33
+ /** Oldest first, by name (= by time). AI exchanges are retained even beyond the count/age budgets. */
34
34
  export declare function pruneHttpExchanges(dir: string, now?: number): number;
35
35
  /** Persist one exchange (temp name, then rename). Best-effort: false on any failure — the call goes on regardless. */
36
36
  export declare function writeHttpExchange(x: HttpExchange): boolean;
package/lib/http-store.js CHANGED
@@ -1 +1 @@
1
- "use strict";var d=Object.defineProperty;var u=(e,t)=>d(e,"name",{value:t,configurable:!0});var f=Object.defineProperty,i=u((e,t)=>f(e,"name",{value:t,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.exchangeTime=exports.DEFAULT_HTTP_MAX_AGE_MS=exports.DEFAULT_HTTP_MAX_FILES=exports.newExchangeId=exports.MAX_KEPT_BYTES=exports.REMOVED=void 0,exports.httpCaptureMode=httpCaptureMode,exports.redactHeaders=redactHeaders,exports.redactQuery=redactQuery,exports.keepBody=keepBody,exports.pruneHttpExchanges=pruneHttpExchanges,exports.writeHttpExchange=writeHttpExchange;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),node_crypto_1=require("node:crypto"),http_exchange_1=require("@lensmcp/core/http-exchange"),redact_1=require("@lensmcp/core/redact"),ai_store_1=require("./ai-store"),emit_1=require("./emit"),mkdirSync=nodeFs.mkdirSync,writeFileSync=nodeFs.writeFileSync,renameSync=nodeFs.renameSync,readdirSync=nodeFs.readdirSync,unlinkSync=nodeFs.unlinkSync;function httpCaptureMode(e=process.env){const t=(e.LENSMCP_HTTP_CAPTURE??"").trim().toLowerCase();return t==="off"||t==="0"||t==="false"||t==="none"?"off":t==="headers"?"headers":"bodies"}u(httpCaptureMode,"httpCaptureMode"),i(httpCaptureMode,"httpCaptureMode"),exports.REMOVED="\u2022\u2022\u2022\u2022 redacted",exports.MAX_KEPT_BYTES=64*1024;const MAX_PARSE_BYTES=2*1024*1024,newExchangeId=i(()=>`hx_${Date.now().toString(36)}${(0,node_crypto_1.randomBytes)(5).toString("hex")}`,"newExchangeId");exports.newExchangeId=newExchangeId;const CREDENTIAL_HEADERS=/^(authorization|proxy-authorization|cookie|set-cookie|x-api-key|api-key|openai-organization|openai-project|x-internal-token|x-amz-security-token)$|-api-key$|-token$|-secret$/i,CREDENTIAL_FIELD=/pass(word|wd)?|secret|token|api[-_]?key|authorization|credential|cookie|signature/i;function jwtClaims(e){const t=e.split(".");if(t.length===3)try{const r=JSON.parse(Buffer.from(t[1].replace(/-/g,"+").replace(/_/g,"/"),"base64").toString("utf8"));return`JWT \xB7 ${["iss","aud","sub","exp"].filter(n=>r[n]!==void 0).map(n=>`${n}=${n==="exp"&&typeof r[n]=="number"?new Date(r[n]*1e3).toISOString():String(r[n]).slice(0,80)}`).join(" \xB7 ")||"no claims"} \xB7 ${exports.REMOVED.split(" ")[0]} signature`}catch{return}}u(jwtClaims,"jwtClaims"),i(jwtClaims,"jwtClaims");function redactHeaders(e){const t=[],r=[];for(const[n,a]of e){const o=n.toLowerCase();if(CREDENTIAL_HEADERS.test(o)||(0,redact_1.isSecretParam)(o)){r.push(o);const s=/^bearer\s+(\S+)$/i.exec(a.trim()),c=s?jwtClaims(s[1]):void 0,p=/^(\w+)\s+\S/.exec(a.trim())?.[1];t.push([o,c?`Bearer ${c}`:p&&o.endsWith("authorization")?`${p} ${exports.REMOVED}`:exports.REMOVED])}else t.push([o,(0,ai_store_1.redactAiText)(a).slice(0,2048)])}return{headers:t,redacted:r}}u(redactHeaders,"redactHeaders"),i(redactHeaders,"redactHeaders");function redactQuery(e){const t=[];try{for(const[r,n]of new URLSearchParams(e))t.push([r,(0,redact_1.isSecretParam)(r)||CREDENTIAL_FIELD.test(r)?exports.REMOVED:(0,ai_store_1.redactAiText)(n).slice(0,1024)])}catch{}return t.slice(0,100)}u(redactQuery,"redactQuery"),i(redactQuery,"redactQuery");function redactJson(e,t,r,n=0){if(n>32||e===null||typeof e!="object")return typeof e=="string"?(0,ai_store_1.redactAiText)(e):e;if(Array.isArray(e))return e.map((o,s)=>redactJson(o,`${t}[${s}]`,r,n+1));const a={};for(const[o,s]of Object.entries(e)){const c=t?`${t}.${o}`:o,p=typeof s=="number"&&/tokens/i.test(o);CREDENTIAL_FIELD.test(o)&&s!==null&&typeof s!="object"&&!p?(a[o]=exports.REMOVED,r.length<50&&r.push(c)):a[o]=redactJson(s,c,r,n+1)}return a}u(redactJson,"redactJson"),i(redactJson,"redactJson");const textual=i(e=>/json|^text\/|x-www-form-urlencoded|xml|javascript|graphql|event-stream/i.test(e)||e==="","textual");function keepBody(e,t,r,n,a){const o=e.split(";")[0].trim().toLowerCase();if(!textual(o))return{contentType:o||"application/octet-stream",size:r,kept:0,truncated:!1,omitted:"binary",...a?{sha256:a}:{}};const s=t.byteLength>=r&&r<=MAX_PARSE_BYTES,c=Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8");let p;if(s&&/json/.test(o))try{p=JSON.stringify(redactJson(JSON.parse(c),"",n))}catch{p=(0,ai_store_1.redactAiText)(c)}else s&&/x-www-form-urlencoded/.test(o)?p=new URLSearchParams(redactQuery(c)).toString():p=(0,ai_store_1.redactAiText)(c);const x=Buffer.byteLength(p)>exports.MAX_KEPT_BYTES?Buffer.from(p).subarray(0,exports.MAX_KEPT_BYTES).toString("utf8"):p;return{contentType:o,size:r,kept:Buffer.byteLength(x),text:x,truncated:r>t.byteLength||x.length<p.length}}u(keepBody,"keepBody"),i(keepBody,"keepBody"),exports.DEFAULT_HTTP_MAX_FILES=1e4,exports.DEFAULT_HTTP_MAX_AGE_MS=1440*6e4;const PRUNE_EVERY_MS=3e4,maxFiles=i(()=>{const e=Number(process.env.LENSMCP_HTTP_MAX_FILES);return Number.isInteger(e)&&e>=10?e:exports.DEFAULT_HTTP_MAX_FILES},"maxFiles"),maxAgeMs=i(()=>{const e=Number(process.env.LENSMCP_HTTP_MAX_AGE_MS);return Number.isFinite(e)&&e>=6e4?e:exports.DEFAULT_HTTP_MAX_AGE_MS},"maxAgeMs"),exchangeTime=i(e=>{const t=/^hx_([0-9a-z]{8})/.exec(e),r=t?parseInt(t[1],36):NaN;return Number.isFinite(r)?r:void 0},"exchangeTime");exports.exchangeTime=exchangeTime;const lastPrune=new Map;function pruneHttpExchanges(e,t=Date.now()){let r=0;try{const n=readdirSync(e).filter(s=>/^hx_[0-9a-z]+\.json$/.test(s)).sort(),a=n.length-maxFiles(),o=t-maxAgeMs();for(let s=0;s<n.length;s+=1){const c=(0,exports.exchangeTime)(n[s]);if(s>=a&&(c===void 0||c>=o))break;try{unlinkSync(`${e}/${n[s]}`),r+=1}catch{}}}catch{}return r}u(pruneHttpExchanges,"pruneHttpExchanges"),i(pruneHttpExchanges,"pruneHttpExchanges");function writeHttpExchange(e){try{const t=(0,http_exchange_1.exchangePlace)(e),r=(0,http_exchange_1.httpExchangeDir)(emit_1.EVENT_FILE,t);mkdirSync(r,{recursive:!0,mode:448});const n=(0,http_exchange_1.httpExchangeFile)(emit_1.EVENT_FILE,e.id,t);writeFileSync(`${n}.tmp`,JSON.stringify(e),{mode:384}),renameSync(`${n}.tmp`,n);const a=Date.now();return a-(lastPrune.get(t)??0)>=PRUNE_EVERY_MS&&(lastPrune.set(t,a),pruneHttpExchanges(r,a)),!0}catch{return!1}}u(writeHttpExchange,"writeHttpExchange"),i(writeHttpExchange,"writeHttpExchange");
1
+ "use strict";var d=Object.defineProperty;var u=(e,t)=>d(e,"name",{value:t,configurable:!0});var f=Object.defineProperty,a=u((e,t)=>f(e,"name",{value:t,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.exchangeTime=exports.DEFAULT_HTTP_MAX_AGE_MS=exports.DEFAULT_HTTP_MAX_FILES=exports.newExchangeId=exports.MAX_KEPT_BYTES=exports.REMOVED=void 0,exports.httpCaptureMode=httpCaptureMode,exports.redactHeaders=redactHeaders,exports.redactQuery=redactQuery,exports.keepBody=keepBody,exports.pruneHttpExchanges=pruneHttpExchanges,exports.writeHttpExchange=writeHttpExchange;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),node_crypto_1=require("node:crypto"),http_exchange_1=require("@lensmcp/core/http-exchange"),redact_1=require("@lensmcp/core/redact"),ai_store_1=require("./ai-store"),emit_1=require("./emit"),mkdirSync=nodeFs.mkdirSync,writeFileSync=nodeFs.writeFileSync,renameSync=nodeFs.renameSync,readdirSync=nodeFs.readdirSync,readFileSync=nodeFs.readFileSync,unlinkSync=nodeFs.unlinkSync;function httpCaptureMode(e=process.env){const t=(e.LENSMCP_HTTP_CAPTURE??"").trim().toLowerCase();return t==="off"||t==="0"||t==="false"||t==="none"?"off":t==="headers"?"headers":"bodies"}u(httpCaptureMode,"httpCaptureMode"),a(httpCaptureMode,"httpCaptureMode"),exports.REMOVED="\u2022\u2022\u2022\u2022 redacted",exports.MAX_KEPT_BYTES=64*1024;const MAX_PARSE_BYTES=2*1024*1024,newExchangeId=a(()=>`hx_${Date.now().toString(36)}${(0,node_crypto_1.randomBytes)(5).toString("hex")}`,"newExchangeId");exports.newExchangeId=newExchangeId;const CREDENTIAL_HEADERS=/^(authorization|proxy-authorization|cookie|set-cookie|x-api-key|api-key|openai-organization|openai-project|x-internal-token|x-amz-security-token)$|-api-key$|-token$|-secret$/i,CREDENTIAL_FIELD=/pass(word|wd)?|secret|token|api[-_]?key|authorization|credential|cookie|signature/i;function jwtClaims(e){const t=e.split(".");if(t.length===3)try{const r=JSON.parse(Buffer.from(t[1].replace(/-/g,"+").replace(/_/g,"/"),"base64").toString("utf8"));return`JWT \xB7 ${["iss","aud","sub","exp"].filter(n=>r[n]!==void 0).map(n=>`${n}=${n==="exp"&&typeof r[n]=="number"?new Date(r[n]*1e3).toISOString():String(r[n]).slice(0,80)}`).join(" \xB7 ")||"no claims"} \xB7 ${exports.REMOVED.split(" ")[0]} signature`}catch{return}}u(jwtClaims,"jwtClaims"),a(jwtClaims,"jwtClaims");function redactHeaders(e){const t=[],r=[];for(const[n,i]of e){const o=n.toLowerCase();if(CREDENTIAL_HEADERS.test(o)||(0,redact_1.isSecretParam)(o)){r.push(o);const s=/^bearer\s+(\S+)$/i.exec(i.trim()),c=s?jwtClaims(s[1]):void 0,p=/^(\w+)\s+\S/.exec(i.trim())?.[1];t.push([o,c?`Bearer ${c}`:p&&o.endsWith("authorization")?`${p} ${exports.REMOVED}`:exports.REMOVED])}else t.push([o,(0,ai_store_1.redactAiText)(i).slice(0,2048)])}return{headers:t,redacted:r}}u(redactHeaders,"redactHeaders"),a(redactHeaders,"redactHeaders");function redactQuery(e){const t=[];try{for(const[r,n]of new URLSearchParams(e))t.push([r,(0,redact_1.isSecretParam)(r)||CREDENTIAL_FIELD.test(r)?exports.REMOVED:(0,ai_store_1.redactAiText)(n).slice(0,1024)])}catch{}return t.slice(0,100)}u(redactQuery,"redactQuery"),a(redactQuery,"redactQuery");function redactJson(e,t,r,n=0){if(n>32||e===null||typeof e!="object")return typeof e=="string"?(0,ai_store_1.redactAiText)(e):e;if(Array.isArray(e))return e.map((o,s)=>redactJson(o,`${t}[${s}]`,r,n+1));const i={};for(const[o,s]of Object.entries(e)){const c=t?`${t}.${o}`:o,p=typeof s=="number"&&/tokens/i.test(o);CREDENTIAL_FIELD.test(o)&&s!==null&&typeof s!="object"&&!p?(i[o]=exports.REMOVED,r.length<50&&r.push(c)):i[o]=redactJson(s,c,r,n+1)}return i}u(redactJson,"redactJson"),a(redactJson,"redactJson");const textual=a(e=>/json|^text\/|x-www-form-urlencoded|xml|javascript|graphql|event-stream/i.test(e)||e==="","textual");function keepBody(e,t,r,n,i){const o=e.split(";")[0].trim().toLowerCase();if(!textual(o))return{contentType:o||"application/octet-stream",size:r,kept:0,truncated:!1,omitted:"binary",...i?{sha256:i}:{}};const s=t.byteLength>=r&&r<=MAX_PARSE_BYTES,c=Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8");let p;if(s&&/json/.test(o))try{p=JSON.stringify(redactJson(JSON.parse(c),"",n))}catch{p=(0,ai_store_1.redactAiText)(c)}else s&&/x-www-form-urlencoded/.test(o)?p=new URLSearchParams(redactQuery(c)).toString():p=(0,ai_store_1.redactAiText)(c);const x=Buffer.byteLength(p)>exports.MAX_KEPT_BYTES?Buffer.from(p).subarray(0,exports.MAX_KEPT_BYTES).toString("utf8"):p;return{contentType:o,size:r,kept:Buffer.byteLength(x),text:x,truncated:r>t.byteLength||x.length<p.length}}u(keepBody,"keepBody"),a(keepBody,"keepBody"),exports.DEFAULT_HTTP_MAX_FILES=1e4,exports.DEFAULT_HTTP_MAX_AGE_MS=1440*6e4;const PRUNE_EVERY_MS=3e4,maxFiles=a(()=>{const e=Number(process.env.LENSMCP_HTTP_MAX_FILES);return Number.isInteger(e)&&e>=10?e:exports.DEFAULT_HTTP_MAX_FILES},"maxFiles"),maxAgeMs=a(()=>{const e=Number(process.env.LENSMCP_HTTP_MAX_AGE_MS);return Number.isFinite(e)&&e>=6e4?e:exports.DEFAULT_HTTP_MAX_AGE_MS},"maxAgeMs"),exchangeTime=a(e=>{const t=/^hx_([0-9a-z]{8})/.exec(e),r=t?parseInt(t[1],36):NaN;return Number.isFinite(r)?r:void 0},"exchangeTime");exports.exchangeTime=exchangeTime;const lastPrune=new Map;function pruneHttpExchanges(e,t=Date.now()){let r=0;try{const n=readdirSync(e).filter(s=>/^hx_[0-9a-z]+\.json$/.test(s)).sort(),i=n.length-maxFiles(),o=t-maxAgeMs();for(let s=0;s<n.length;s+=1){const c=(0,exports.exchangeTime)(n[s]);if(s>=i&&(c===void 0||c>=o))break;try{if(JSON.parse(readFileSync(`${e}/${n[s]}`,"utf8"))?.ai)continue;unlinkSync(`${e}/${n[s]}`),r+=1}catch{}}}catch{}return r}u(pruneHttpExchanges,"pruneHttpExchanges"),a(pruneHttpExchanges,"pruneHttpExchanges");function writeHttpExchange(e){try{const t=(0,http_exchange_1.exchangePlace)(e),r=(0,http_exchange_1.httpExchangeDir)(emit_1.EVENT_FILE,t);mkdirSync(r,{recursive:!0,mode:448});const n=(0,http_exchange_1.httpExchangeFile)(emit_1.EVENT_FILE,e.id,t);writeFileSync(`${n}.tmp`,JSON.stringify(e),{mode:384}),renameSync(`${n}.tmp`,n);const i=Date.now();return i-(lastPrune.get(t)??0)>=PRUNE_EVERY_MS&&(lastPrune.set(t,i),pruneHttpExchanges(r,i)),!0}catch{return!1}}u(writeHttpExchange,"writeHttpExchange"),a(writeHttpExchange,"writeHttpExchange");
@@ -0,0 +1,8 @@
1
+ /** Stable across module copies, distinct across restarts and worker threads. */
2
+ export declare const NODE_LOOP: {
3
+ id: string;
4
+ runtime: "node";
5
+ project: string;
6
+ pid: number;
7
+ threadId: number;
8
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.NODE_LOOP=void 0;const node_perf_hooks_1=require("node:perf_hooks"),node_worker_threads_1=require("node:worker_threads");exports.NODE_LOOP={id:`node:${process.pid}:${node_worker_threads_1.threadId}:${node_perf_hooks_1.performance.timeOrigin}`,runtime:"node",project:process.env.LENSMCP_PROJECT||process.env.SERVICE_NAME||"app",pid:process.pid,threadId:node_worker_threads_1.threadId};
@@ -0,0 +1 @@
1
+ export declare function startLoopWatchdog(onUnavailable?: () => void): (() => void) | undefined;
@@ -0,0 +1,38 @@
1
+ "use strict";var p=Object.defineProperty;var i=(t,e)=>p(t,"name",{value:e,configurable:!0});var d=Object.defineProperty,o=i((t,e)=>d(t,"name",{value:e,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.startLoopWatchdog=startLoopWatchdog;const node_worker_threads_1=require("node:worker_threads"),node_path_1=require("node:path"),loop_identity_1=require("./loop-identity"),emit_1=require("./emit"),WATCHER=`
2
+ const { workerData } = require('node:worker_threads');
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const cells = new BigInt64Array(workerData.shared);
6
+ const loop = workerData.loop;
7
+ let incident;
8
+ let lastWrite = 0;
9
+ let serial = 0;
10
+ function write(raw) {
11
+ const now = Date.now();
12
+ const row = { id: raw.incidentId + ':' + (++serial), timestamp: now, source: 'perf', category: 'performance',
13
+ severity: raw.state === 'unresponsive' ? 'error' : 'warning', title: 'Event loop ' + raw.state + ' (' + Math.round(raw.durationMs) + 'ms)',
14
+ context: { projectName: loop.project }, raw: { ...raw, loop, loopId: loop.id, kind: 'event-loop-stall', mechanism: 'heartbeat' } };
15
+ try {
16
+ fs.mkdirSync(path.dirname(workerData.file), { recursive: true, mode: 448 });
17
+ fs.writeFileSync(workerData.file + '.tmp', JSON.stringify(row), { mode: 384 });
18
+ fs.renameSync(workerData.file + '.tmp', workerData.file);
19
+ } catch {}
20
+ lastWrite = now;
21
+ }
22
+ setInterval(() => {
23
+ const now = Date.now();
24
+ const beat = Atomics.load(cells, 0);
25
+ if (!beat) return;
26
+ const delay = Number(process.hrtime.bigint() - beat) / 1e6;
27
+ if (delay >= workerData.threshold) {
28
+ if (!incident) incident = { incidentId: loop.id + ':watch:' + now, startedAt: now - delay, durationMs: delay, state: 'unresponsive' };
29
+ incident.durationMs = delay;
30
+ if (now - lastWrite >= 1000) write(incident);
31
+ } else if (incident) {
32
+ // The last heartbeat proves recovery; the exact end is bounded by the heartbeat resolution.
33
+ incident.durationMs = Math.max(incident.durationMs, Number(Atomics.load(cells, 1)) - incident.startedAt);
34
+ write({ ...incident, state: 'recovered' });
35
+ incident = undefined;
36
+ }
37
+ }, 250);
38
+ `;function startLoopWatchdog(t){if(process.env.LENSMCP_EVENT_LOOP_WATCHDOG==="0")return;const e=new SharedArrayBuffer(16),s=new BigInt64Array(e),c=o(()=>{Atomics.store(s,0,process.hrtime.bigint()),Atomics.store(s,1,BigInt(Date.now()))},"beat");c();const r=setInterval(c,20);r.unref();try{let a=!1;const n=new node_worker_threads_1.Worker(WATCHER,{eval:!0,execArgv:[],env:{...process.env,NODE_OPTIONS:"",LENSMCP_AUTO:"0",LENSMCP_EVENT_LOOP:"0"},workerData:{shared:e,loop:loop_identity_1.NODE_LOOP,threshold:1e3,file:(0,node_path_1.join)((0,node_path_1.dirname)(emit_1.EVENT_FILE),"event-loops",`${process.pid}-${loop_identity_1.NODE_LOOP.threadId}-${Math.floor(performance.timeOrigin)}.json`)}}),l=o(()=>{clearInterval(r),a||(a=!0,t?.())},"unavailable");return n.on("error",l),n.on("exit",l),n.unref(),()=>{a=!0,clearInterval(r),n.terminate()}}catch{clearInterval(r);return}}i(startLoopWatchdog,"startLoopWatchdog"),o(startLoopWatchdog,"startLoopWatchdog");
package/lib/tap-ai.js CHANGED
@@ -1 +1 @@
1
- "use strict";var R=Object.defineProperty;var c=(t,e)=>R(t,"name",{value:e,configurable:!0});var x=Object.defineProperty,d=c((t,e)=>x(t,"name",{value:e,configurable:!0}),"d");Object.defineProperty(exports,"__esModule",{value:!0}),exports.aiCaptureMode=aiCaptureMode,exports.flushAiTapForTests=flushAiTapForTests,exports.resetAiTapForTests=resetAiTapForTests,exports.tapAi=tapAi;const emit_1=require("./emit"),flow_context_1=require("./flow-context"),ai_providers_1=require("./ai-providers"),ai_store_1=require("./ai-store");function aiCaptureMode(t=process.env){const e=(t.LENSMCP_AI_CAPTURE??"").trim().toLowerCase();return e==="content"?"content":e==="metadata"||e==="1"||e==="true"||e==="on"?"metadata":"off"}c(aiCaptureMode,"aiCaptureMode"),d(aiCaptureMode,"aiCaptureMode");const MAX_REQUEST_PARSE_BYTES=2*1024*1024,MAX_RESPONSE_PARSE_BYTES=8*1024*1024,MAX_STREAM_BYTES=8*1024*1024,MAX_STREAM_MS=300*1e3,MAX_ENDPOINT_CHARS=256;function locate(t){try{const e=new URL(typeof t=="string"?t:t instanceof URL?t.href:t.url);return{host:e.hostname,port:e.port,pathname:e.pathname}}catch{return}}c(locate,"locate"),d(locate,"locate");const methodOf=d((t,e)=>{const o=typeof t=="object"&&t!==null&&!(t instanceof URL)?t.method:void 0;return String(e?.method??o??"GET").toUpperCase()},"methodOf"),decode=d(t=>({bytes:t.byteLength,...t.byteLength<=MAX_REQUEST_PARSE_BYTES?{text:Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8")}:{}}),"decode");function takeRequestBody(t,e){const o=e?.body;if(typeof o=="string")return{text:o,bytes:Buffer.byteLength(o)};if(o instanceof ArrayBuffer)return decode(new Uint8Array(o));if(ArrayBuffer.isView(o))return decode(new Uint8Array(o.buffer,o.byteOffset,o.byteLength));if(typeof Blob<"u"&&o instanceof Blob)return{bytes:o.size,...o.size<=MAX_REQUEST_PARSE_BYTES?{pending:o.text().catch(()=>{})}:{}};if(o==null&&typeof t=="object"&&t!==null&&!(t instanceof URL)){const s=t;if(!s.bodyUsed&&s.body)try{return{pending:s.clone().text().catch(()=>{})}}catch{return{}}}return{}}c(takeRequestBody,"takeRequestBody"),d(takeRequestBody,"takeRequestBody");const sum=d(t=>t.reduce((e,o)=>e+o,0),"sum");function parseJson(t){if(t)try{return JSON.parse(t)}catch{return}}c(parseJson,"parseJson"),d(parseJson,"parseJson");function headerOf(t,e){try{return t?.headers?.get?.(e)??""}catch{return""}}c(headerOf,"headerOf"),d(headerOf,"headerOf");const statusOf=d(t=>typeof t?.status=="number"?t.status:void 0,"statusOf");function failureKind(t,e){const o=t?.name,s=o==="TimeoutError";return{timedOut:s,aborted:!s&&(o==="AbortError"||e?.aborted===!0)}}c(failureKind,"failureKind"),d(failureKind,"failureKind");async function readStream(t,e,o){const s=(0,ai_providers_1.createStreamAccumulator)(e);let i,r=!1;const a=t.body;if(a){const f=a.getReader(),n=new TextDecoder;let p=0;try{for(;;){const{done:m,value:g}=await f.read();if(m)break;if(p+=g.byteLength,s.push(n.decode(g,{stream:!0})),i===void 0&&s.eventCount>0&&(i=Date.now()-o),p>MAX_STREAM_BYTES||Date.now()-o>MAX_STREAM_MS){r=!0,await f.cancel().catch(()=>{});break}}}catch{r=!0}}return{response:s.end(),sse:!0,cut:r,...i!==void 0?{firstEventMs:i}:{}}}c(readStream,"readStream"),d(readStream,"readStream");async function readBody(t){const e={completion:"",toolCalls:[],usage:{}};if(!t.res||!t.clone)return{response:e,sse:!1,cut:!1};try{if(headerOf(t.res,"content-type").includes("text/event-stream"))return await readStream(t.clone,t.target,t.startedAt);const o=Number(headerOf(t.res,"content-length"));if(Number.isFinite(o)&&o>MAX_RESPONSE_PARSE_BYTES)return{response:e,sse:!1,cut:!1};const s=await t.clone.text().catch(()=>{});if(s&&/^\s*(?:event|data):/.test(s)){const i=(0,ai_providers_1.createStreamAccumulator)(t.target);return i.push(s),{response:i.end(),sse:!0,cut:!1}}return{response:(0,ai_providers_1.normalizeResponse)(t.target,parseJson(s)),sse:!1,cut:!1}}catch{return{response:e,sse:!1,cut:!1}}}c(readBody,"readBody"),d(readBody,"readBody");async function capture(t){const e=readBody(t),o=t.request.text??await t.request.pending,s=t.request.bytes??(o?Buffer.byteLength(o):void 0),i=s!==void 0&&s>MAX_REQUEST_PARSE_BYTES,r=(0,ai_providers_1.normalizeRequest)(t.target,i?void 0:parseJson(o)),a=statusOf(t.res),f=await e;let n=f.response;const p=r.stream||f.sse,m=f.firstEventMs,g=f.cut,{aborted:y,timedOut:T}=t.failure!==void 0?failureKind(t.failure,t.signal):{aborted:!1,timedOut:!1};t.failure!==void 0&&(n={...n,error:String(t.failure?.message??t.failure)});const b=y||g,A=Date.now()-t.startedAt,u=n.model??r.model,_=sum(r.messages.map(w=>w.content.length))+(r.system?.length??0);let l,h=!1;if(t.mode==="content"&&t.target.api!=="embeddings"){const w={schemaVersion:1,id:(0,ai_store_1.newAiCallId)(),capturedAt:Date.now(),...t.ctx?.flowId?{flowId:t.ctx.flowId}:{},...t.ctx?.requestId?{requestId:t.ctx.requestId}:{},provider:t.target.provider,api:t.target.api,...u?{model:u}:{},host:t.host,endpoint:t.endpoint,stream:p,...a!==void 0?{status:a}:{},request:{...r.system?{system:r.system}:{},messages:r.messages,...r.tools.length?{tools:r.tools}:{},...Object.keys(r.params).length?{params:r.params}:{}},response:{completion:n.completion,...n.reasoning?{reasoning:n.reasoning}:{},...n.toolCalls.length?{toolCalls:n.toolCalls}:{},...n.finishReason?{finishReason:n.finishReason}:{},usage:n.usage,...n.error?{error:n.error}:{}}},S=(0,ai_store_1.writeAiContent)(w);S&&(l=S.id,h=S.truncated)}const v=a!==void 0&&a>=400||t.failure!==void 0&&!y,C=u??"?";!(0,emit_1.lensEmit)("ai",t.failure!==void 0&&!y?"error":"info",a!==void 0?`ai ${t.target.provider}/${C} ${t.target.api} \u2192 ${a}`:`ai ${t.target.provider}/${C} ${t.target.api} ${y?"aborted":"failed"}`,{name:t.target.provider,kind:"ai-call",provider:t.target.provider,api:t.target.api,...u?{model:u}:{},...r.model&&u&&r.model!==u?{requestedModel:r.model}:{},host:t.host,endpoint:t.endpoint,method:t.method,...a!==void 0?{status:a}:{},stream:p,startedAt:t.startedAt,durationMs:A,...t.ttfbMs!==void 0?{ttfbMs:t.ttfbMs}:{},...m!==void 0?{firstEventMs:m}:{},...Object.keys(n.usage).length?{usage:n.usage}:{},...n.finishReason?{finishReason:n.finishReason}:{},toolCallCount:n.toolCalls.length,sizes:{messageCount:r.messages.length,toolCount:r.tools.length,promptChars:_,completionChars:n.completion.length,...s!==void 0?{requestBytes:s}:{}},...r.inputCount!==void 0?{inputCount:r.inputCount}:{},...n.vectorCount!==void 0?{vectorCount:n.vectorCount}:{},contentCaptured:l!==void 0,...l?{contentRef:l}:{},...h?{truncated:!0}:{},...b?{aborted:!0}:{},...T?{timedOut:!0}:{},...n.error?{error:(0,ai_store_1.redactAiText)(n.error).slice(0,300)}:{}},v?`ai:error:${t.target.provider}:${C}`:`ai:${t.target.provider}:${C}`,t.ctx,"ai")&&l&&(0,ai_store_1.removeAiContent)(l)}c(capture,"capture"),d(capture,"capture");let savedFetch;const pending=new Set;async function flushAiTapForTests(){await Promise.allSettled([...pending])}c(flushAiTapForTests,"flushAiTapForTests"),d(flushAiTapForTests,"flushAiTapForTests");function resetAiTapForTests(){const t=globalThis;savedFetch&&(t.fetch=savedFetch),delete t.__lensmcpAiTap,savedFetch=void 0,pending.clear()}c(resetAiTapForTests,"resetAiTapForTests"),d(resetAiTapForTests,"resetAiTapForTests");function tapAi(){const t=aiCaptureMode();if(t==="off")return;const e=globalThis;if(typeof e.fetch!="function"||e.__lensmcpAiTap)return;e.__lensmcpAiTap=!0;const o=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS),s=e.fetch.bind(globalThis);savedFetch=e.fetch,e.fetch=(async(i,r)=>{const a=locate(i),f=methodOf(i,r),n=a&&f==="POST"?(0,ai_providers_1.matchTarget)(a.host,a.port,a.pathname,o):void 0;if(!a||!n)return s(i,r);const p=Date.now(),m=(0,flow_context_1.flowContext)(),g=a.pathname.length>MAX_ENDPOINT_CHARS?`${a.pathname.slice(0,MAX_ENDPOINT_CHARS)}\u2026`:a.pathname,y=takeRequestBody(i,r),T=r?.signal??(typeof i=="object"&&i!==null&&!(i instanceof URL)?i.signal:void 0),b=d(h=>{const v=capture(h).catch(()=>{});pending.add(v),v.finally(()=>pending.delete(v))},"track"),A={mode:t,target:n,host:a.host,endpoint:g,method:f,startedAt:p,ctx:m,signal:T,request:y};let u;try{u=await s(i,r)}catch(h){throw b({...A,failure:h}),h}const _=Date.now()-p;let l;try{l=u.clone()}catch{l=void 0}return b({...A,ttfbMs:_,res:u,...l?{clone:l}:{}}),u})}c(tapAi,"tapAi"),d(tapAi,"tapAi");
1
+ "use strict";var R=Object.defineProperty;var c=(t,e)=>R(t,"name",{value:e,configurable:!0});var x=Object.defineProperty,d=c((t,e)=>x(t,"name",{value:e,configurable:!0}),"d");Object.defineProperty(exports,"__esModule",{value:!0}),exports.aiCaptureMode=aiCaptureMode,exports.flushAiTapForTests=flushAiTapForTests,exports.resetAiTapForTests=resetAiTapForTests,exports.tapAi=tapAi;const emit_1=require("./emit"),flow_context_1=require("./flow-context"),ai_providers_1=require("./ai-providers"),ai_store_1=require("./ai-store");function aiCaptureMode(t=process.env){const e=(t.LENSMCP_AI_CAPTURE??"").trim().toLowerCase();return e==="content"?"content":e==="metadata"||e==="1"||e==="true"||e==="on"?"metadata":"off"}c(aiCaptureMode,"aiCaptureMode"),d(aiCaptureMode,"aiCaptureMode");const MAX_REQUEST_PARSE_BYTES=2*1024*1024,MAX_RESPONSE_PARSE_BYTES=8*1024*1024,MAX_STREAM_BYTES=8*1024*1024,MAX_STREAM_MS=300*1e3,MAX_ENDPOINT_CHARS=256;function locate(t){try{const e=new URL(typeof t=="string"?t:t instanceof URL?t.href:t.url);return{host:e.hostname,port:e.port,pathname:e.pathname}}catch{return}}c(locate,"locate"),d(locate,"locate");const methodOf=d((t,e)=>{const o=typeof t=="object"&&t!==null&&!(t instanceof URL)?t.method:void 0;return String(e?.method??o??"GET").toUpperCase()},"methodOf"),decode=d(t=>({bytes:t.byteLength,...t.byteLength<=MAX_REQUEST_PARSE_BYTES?{text:Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8")}:{}}),"decode");function takeRequestBody(t,e){const o=e?.body;if(typeof o=="string")return{text:o,bytes:Buffer.byteLength(o)};if(o instanceof ArrayBuffer)return decode(new Uint8Array(o));if(ArrayBuffer.isView(o))return decode(new Uint8Array(o.buffer,o.byteOffset,o.byteLength));if(typeof Blob<"u"&&o instanceof Blob)return{bytes:o.size,...o.size<=MAX_REQUEST_PARSE_BYTES?{pending:o.text().catch(()=>{})}:{}};if(o==null&&typeof t=="object"&&t!==null&&!(t instanceof URL)){const s=t;if(!s.bodyUsed&&s.body)try{return{pending:s.clone().text().catch(()=>{})}}catch{return{}}}return{}}c(takeRequestBody,"takeRequestBody"),d(takeRequestBody,"takeRequestBody");const sum=d(t=>t.reduce((e,o)=>e+o,0),"sum");function parseJson(t){if(t)try{return JSON.parse(t)}catch{return}}c(parseJson,"parseJson"),d(parseJson,"parseJson");function headerOf(t,e){try{return t?.headers?.get?.(e)??""}catch{return""}}c(headerOf,"headerOf"),d(headerOf,"headerOf");const statusOf=d(t=>typeof t?.status=="number"?t.status:void 0,"statusOf");function failureKind(t,e){const o=t?.name,s=o==="TimeoutError";return{timedOut:s,aborted:!s&&(o==="AbortError"||e?.aborted===!0)}}c(failureKind,"failureKind"),d(failureKind,"failureKind");async function readStream(t,e,o){const s=(0,ai_providers_1.createStreamAccumulator)(e);let i,r=!1;const a=t.body;if(a){const l=a.getReader(),n=new TextDecoder;let p=0;try{for(;;){const{done:m,value:g}=await l.read();if(m)break;if(p+=g.byteLength,s.push(n.decode(g,{stream:!0})),i===void 0&&s.eventCount>0&&(i=Date.now()-o),p>MAX_STREAM_BYTES||Date.now()-o>MAX_STREAM_MS){r=!0,await l.cancel().catch(()=>{});break}}}catch{r=!0}}return{response:s.end(),sse:!0,cut:r,...i!==void 0?{firstEventMs:i}:{}}}c(readStream,"readStream"),d(readStream,"readStream");async function readBody(t){const e={completion:"",toolCalls:[],usage:{}};if(!t.res||!t.clone)return{response:e,sse:!1,cut:!1};try{if(headerOf(t.res,"content-type").includes("text/event-stream"))return await readStream(t.clone,t.target,t.startedAt);const o=Number(headerOf(t.res,"content-length"));if(Number.isFinite(o)&&o>MAX_RESPONSE_PARSE_BYTES)return{response:e,sse:!1,cut:!1};const s=await t.clone.text().catch(()=>{});if(s&&/^\s*(?:event|data):/.test(s)){const i=(0,ai_providers_1.createStreamAccumulator)(t.target);return i.push(s),{response:i.end(),sse:!0,cut:!1}}return{response:(0,ai_providers_1.normalizeResponse)(t.target,parseJson(s)),sse:!1,cut:!1}}catch{return{response:e,sse:!1,cut:!1}}}c(readBody,"readBody"),d(readBody,"readBody");async function capture(t){const e=readBody(t),o=t.request.text??await t.request.pending,s=t.request.bytes??(o?Buffer.byteLength(o):void 0),i=s!==void 0&&s>MAX_REQUEST_PARSE_BYTES,r=(0,ai_providers_1.normalizeRequest)(t.target,i?void 0:parseJson(o)),a=statusOf(t.res),l=await e;let n=l.response;const p=r.stream||l.sse,m=l.firstEventMs,g=l.cut,{aborted:y,timedOut:C}=t.failure!==void 0?failureKind(t.failure,t.signal):{aborted:!1,timedOut:!1};t.failure!==void 0&&(n={...n,error:String(t.failure?.message??t.failure)});const b=y||g,A=Date.now()-t.startedAt,u=n.model??r.model,_=sum(r.messages.map(w=>w.content.length))+(r.system?.length??0);let f,h=!1;if(t.mode==="content"&&t.target.api!=="embeddings"){const w={schemaVersion:1,id:(0,ai_store_1.newAiCallId)(),capturedAt:Date.now(),...t.ctx?.flowId?{flowId:t.ctx.flowId}:{},...t.ctx?.requestId?{requestId:t.ctx.requestId}:{},provider:t.target.provider,api:t.target.api,...u?{model:u}:{},host:t.host,endpoint:t.endpoint,stream:p,...a!==void 0?{status:a}:{},request:{...r.system?{system:r.system}:{},messages:r.messages,...r.tools.length?{tools:r.tools}:{},...Object.keys(r.params).length?{params:r.params}:{}},response:{completion:n.completion,...n.reasoning?{reasoning:n.reasoning}:{},...n.toolCalls.length?{toolCalls:n.toolCalls}:{},...n.finishReason?{finishReason:n.finishReason}:{},usage:n.usage,...n.error?{error:n.error}:{}}},S=(0,ai_store_1.writeAiContent)(w);S&&(f=S.id,h=S.truncated)}const v=a!==void 0&&a>=400||t.failure!==void 0&&!y,T=u??"?";(0,emit_1.lensEmit)("ai",t.failure!==void 0&&!y?"error":"info",a!==void 0?`ai ${t.target.provider}/${T} ${t.target.api} \u2192 ${a}`:`ai ${t.target.provider}/${T} ${t.target.api} ${y?"aborted":"failed"}`,{name:t.target.provider,kind:"ai-call",provider:t.target.provider,api:t.target.api,...u?{model:u}:{},...r.model&&u&&r.model!==u?{requestedModel:r.model}:{},host:t.host,endpoint:t.endpoint,method:t.method,...a!==void 0?{status:a}:{},stream:p,startedAt:t.startedAt,durationMs:A,...t.ttfbMs!==void 0?{ttfbMs:t.ttfbMs}:{},...m!==void 0?{firstEventMs:m}:{},...Object.keys(n.usage).length?{usage:n.usage}:{},...n.finishReason?{finishReason:n.finishReason}:{},toolCallCount:n.toolCalls.length,sizes:{messageCount:r.messages.length,toolCount:r.tools.length,promptChars:_,completionChars:n.completion.length,...s!==void 0?{requestBytes:s}:{}},...r.inputCount!==void 0?{inputCount:r.inputCount}:{},...n.vectorCount!==void 0?{vectorCount:n.vectorCount}:{},contentCaptured:f!==void 0,...f?{contentRef:f}:{},...h?{truncated:!0}:{},...b?{aborted:!0}:{},...C?{timedOut:!0}:{},...n.error?{error:(0,ai_store_1.redactAiText)(n.error).slice(0,300)}:{}},v?`ai:error:${t.target.provider}:${T}`:`ai:${t.target.provider}:${T}`,t.ctx,"ai")}c(capture,"capture"),d(capture,"capture");let savedFetch;const pending=new Set;async function flushAiTapForTests(){await Promise.allSettled([...pending])}c(flushAiTapForTests,"flushAiTapForTests"),d(flushAiTapForTests,"flushAiTapForTests");function resetAiTapForTests(){const t=globalThis;savedFetch&&(t.fetch=savedFetch),delete t.__lensmcpAiTap,savedFetch=void 0,pending.clear()}c(resetAiTapForTests,"resetAiTapForTests"),d(resetAiTapForTests,"resetAiTapForTests");function tapAi(){const t=aiCaptureMode();if(t==="off")return;const e=globalThis;if(typeof e.fetch!="function"||e.__lensmcpAiTap)return;e.__lensmcpAiTap=!0;const o=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS),s=e.fetch.bind(globalThis);savedFetch=e.fetch,e.fetch=(async(i,r)=>{const a=locate(i),l=methodOf(i,r),n=a&&l==="POST"?(0,ai_providers_1.matchTarget)(a.host,a.port,a.pathname,o):void 0;if(!a||!n)return s(i,r);const p=Date.now(),m=(0,flow_context_1.flowContext)(),g=a.pathname.length>MAX_ENDPOINT_CHARS?`${a.pathname.slice(0,MAX_ENDPOINT_CHARS)}\u2026`:a.pathname,y=takeRequestBody(i,r),C=r?.signal??(typeof i=="object"&&i!==null&&!(i instanceof URL)?i.signal:void 0),b=d(h=>{const v=capture(h).catch(()=>{});pending.add(v),v.finally(()=>pending.delete(v))},"track"),A={mode:t,target:n,host:a.host,endpoint:g,method:l,startedAt:p,ctx:m,signal:C,request:y};let u;try{u=await s(i,r)}catch(h){throw b({...A,failure:h}),h}const _=Date.now()-p;let f;try{f=u.clone()}catch{f=void 0}return b({...A,ttfbMs:_,res:u,...f?{clone:f}:{}}),u})}c(tapAi,"tapAi"),d(tapAi,"tapAi");
@@ -1 +1 @@
1
- "use strict";var m=Object.defineProperty;var r=(e,o)=>m(e,"name",{value:o,configurable:!0});var a=Object.defineProperty,n=r((e,o)=>a(e,"name",{value:o,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.tapEventLoop=tapEventLoop,exports.stopEventLoopTapForTests=stopEventLoopTapForTests;const node_perf_hooks_1=require("node:perf_hooks"),emit_1=require("./emit"),SAMPLE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_SAMPLE_MS);return Number.isFinite(e)&&e>=1e3?e:3e4})(),REPORT_ABOVE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_MIN_MS);return Number.isFinite(e)&&e>=0?e:50})(),RESOLUTION_MS=20;let timer,published=!1;function tapEventLoop(){if(process.env.LENSMCP_EVENT_LOOP==="0"||timer)return;const e=(0,node_perf_hooks_1.monitorEventLoopDelay)({resolution:RESOLUTION_MS});e.enable(),timer=setInterval(()=>{const o=RESOLUTION_MS,s=e.mean/1e6;if(!Number.isFinite(s))return;const i=Math.max(0,s-o),t=Math.max(0,e.max/1e6-o),p=Math.max(0,e.percentile(99)/1e6-o);e.reset(),!(published&&t<REPORT_ABOVE_MS)&&(published=!0,(0,emit_1.lensEmit)("perf",t>=1e3?"error":"info",`event loop lag ${i.toFixed(1)}ms mean / ${t.toFixed(1)}ms max`,{kind:"event-loop-lag",lagMs:Number(i.toFixed(2)),maxMs:Number(t.toFixed(2)),p99Ms:Number(p.toFixed(2)),windowMs:SAMPLE_MS},"perf:event-loop-lag",void 0,"performance"))},SAMPLE_MS),timer.unref?.()}r(tapEventLoop,"tapEventLoop"),n(tapEventLoop,"tapEventLoop");function stopEventLoopTapForTests(){timer&&clearInterval(timer),timer=void 0,published=!1}r(stopEventLoopTapForTests,"stopEventLoopTapForTests"),n(stopEventLoopTapForTests,"stopEventLoopTapForTests");
1
+ "use strict";var f=Object.defineProperty;var a=(e,t)=>f(e,"name",{value:t,configurable:!0});var E=Object.defineProperty,o=a((e,t)=>E(e,"name",{value:t,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.tapEventLoop=tapEventLoop,exports.stopEventLoopTapForTests=stopEventLoopTapForTests;const node_perf_hooks_1=require("node:perf_hooks"),emit_1=require("./emit"),loop_identity_1=require("./loop-identity"),tap_loop_callbacks_1=require("./tap-loop-callbacks"),loop_watchdog_1=require("./loop-watchdog"),SAMPLE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_SAMPLE_MS);return Number.isFinite(e)&&e>=1e3?e:3e4})(),REPORT_ABOVE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_MIN_MS);return Number.isFinite(e)&&e>=0?e:50})(),RESOLUTION_MS=20;let timer,published=!1,stopMonitoring;function tapEventLoop(){if(process.env.LENSMCP_EVENT_LOOP==="0"||timer)return;const e=(0,node_perf_hooks_1.monitorEventLoopDelay)({resolution:RESOLUTION_MS});e.enable();let t=node_perf_hooks_1.performance.eventLoopUtilization(),p=0;const s=process.env.LENSMCP_EVENT_LOOP_CALLBACKS!=="0";let r=!1;const l=o(()=>`${s?"Callback wall time and loop delay":"Loop delay only; callback measurement disabled"}; ${r?"independent watchdog":"watchdog unavailable or disabled"}`,"coverage"),c=(0,loop_watchdog_1.startLoopWatchdog)(()=>{r=!1,(0,emit_1.lensEmit)("perf","warning","Event loop watchdog unavailable",{kind:"event-loop-status",loop:loop_identity_1.NODE_LOOP,coverage:l()},"perf:event-loop-status",void 0,"performance")});r=!!c,(0,emit_1.lensEmit)("perf","info","Event loop monitoring started",{kind:"event-loop-status",loop:loop_identity_1.NODE_LOOP,coverage:l()},"perf:event-loop-status",void 0,"performance"),timer=setInterval(()=>{const i=RESOLUTION_MS,d=e.mean/1e6;if(!Number.isFinite(d))return;const v=Math.max(0,d-i),n=Math.max(0,e.max/1e6-i),_=Math.max(0,e.percentile(99)/1e6-i);if(e.reset(),published&&n<REPORT_ABOVE_MS&&Date.now()-p<6e4)return;published=!0,p=Date.now();const m=node_perf_hooks_1.performance.eventLoopUtilization(),L=node_perf_hooks_1.performance.eventLoopUtilization(m,t).utilization;t=m,(0,emit_1.lensEmit)("perf",n>=1e3?"error":"info",`event loop lag ${v.toFixed(1)}ms mean / ${n.toFixed(1)}ms max`,{kind:"event-loop-lag",lagMs:Number(v.toFixed(2)),maxMs:Number(n.toFixed(2)),p99Ms:Number(_.toFixed(2)),windowMs:SAMPLE_MS,loop:loop_identity_1.NODE_LOOP,utilization:Number(L.toFixed(4))},"perf:event-loop-lag",void 0,"performance")},SAMPLE_MS),timer.unref?.();const u=s?(0,tap_loop_callbacks_1.tapLoopCallbacks)(Math.max(50,REPORT_ABOVE_MS)):void 0;stopMonitoring=o(()=>{u?.(),c?.(),e.disable()},"stopMonitoring")}a(tapEventLoop,"tapEventLoop"),o(tapEventLoop,"tapEventLoop");function stopEventLoopTapForTests(){timer&&clearInterval(timer),timer=void 0,stopMonitoring?.(),stopMonitoring=void 0,published=!1}a(stopEventLoopTapForTests,"stopEventLoopTapForTests"),o(stopEventLoopTapForTests,"stopEventLoopTapForTests");
@@ -0,0 +1,2 @@
1
+ /** Measures callback execution, not the time a promise/request spent awaiting I/O. No per-callback stack capture. */
2
+ export declare function tapLoopCallbacks(minMs?: number): () => void;
@@ -0,0 +1 @@
1
+ "use strict";var y=Object.defineProperty;var d=(r,e)=>y(r,"name",{value:e,configurable:!0});var _=Object.defineProperty,f=d((r,e)=>_(r,"name",{value:e,configurable:!0}),"f");Object.defineProperty(exports,"__esModule",{value:!0}),exports.tapLoopCallbacks=tapLoopCallbacks;const node_async_hooks_1=require("node:async_hooks"),node_perf_hooks_1=require("node:perf_hooks"),emit_1=require("./emit"),flow_context_1=require("./flow-context"),loop_identity_1=require("./loop-identity");function tapLoopCallbacks(r=50){const e=new Map,a=new Map;let n=!1,p=!1,c=!1,i=0;const s=[],u=(0,node_async_hooks_1.createHook)({init(t,l){n||(e.set(t,l),e.size>2e4&&e.delete(e.keys().next().value))},before(t){if(!(n||!e.has(t)))try{a.set(t,{start:node_perf_hooks_1.performance.now(),at:Date.now(),type:e.get(t)??"unknown"})}catch{}},after(t){const l=a.get(t);if(a.delete(t),!l||n)return;const k=node_perf_hooks_1.performance.now()-l.start;if(!(k<r)){if(s.length>=50){i++;return}s.push({...l,duration:k,id:t,context:(0,flow_context_1.flowContext)()}),!c&&(c=!0,n=!0,setImmediate(()=>{if(c=!1,!p){n=!0;try{for(const o of s.splice(0))(0,emit_1.lensEmit)("perf",o.duration>=1e3?"error":"warning",`Event loop callback blocked for ${o.duration.toFixed(1)}ms (${o.type})`,{kind:"event-loop-stall",incidentId:`${loop_identity_1.NODE_LOOP.id}:callback:${o.id}:${o.start}`,loop:loop_identity_1.NODE_LOOP,startedAt:o.at,durationMs:Math.round(o.duration*100)/100,state:"recovered",mechanism:"callback",callbackType:o.type,...i?{droppedIncidents:i}:{}},"perf:event-loop-stall",o.context,"performance");i=0}finally{n=!1}}}),n=!1)}},destroy(t){e.delete(t),a.delete(t)}});return u.enable(),()=>{p=!0,u.disable(),e.clear(),a.clear(),s.length=0}}d(tapLoopCallbacks,"tapLoopCallbacks"),f(tapLoopCallbacks,"tapLoopCallbacks");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/node-instrumentation",
3
- "version": "1.25.24",
3
+ "version": "1.25.26",
4
4
  "main": "./index.js",
5
5
  "types": "./index.d.ts",
6
6
  "exports": {
@@ -15,9 +15,9 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@lensmcp/core": "1.25.24",
19
- "@lensmcp/nest-instrumentation": "1.25.24",
20
- "@lensmcp/protocol-types": "1.25.24",
18
+ "@lensmcp/core": "1.25.26",
19
+ "@lensmcp/nest-instrumentation": "1.25.26",
20
+ "@lensmcp/protocol-types": "1.25.26",
21
21
  "tslib": "^2.3.0"
22
22
  },
23
23
  "optionalDependencies": {