Files
thedotmack__claude-mem/plugin/scripts/transcript-watcher.cjs
T

30 lines
74 KiB
JavaScript
Raw Normal View History

fix(build): add transcript-watcher.cjs build target (closes #2450) (#2592) * fix(build): add transcript-watcher.cjs build target (closes #2450) src/npx-cli/commands/runtime.ts:230 hard-codes a reference to plugin/scripts/transcript-watcher.cjs, but scripts/build-hooks.js never compiled this entry. The fallback `spawnBunWorkerCommand` path silently no-ops on systems where bun isn't reachable, so `claude-mem transcript watch` is effectively dead in the published npm bundle. This adds: - A thin entry (src/services/transcripts/transcript-watcher-entry.ts) that dispatches process.argv to the existing runTranscriptCommand function in cli.ts (which until now had no caller in the build). - A new esbuild target in build-hooks.js modeled on context-generator (Node 18 CJS, minified, bun:sqlite external) plus the standard stripHardcodedDirname pass and chmod 0755. - Updated build summary so the new artifact shows up in the "compiled successfully" footer. The compiled bundle is 137 KB. `node plugin/scripts/transcript-watcher.cjs` prints the usage line and exits 1 as expected; `... validate`, `... init`, and `... watch` all route correctly. No behavior change on platforms that previously had a working bun fallback - the explicit path now just wins the existsSync check. * fix(build): externalize zod + add bundle-size guard for transcript-watcher Addresses Greptile review on #2592: 1. Add `zod` to the external list. worker-service and server-beta-service both externalize zod and rely on plugin/package.json to provide it at runtime; the watcher build was inconsistent. The current processor.ts chain does not actually pull in zod (bundle size unchanged at 137.64 KB), but the external entry is defensive — any future import in src/services/transcripts/ that touches zod will now resolve against the plugin's declared dependency rather than getting silently inlined (and potentially creating a duplicate-instance hazard). 2. Add a 200 KB size guard mirroring the 600 KB ceiling on mcp-server.cjs. The watcher is meant to be a thin file-tail loop; if a transitive import ever drags a heavy module (worker-service, an SDK runtime) into the chain, this catches it at build time with an informative error pointing at processor.ts / watcher.ts. No functional change to the runtime artifact (size identical, behavior identical). Both follow-ups recommended by Greptile.
2026-06-06 14:21:25 +08:00
#!/usr/bin/env bun
2026-08-02 10:14:45 -07:00
"use strict";var tn=Object.create;var Rt=Object.defineProperty;var rn=Object.getOwnPropertyDescriptor;var nn=Object.getOwnPropertyNames;var on=Object.getPrototypeOf,sn=Object.prototype.hasOwnProperty;var Se=(r,e,t)=>()=>{if(t)throw t[0];try{return r&&(e=r(r=0)),e}catch(n){throw t=[n],n}};var an=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of nn(e))!sn.call(r,o)&&o!==t&&Rt(r,o,{get:()=>e[o],enumerable:!(n=rn(e,o))||n.enumerable});return r};var X=(r,e,t)=>(t=r!=null?tn(on(r)):{},an(e||!r||!r.__esModule?Rt(t,"default",{value:r,enumerable:!0}):t,r));function Ze(r){return(cn??process.stderr.write.bind(process.stderr))(r)}function ee(r){Ze(r)}function At(r,e={}){he&&he.length>0&&(Ze(he.join("")),he=[]),Ze(r.endsWith(`
`)?r:`${r}
2026-08-02 10:14:45 -07:00
`),e.skipExit||process.exit(2)}var cn,he,ye=Se(()=>{"use strict";cn=null;he=null});function un(r){return r.replace(/^\uFEFF/,"")}function K(r){return JSON.parse(un(r))}function dn(r){(0,p.existsSync)(r)||(0,p.mkdirSync)(r,{recursive:!0})}function Te(r,e){let t=r;try{if((0,p.lstatSync)(r).isSymbolicLink())try{t=(0,p.realpathSync)(r)}catch(d){let u=d instanceof Error?d:new Error(String(d));ee(`claude-mem: realpathSync failed for ${r}, resolving symlink manually: ${u.message}
`);let E=(0,p.readlinkSync)(r);t=(0,D.resolve)((0,D.dirname)(r),E)}}catch(d){let u=d.code;if(u!=="ENOENT"&&u!=="ENOTDIR")throw d}dn((0,D.dirname)(t));let n=(0,D.dirname)(t),o=(0,D.basename)(t),i=(0,D.join)(n,`.${o}.${process.pid}.${(0,kt.randomBytes)(6).toString("hex")}.tmp`),s=Buffer.from(JSON.stringify(e,null,2)+`
`,"utf-8"),c;try{c=(0,p.statSync)(t).mode&511}catch{}let l;try{l=c!==void 0?(0,p.openSync)(i,"w",c):(0,p.openSync)(i,"w");let d=0;for(;d<s.length;){let u=(0,p.writeSync)(l,s,d,s.length-d);if(u===0)throw new Error(`writeSync stalled at ${d}/${s.length} bytes`);d+=u}if((0,p.fsyncSync)(l),(0,p.closeSync)(l),l=void 0,(0,p.renameSync)(i,t),!ln){let u;try{u=(0,p.openSync)(n,"r"),(0,p.fsyncSync)(u)}catch(E){let g=E instanceof Error?E:new Error(String(E));ee(`claude-mem: directory fsync failed for ${n}: ${g.message}
2026-08-02 10:14:45 -07:00
`)}finally{if(u!==void 0)try{(0,p.closeSync)(u)}catch{}}}}catch(d){if(l!==void 0)try{(0,p.closeSync)(l)}catch{}try{(0,p.unlinkSync)(i)}catch{}throw d}}var p,kt,D,ln,ce=Se(()=>{"use strict";p=require("fs"),kt=require("crypto"),D=require("path");ye();ln=process.platform==="win32"});function pn(){return typeof __dirname<"u"?__dirname:(0,m.dirname)((0,Ot.fileURLToPath)(En.url))}function It(r){return typeof r!="string"||r.length===0?r:r==="~"?(0,le.homedir)():r.startsWith("~/")?(0,m.join)((0,le.homedir)(),r.slice(2)):r}function te(){if(process.env.CLAUDE_MEM_DATA_DIR)return It(process.env.CLAUDE_MEM_DATA_DIR);let r=(0,m.join)((0,le.homedir)(),".claude-mem"),e=(0,m.join)(r,"settings.json");try{if((0,ue.existsSync)(e)){let t=K((0,ue.readFileSync)(e,"utf-8")),n=t.env??t;if(n.CLAUDE_MEM_DATA_DIR)return It(n.CLAUDE_MEM_DATA_DIR)}}catch{}return r}var m,le,ue,Ot,En,Xo,S,fn,we,mn,re,zo,et,gn,y,w=Se(()=>{"use strict";m=require("path"),le=require("os"),ue=require("fs"),Ot=require("url");ce();En={};Xo=pn();S=te(),fn=process.env.CLAUDE_CONFIG_DIR||(0,m.join)((0,le.homedir)(),".claude"),we=(0,m.join)(fn,"plugins","marketplaces","thedotmack"),mn=(0,m.join)(S,"logs"),re=(0,m.join)(S,"settings.json"),zo=(0,m.join)(S,"claude-mem.db"),et=(0,m.join)(S,"observer-sessions"),gn=(0,m.basename)(et),y={dataDir:()=>S,workerPid:()=>(0,m.join)(S,"worker.pid"),serverPid:()=>(0,m.join)(S,".server-beta.pid"),serverPort:()=>(0,m.join)(S,".server-beta.port"),serverRuntime:()=>(0,m.join)(S,".server-beta.runtime.json"),settings:()=>(0,m.join)(S,"settings.json"),database:()=>(0,m.join)(S,"claude-mem.db"),chroma:()=>(0,m.join)(S,"chroma"),combinedCerts:()=>(0,m.join)(S,"combined_certs.pem"),transcriptsConfig:()=>(0,m.join)(S,"transcript-watch.json"),transcriptsState:()=>(0,m.join)(S,"transcript-watch-state.json"),corpora:()=>(0,m.join)(S,"corpora"),supervisorRegistry:()=>(0,m.join)(S,"supervisor.json"),envFile:()=>(0,m.join)(S,".env"),logsDir:()=>mn}});var U,Pt,nt,rt,ot,a,f=Se(()=>{"use strict";U=require("fs"),Pt=require("path");w();ye();ce();nt=(i=>(i[i.DEBUG=0]="DEBUG",i[i.INFO=1]="INFO",i[i.WARN=2]="WARN",i[i.ERROR=3]="ERROR",i[i.SILENT=4]="SILENT",i))(nt||{}),rt=null,ot=class{level=null;useColor;logFilePath=null;logFileInitialized=!1;constructor(){this.useColor=process.stdout.isTTY??!1}ensureLogFileInitialized(){if(!this.logFileInitialized){this.logFileInitialized=!0;try{let e=y.logsDir();(0,U.existsSync)(e)||(0,U.mkdirSync)(e,{recursive:!0});let t=new Date().toISOString().split("T")[0];this.logFilePath=(0,Pt.join)(e,`claude-mem-${t}.log`)}catch(e){console.error("[LOGGER] Failed to initialize log file:",e instanceof Error?e.message:String(e)),this.logFilePath=null}}}getLevel(){if(this.level===null)try{let e=y.settings();if((0,U.existsSync)(e)){let t=(0,U.readFileSync)(e,"utf-8"),o=(K(t).CLAUDE_MEM_LOG_LEVEL||"INFO").toUpperCase();this.level=nt[o]??1}else this.level=1}catch(e){console.error("[LOGGER] Failed to load log level from settings:",e instanceof Error?e.message:String(e)),this.level=1}return this.level}formatData(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return e.toString();if(typeof e=="object"){if(e instanceof Error)return this.getLevel()===0?`${e.message}
${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let t=Object.keys(e);return t.length===0?"{}":t.length<=3?JSON.stringify(e):`{${t.length} keys: ${t.slice(0,3).join(", ")}...}`}return String(e)}formatTool(e,t){if(!t)return e;let n=t;if(typeof t=="string")try{n=JSON.parse(t)}catch{n=t}if(e==="Bash"&&n.command)return`${e}(${n.command})`;if(n.file_path)return`${e}(${n.file_path})`;if(n.notebook_path)return`${e}(${n.notebook_path})`;if(e==="Glob"&&n.pattern)return`${e}(${n.pattern})`;if(e==="Grep"&&n.pattern)return`${e}(${n.pattern})`;if(n.url)return`${e}(${n.url})`;if(n.query)return`${e}(${n.query})`;if(e==="Task"){if(n.subagent_type)return`${e}(${n.subagent_type})`;if(n.description)return`${e}(${n.description})`}return e==="Skill"&&n.skill?`${e}(${n.skill})`:e==="LSP"&&n.operation?`${e}(${n.operation})`:e}formatTimestamp(e){let t=e.getFullYear(),n=String(e.getMonth()+1).padStart(2,"0"),o=String(e.getDate()).padStart(2,"0"),i=String(e.getHours()).padStart(2,"0"),s=String(e.getMinutes()).padStart(2,"0"),c=String(e.getSeconds()).padStart(2,"0"),l=String(e.getMilliseconds()).padStart(3,"0");return`${t}-${n}-${o} ${i}:${s}:${c}.${l}`}log(e,t,n,o,i){if(e<this.getLevel())return;this.ensureLogFileInitialized();let s=this.formatTimestamp(new Date),c=nt[e].padEnd(5),l=t.padEnd(6),d="";o?.correlationId?d=`[${o.correlationId}] `:o?.sessionId&&(d=`[session-${o.sessionId}] `);let u="";if(i!=null)if(i instanceof Error)u=this.getLevel()===0?`
${i.message}
${i.stack}`:` ${i.message}`;else if(this.getLevel()===0&&typeof i=="object")try{u=`
2026-08-02 10:14:45 -07:00
`+JSON.stringify(i,null,2)}catch{u=" "+this.formatData(i)}else u=" "+this.formatData(i);let E="";if(o){let{sessionId:M,memorySessionId:_,correlationId:Y,..._e}=o;Object.keys(_e).length>0&&(E=` {${Object.entries(_e).map(([q,en])=>`${q}=${en}`).join(", ")}}`)}let g=`[${s}] [${c}] [${l}] ${d}${n}${E}${u}`;if(this.logFilePath)try{(0,U.appendFileSync)(this.logFilePath,g+`
`,"utf8")}catch(M){let _=M instanceof Error?M:new Error(String(M));ee(`[LOGGER] Failed to write to log file: ${_.message}
fix(worker): single version oracle — never rank plugin cache dirs by mtime The 2026-07-22 restart storm: claude-mem had four resolvers that ranked plugin cache dirs by directory mtime (worker-utils cacheWorkerScriptCandidates, the inline node L() and Codex Windows resolvers in hook-shell-template, and the POSIX `ls -dt` hook resolver), while the staleness detector compared the live worker against the marketplace package.json. When Claude Code stamped the superseded 13.11.0 cache dir with .orphaned_at, its mtime became newest, so every recycle respawned 13.11.0 while hooks running 13.12.0 kept demanding a restart — 485 respawns in ~2 hours, each spawning SDK and Chroma children, until the host exhausted its process table. The fix is an invariant, not a mitigation: one deterministic version oracle shared by detection and respawn. - resolveWorkerScript() ranks candidates by version descending (numeric major.minor.patch, release ahead of prerelease at the same base, reverse-lexical tiebreak) and skips .orphaned_at-stamped dirs; stable sort keeps cache → marketplace → cwd precedence on ties. - ensureWorkerRunning() resolves once and feeds the same result to both checkVersionMatch(port, expectedVersion) and the lazy-spawn script, so a mismatch can only ever resolve toward the version the respawn will actually produce — the loop is structurally impossible. - checkVersionMatch now takes the caller's expected version; getInstalledPluginVersion (the second oracle) is deleted. - All three generated bootstrap resolvers (mcp node launcher, Codex Windows launcher, POSIX hook prelude) embed the identical ordering; the POSIX variant uses a zero-padded sort key and balanced (pattern) case forms (unmatched parens are a parser error inside $(...)). - scripts/build-hooks.js gains --write-shell-templates, the regeneration mode its hand-edit tripwire error message points at. - Regression coverage: comparator/selection unit tests plus a functional shell-matrix test that rebuilds the exact storm layout (orphaned old dir with newest mtime) and asserts the new version wins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:30:35 -07:00
${_.stack??""}
`)}else ee(g+`
2026-08-02 10:14:45 -07:00
`)}debug(e,t,n,o){this.log(0,e,t,n,o)}info(e,t,n,o){this.log(1,e,t,n,o)}warn(e,t,n,o){this.log(2,e,t,n,o)}setErrorSink(e){rt=e}error(e,t,n,o){this.log(3,e,t,n,o),this.routeErrorToSink(t,n,o)}routeErrorToSink(e,t,n){try{if(!rt||!(n instanceof Error))return;rt(n)}catch{}}dataIn(e,t,n,o){this.info(e,`\u2192 ${t}`,n,o)}dataOut(e,t,n,o){this.info(e,`\u2190 ${t}`,n,o)}success(e,t,n,o){this.info(e,`\u2713 ${t}`,n,o)}failure(e,t,n,o){this.error(e,`\u2717 ${t}`,n,o)}},a=new ot});var N=require("fs"),tt=require("os"),de=require("path");w();var ne=y.transcriptsConfig(),Me=y.transcriptsState(),_n={version:1,schemas:{},watches:[],stateFile:Me};function Sn(r){let e=typeof r.schema=="string"?r.schema:r.schema?.name;if(!(r.name==="codex"||e==="codex")||!r.path)return!1;let n=b(r.path).replace(/\\/g,"/"),o=(0,de.join)((0,tt.homedir)(),".codex","sessions").replace(/\\/g,"/");return n===`${o}/**/*.jsonl`}function Dt(r){let e=typeof r.schema=="string"?r.schema:r.schema?.name,t=r.name==="codex"&&(!e||e==="codex");return r.context?.mode==="agents"&&t&&Sn(r)}function b(r){return r&&(r.startsWith("~")?(0,de.join)((0,tt.homedir)(),r.slice(1)):r)}function pe(r=ne){let e=b(r);if(!(0,N.existsSync)(e))throw new Error(`Transcript watch config not found: ${e}`);let t=(0,N.readFileSync)(e,"utf-8"),n=JSON.parse(t);if(!n.version||!n.watches)throw new Error(`Invalid transcript watch config: ${e}`);return n.stateFile||(n.stateFile=Me),n}function Ce(r=ne){let e=b(r),t=(0,de.dirname)(e);(0,N.existsSync)(t)||(0,N.mkdirSync)(t,{recursive:!0}),(0,N.writeFileSync)(e,JSON.stringify(_n,null,2))}var C=require("fs"),O=require("path");f();var F=require("fs"),Lt=require("path");f();function xt(r){try{if(!(0,F.existsSync)(r))return{offsets:{}};let e=(0,F.readFileSync)(r,"utf-8"),t=JSON.parse(e);return t.offsets?t:{offsets:{}}}catch(e){return a.warn("TRANSCRIPT","Failed to load watch state, starting fresh",{statePath:r,error:e instanceof Error?e.message:String(e)}),{offsets:{}}}}function Nt(r,e){try{let t=(0,Lt.dirname)(r);(0,F.existsSync)(t)||(0,F.mkdirSync)(t,{recursive:!0}),(0,F.writeFileSync)(r,JSON.stringify(e,null,2))}catch(t){a.warn("TRANSCRIPT","Failed to save watch state",{statePath:r,error:t instanceof Error?t.message:String(t)})}}var Ee=X(require("path"),1);var T=X(require("path"),1),h=require("fs");var st=require("node:child_process");function be(r,e,t){return(0,st.spawn)(r,e??[],{windowsHide:!0,...t})}var hn=new Set([".cmd",".bat"]),yn=new Set([".exe",".com"]),as=new Set([...yn,...hn]);f();var k={HEALTH_CHECK:3e3,API_REQUEST:3e4,HOOK_READINESS_WAIT:1e4,POST_SPAWN_WAIT:15e3,READINESS_WAIT:3e4,PORT_IN_USE_WAIT:3e3,POWERSHELL_COMMAND:1e4,WINDOWS_MULTIPLIER:1.5},B={SUCCESS:0,BLOCKING_ERROR:2};function J(r){return process.platform==="win32"?Math.round(r*k.WINDOWS_MULTIPLIER):r}var ve=require("fs"),it=require("path"),fe=require("os");ce();var Tn="security_alert",v=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5-20251001",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:String(37700+(process.getuid?.()??77)%100),CLAUDE_MEM_WORKER_HOST:"127.0.0.1",CLAUDE_MEM_API_TIMEOUT_MS:String(J(k.API_REQUEST)),CLAUDE_MEM_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",CLAUDE_MEM_PROVIDER:"claude",CLAUDE_MEM_CLAUDE_AUTH_METHOD:"subscription",CLAUDE_MEM_GEMINI_API_KEY:"",CLAUDE_MEM_GEMINI_MODEL:"gemini-flash-latest",CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED:"true",CLAUDE_MEM_OPENROUTER_API_KEY:"",CLAUDE_MEM_OPENROUTER_MODEL:"xiaomi/mimo-v2-flash:free",CLAUDE_MEM_OPENROUTER_BASE_URL:"",CLAUDE_MEM_OPENROUTER_SITE_URL:"",CLAUDE_MEM_OPENROUTER_APP_NAME:"claude-mem",CLAUDE_MEM_DATA_DIR:(0,it.join)((0,fe.homedir)(),".claude-mem"),CLAUDE_MEM_LOG_LEVEL:"INFO",CLAUDE_MEM_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",CLAUDE_MEM_MODE:"code",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"false",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"false",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"false",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_FULL_COUNT:"0",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CON
2026-08-03 14:26:10 -07:00
`)}function Qt(){let r=gt();if(r?.installId)return r.installId;let e=(0,qt.randomUUID)();return Wn({installId:e,decidedAt:""}),e}var Hn=new Set(["version","os","os_version","is_wsl","arch","runtime","runtime_version","node_version","duration_ms","outcome","error_category","locale","is_ci","endpoint","ide","provider","runtime_mode","trigger","count","has_summary","is_update","install_method","interactive","bun_version","uv_version","claude_code_version","observation_count","session_count","timeline_depth_days","has_session_summary","obs_type_bugfix","obs_type_discovery","obs_type_decision","obs_type_refactor","obs_type_other","tokens_injected","tokens_saved_vs_naive","mode","search_strategy","observation_type","hook","compression_ms","tokens_input","tokens_output","compression_ratio","model","cost_usd","endpoint_class","db_observation_count","db_session_count","db_summary_count","db_project_count","db_size_mb","install_age_days","obs_count_7d","obs_count_30d","days_since_last_obs","result_count","chroma_available","fallback_reason","invalid_output_class","consecutive_invalid_outputs","respawn_triggered","abort_reason","previous_shutdown","previous_uptime_seconds","uptime_seconds","shutdown_reason","process_rss_mb","heap_used_mb","hook_type","error_mode","consecutive_failures","threshold_tripped","discovery_tokens","read_tokens","summary_count","prompt_count","project_count","backfilled","first_active_date","session_completed_count","session_failed_count","sessions_claude_count","sessions_codex_count","sessions_gemini_count","sessions_other_platform_count","subagent_obs_count","total_tokens_input","total_tokens_output","total_cost_usd","avg_duration_ms","avg_compression_ms","outcomes_ok","outcomes_error","outcomes_aborted","outcomes_invalid_output","top_model","window_start_ts","rollup_reason","window_seq","total_tokens","avg_tokens","observations_created","total_observations_injected","total_tokens_saved_vs_naive"]),Zt=200;function jn(r,e){if(!(!r||typeof r!="object"))for(let t of Object.keys(r)){if(!Hn.has(t))continue;let n=r[t];typeof n=="string"?e[t]=n.length>Zt?n.slice(0,Zt):n:(typeof n=="number"&&Number.isFinite(n)||typeof n=="boolean")&&(e[t]=n)}}function er(r){let e={};try{jn(r,e)}catch{}return e}var Et=X(require("os"),1);f();var $n="13.13.1",Gn="phc_BKJAeNbpj932N9qEiU6qhutZEiu6LLfRpXfTbLM9MLaG",Yn="https://us.i.posthog.com";function tr(){return process.env.CLAUDE_MEM_TELEMETRY_KEY||Gn}function rr(){return process.env.CLAUDE_MEM_TELEMETRY_HOST||Yn}var Kn=["version","os","os_version","is_wsl","arch","runtime","locale","ide","provider","runtime_mode","install_method","claude_code_version","first_active_date","db_observation_count","db_session_count","db_summary_count","db_project_count","db_size_mb","install_age_days","obs_count_7d","obs_count_30d","days_since_last_obs"];function nr(r){let e={};for(let t of Kn)r[t]!==void 0&&(e[t]=r[t]);return e}function Bn(){if(process.platform!=="linux")return!1;try{return!!process.env.WSL_DISTRO_NAME||Et.default.release().toLowerCase().includes("microsoft")}catch(r){let e=r instanceof Error?r:new Error(String(r));return a.warn("SYSTEM","Telemetry: WSL detection failed; reporting is_wsl=false",void 0,e),!1}}function or(){return{version:$n,os:process.platform,os_version:Et.default.release(),is_wsl:Bn(),arch:process.arch,runtime:process.versions.bun?"bun":"node",runtime_version:process.versions.bun??process.versions.node,node_version:process.versions.node,is_ci:!!process.env.CI,locale:Intl.DateTimeFormat().resolvedOptions().locale}}var Jn=2e3;async function sr(r,e,t){try{if(!zt(process.env,gt()))return;let n=er({...or(),...e??{}});if(t?.person?n.$set=nr(n):n.$process_person_profile=!1,process.env.CLAUDE_MEM_TELEMETRY_DEBUG==="1"){process.stderr.write("[telemetry] "+JSON.stringify({event:r,properties:n})+`
2026-08-02 10:14:45 -07:00
`);return}let o=tr(),i=new AbortController,s=setTimeout(()=>i.abort(),Jn);try{await Vn(o,r,n,i.signal)}finally{clearTimeout(s)}}catch{}}async function Vn(r,e,t,n){await fetch(`${rr()}/capture/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({api_key:r,event:e,distinct_id:Qt(),properties:t}),signal:n})}var z=X(require("path"),1),ar=require("os"),j=require("fs"),St=require("child_process");f();f();f();w();var mi=y.dataDir(),gi=y.workerPid();function ir(r){return r?/(^|[\\/])bun(\.exe)?$/i.test(r.trim()):!1}function Xn(r,e){let t=e==="win32"?`where ${r}`:`which ${r}`,n;try{n=(0,St.execSync)(t,{stdio:["ignore","pipe","ignore"],encoding:"utf-8",windowsHide:!0})}catch(i){return i instanceof Error?a.debug("SYSTEM",`Binary lookup failed for ${r}`,{command:t},i):a.debug("SYSTEM",`Binary lookup failed for ${r}`,{command:t},new Error(String(i))),null}return n.split(/\r?\n/).map(i=>i.trim()).find(i=>i.length>0)||null}var _t;function cr(r={}){let e=Object.keys(r).length===0;if(e&&_t!==void 0)return _t;let t=zn(r);return e&&t!==null&&(_t=t),t}function zn(r){let e=r.platform??process.platform,t=r.execPath??process.execPath;if(ir(t))return t;let n=r.env??process.env,o=r.homeDirectory??(0,ar.homedir)(),i=r.pathExists??j.existsSync,s=r.lookupInPath??Xn,c=e==="win32"?[n.BUN,n.BUN_PATH,z.default.join(o,".bun","bin","bun.exe"),z.default.join(o,".bun","bin","bun"),n.USERPROFILE?z.default.join(n.USERPROFILE,".bun","bin","bun.exe"):void 0,n.LOCALAPPDATA?z.default.join(n.LOCALAPPDATA,"bun","bun.exe"):void 0,n.LOCALAPPDATA?z.default.join(n.LOCALAPPDATA,"bun","bin","bun.exe"):void 0]:[n.BUN,n.BUN_PATH,z.default.join(o,".bun","bin","bun"),"/usr/local/bin/bun","/opt/homebrew/bin/bun","/home/linuxbrew/.linuxbrew/bin/bun","/usr/bin/bun","/snap/bin/bun"];for(let l of c){let d=l?.trim();if(d&&(ir(d)&&i(d)||d.toLowerCase()==="bun"))return d}return s("bun",e)}f();w();function Qn(){return v.loadFromFile(re).CLAUDE_MEM_WORKER_HOST}function Zn(r){return r.startsWith("[")&&r.endsWith("]")?r:r.includes(":")?`[${r}]`:r}async function eo(r,e,t="GET"){let n=await fetch(`http://${Zn(Qn())}:${r}${e}`,{method:t}),o="";try{o=await n.text()}catch{}return{ok:n.ok,statusCode:n.status,body:o}}async function to(r){try{let e=await eo(r,"/api/health");return e.ok?JSON.parse(e.body).version:null}catch{return a.debug("SYSTEM","Could not fetch worker version",{}),null}}async function lr(r,e){let t=e??"unknown",n=await to(r);return!n||t==="unknown"?{matches:!0,pluginVersion:t,workerVersion:n}:{matches:t===n,pluginVersion:t,workerVersion:n}}f();var xe=require("path"),R=require("fs");w();f();var ro=9e4;function ur(){return(0,xe.join)(te(),"spawn.lock")}function dr(){let r=ur(),e=JSON.stringify({pid:process.pid,startedAt:new Date().toISOString()});for(let t=0;t<2;t++)try{return(0,R.mkdirSync)((0,xe.dirname)(r),{recursive:!0}),(0,R.writeFileSync)(r,e,{flag:"wx"}),!0}catch(n){let o=n instanceof Error?n:new Error(String(n)),i=o.code;if(i!=="EEXIST")return a.warn("SYSTEM","Spawn lock write failed for a non-contention reason; failing open (spawning unlocked)",{lockPath:r,code:i},o),!0;if(t>0)return!1;let s;try{s=(0,R.statSync)(r).mtimeMs}catch{continue}if(Date.now()-s<=ro)return!1;let c;try{c=(0,R.statSync)(r).mtimeMs}catch{continue}if(c!==s)return!1;try{(0,R.unlinkSync)(r)}catch{return!1}}return!1}function pr(){let r=ur();try{if(JSON.parse((0,R.readFileSync)(r,"utf-8")).pid!==process.pid)return;(0,R.unlinkSync)(r)}catch{}}function yr(r,e,t){let n=process.env[r];if(n){let o=parseInt(n,10);if(Number.isFinite(o)&&o>=t.min&&o<=t.max)return o;a.warn("SYSTEM",`Invalid ${r}, using default`,{value:n,min:t.min,max:t.max})}return e}var je=yr("CLAUDE_MEM_HEALTH_TIMEOUT_MS",J(k.HEALTH_CHECK),{min:500,max:3e5}),no=yr("CLAUDE_MEM_HOOK_READINESS_TIMEOUT_MS",J(k.HOOK_READINESS_WAIT),{min:0,max:3e5}),oo={min:500,max:3e5};async function yt(r,e={},t){try{return await fetch(r,{...e,signal:AbortSignal.timeout(t)})}catch(n){throw n instanceof DOMException&&n.name==="TimeoutError"?new Error(`Request timed out after ${t}ms`):n}}var Ne=null,Ue=null,Fe=null,We=null;function so(
feat(telemetry): reliability signals — retrieval quality, compression trust, worker lifecycle, hook failures (Plan 14) (#2874) * feat(telemetry): disclose 19 reliability-signal fields and 2 new events across all surfaces Whitelist (scrub.ts), scrub tests, public docs (telemetry.mdx), and CLI disclosure (COLLECTED_FIELDS/EVENT_NAMES) for the Plan 14 reliability signals: search retrieval quality, compression trust, worker lifecycle, and hook failure keys, plus the worker_stopped and hook_failed events. Includes the plan document. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): retrieval-quality signals on search_performed SearchManager.search() fills an optional telemetry envelope (result_count, search_strategy, chroma_available, fallback_reason) across all three search paths; handlers stash it on res.locals.searchTelemetry and the existing finish-middleware spreads it into the search_performed capture. Zero-result searches report result_count: 0; Chroma fallback reasons are a closed enum, never the error message. Response shapes unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): compression trust signals on session_compressed fabrication_detected/fabricated_count flow through compressionProps (all three emit paths); invalid-output respawns emit a respawn-gated session_compressed with outcome invalid_output and the classifier value; aborted generators emit outcome aborted with abort_reason normalized to a closed enum in the .finally where all five abort flows converge (the .catch path can never observe a non-null abortReason). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): worker lifecycle signals — worker_stopped, crash detection, memory metrics Clean-shutdown sentinel written before telemetry flush and consumed at startup; worker_started gains previous_shutdown (crash/clean/unknown) and previous_uptime_seconds derived from the stale PID file; new worker_stopped event (uptime_seconds, shutdown_reason stop/restart/ signal) emitted before shutdownTelemetry(); the CLI restart path tags /api/admin/shutdown?reason=restart so restarts are distinguishable; buildLifecycleProps adds integer process_rss_mb/heap_used_mb. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): threshold-gated hook_failed distress signal via CLI transport recordWorkerUnreachable emits hook_failed exactly when the consecutive- failure count reaches the fail-loud threshold; the generic blocking-error branch emits error_mode blocking_error. Both emits are awaited before the process.exit paths so the 2s-capped CLI POST survives; hook_type is a closed enum registered at hookCommand entry. Exit codes unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(build): regenerate plugin artifacts with Plan 14 telemetry signals Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(tests): make PostHog client regression test order-independent via global preload mock The disableGeoip regression test mocked posthog-node per-file, but telemetry.ts is imported transitively by many test files in the shared bun process, so the mock registered too late and the test failed in full-suite runs — CI on main has been red since v13.5.4. The mock now registers in a bunfig [test].preload before any module loads, which also guarantees test runs can never construct a real PostHog client and flush fabricated events into production analytics (consent is default-on and the suite outlives flushInterval). telemetry.ts gains a test-only state reset so construction is observed deterministically regardless of suite order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(telemetry): forward shutdown reason in Windows-managed IPC message Review follow-up: the wrapper IPC path discarded the restart tag, so an external Windows wrapper could only ever report shutdown_reason 'stop'. No wrapper in this repo listens for the message, but the reason now travels with it for any that does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 02:51:45 -07:00
${e}
${n}`;let o=r.indexOf(t),i=r.indexOf(n);return o!==-1&&i!==-1?r.substring(0,o)+`${t}
feat(telemetry): reliability signals — retrieval quality, compression trust, worker lifecycle, hook failures (Plan 14) (#2874) * feat(telemetry): disclose 19 reliability-signal fields and 2 new events across all surfaces Whitelist (scrub.ts), scrub tests, public docs (telemetry.mdx), and CLI disclosure (COLLECTED_FIELDS/EVENT_NAMES) for the Plan 14 reliability signals: search retrieval quality, compression trust, worker lifecycle, and hook failure keys, plus the worker_stopped and hook_failed events. Includes the plan document. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): retrieval-quality signals on search_performed SearchManager.search() fills an optional telemetry envelope (result_count, search_strategy, chroma_available, fallback_reason) across all three search paths; handlers stash it on res.locals.searchTelemetry and the existing finish-middleware spreads it into the search_performed capture. Zero-result searches report result_count: 0; Chroma fallback reasons are a closed enum, never the error message. Response shapes unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): compression trust signals on session_compressed fabrication_detected/fabricated_count flow through compressionProps (all three emit paths); invalid-output respawns emit a respawn-gated session_compressed with outcome invalid_output and the classifier value; aborted generators emit outcome aborted with abort_reason normalized to a closed enum in the .finally where all five abort flows converge (the .catch path can never observe a non-null abortReason). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): worker lifecycle signals — worker_stopped, crash detection, memory metrics Clean-shutdown sentinel written before telemetry flush and consumed at startup; worker_started gains previous_shutdown (crash/clean/unknown) and previous_uptime_seconds derived from the stale PID file; new worker_stopped event (uptime_seconds, shutdown_reason stop/restart/ signal) emitted before shutdownTelemetry(); the CLI restart path tags /api/admin/shutdown?reason=restart so restarts are distinguishable; buildLifecycleProps adds integer process_rss_mb/heap_used_mb. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): threshold-gated hook_failed distress signal via CLI transport recordWorkerUnreachable emits hook_failed exactly when the consecutive- failure count reaches the fail-loud threshold; the generic blocking-error branch emits error_mode blocking_error. Both emits are awaited before the process.exit paths so the 2s-capped CLI POST survives; hook_type is a closed enum registered at hookCommand entry. Exit codes unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(build): regenerate plugin artifacts with Plan 14 telemetry signals Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(tests): make PostHog client regression test order-independent via global preload mock The disableGeoip regression test mocked posthog-node per-file, but telemetry.ts is imported transitively by many test files in the shared bun process, so the mock registered too late and the test failed in full-suite runs — CI on main has been red since v13.5.4. The mock now registers in a bunfig [test].preload before any module loads, which also guarantees test runs can never construct a real PostHog client and flush fabricated events into production analytics (consent is default-on and the suite outlives flushInterval). telemetry.ts gains a test-only state reset so construction is observed deterministically regardless of suite order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(telemetry): forward shutdown reason in Windows-managed IPC message Review follow-up: the wrapper IPC path discarded the restart tag, so an external Windows wrapper could only ever report shutdown_reason 'stop'. No wrapper in this repo listens for the message, but the reason now travels with it for any that does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 02:51:45 -07:00
${e}
${n}`+r.substring(i+n.length):r+`
fix(build): add transcript-watcher.cjs build target (closes #2450) (#2592) * fix(build): add transcript-watcher.cjs build target (closes #2450) src/npx-cli/commands/runtime.ts:230 hard-codes a reference to plugin/scripts/transcript-watcher.cjs, but scripts/build-hooks.js never compiled this entry. The fallback `spawnBunWorkerCommand` path silently no-ops on systems where bun isn't reachable, so `claude-mem transcript watch` is effectively dead in the published npm bundle. This adds: - A thin entry (src/services/transcripts/transcript-watcher-entry.ts) that dispatches process.argv to the existing runTranscriptCommand function in cli.ts (which until now had no caller in the build). - A new esbuild target in build-hooks.js modeled on context-generator (Node 18 CJS, minified, bun:sqlite external) plus the standard stripHardcodedDirname pass and chmod 0755. - Updated build summary so the new artifact shows up in the "compiled successfully" footer. The compiled bundle is 137 KB. `node plugin/scripts/transcript-watcher.cjs` prints the usage line and exits 1 as expected; `... validate`, `... init`, and `... watch` all route correctly. No behavior change on platforms that previously had a working bun fallback - the explicit path now just wins the existsSync check. * fix(build): externalize zod + add bundle-size guard for transcript-watcher Addresses Greptile review on #2592: 1. Add `zod` to the external list. worker-service and server-beta-service both externalize zod and rely on plugin/package.json to provide it at runtime; the watcher build was inconsistent. The current processor.ts chain does not actually pull in zod (bundle size unchanged at 137.64 KB), but the external entry is defensive — any future import in src/services/transcripts/ that touches zod will now resolve against the plugin's declared dependency rather than getting silently inlined (and potentially creating a duplicate-instance hazard). 2. Add a 200 KB size guard mirroring the 600 KB ceiling on mcp-server.cjs. The watcher is meant to be a thin file-tail loop; if a transitive import ever drags a heavy module (worker-service, an SDK runtime) into the chain, this catches it at build time with an informative error pointing at processor.ts / watcher.ts. No functional change to the runtime artifact (size identical, behavior identical). Both follow-ups recommended by Greptile.
2026-06-06 14:21:25 +08:00
${t}
feat(telemetry): reliability signals — retrieval quality, compression trust, worker lifecycle, hook failures (Plan 14) (#2874) * feat(telemetry): disclose 19 reliability-signal fields and 2 new events across all surfaces Whitelist (scrub.ts), scrub tests, public docs (telemetry.mdx), and CLI disclosure (COLLECTED_FIELDS/EVENT_NAMES) for the Plan 14 reliability signals: search retrieval quality, compression trust, worker lifecycle, and hook failure keys, plus the worker_stopped and hook_failed events. Includes the plan document. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): retrieval-quality signals on search_performed SearchManager.search() fills an optional telemetry envelope (result_count, search_strategy, chroma_available, fallback_reason) across all three search paths; handlers stash it on res.locals.searchTelemetry and the existing finish-middleware spreads it into the search_performed capture. Zero-result searches report result_count: 0; Chroma fallback reasons are a closed enum, never the error message. Response shapes unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): compression trust signals on session_compressed fabrication_detected/fabricated_count flow through compressionProps (all three emit paths); invalid-output respawns emit a respawn-gated session_compressed with outcome invalid_output and the classifier value; aborted generators emit outcome aborted with abort_reason normalized to a closed enum in the .finally where all five abort flows converge (the .catch path can never observe a non-null abortReason). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): worker lifecycle signals — worker_stopped, crash detection, memory metrics Clean-shutdown sentinel written before telemetry flush and consumed at startup; worker_started gains previous_shutdown (crash/clean/unknown) and previous_uptime_seconds derived from the stale PID file; new worker_stopped event (uptime_seconds, shutdown_reason stop/restart/ signal) emitted before shutdownTelemetry(); the CLI restart path tags /api/admin/shutdown?reason=restart so restarts are distinguishable; buildLifecycleProps adds integer process_rss_mb/heap_used_mb. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(telemetry): threshold-gated hook_failed distress signal via CLI transport recordWorkerUnreachable emits hook_failed exactly when the consecutive- failure count reaches the fail-loud threshold; the generic blocking-error branch emits error_mode blocking_error. Both emits are awaited before the process.exit paths so the 2s-capped CLI POST survives; hook_type is a closed enum registered at hookCommand entry. Exit codes unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(build): regenerate plugin artifacts with Plan 14 telemetry signals Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(tests): make PostHog client regression test order-independent via global preload mock The disableGeoip regression test mocked posthog-node per-file, but telemetry.ts is imported transitively by many test files in the shared bun process, so the mock registered too late and the test failed in full-suite runs — CI on main has been red since v13.5.4. The mock now registers in a bunfig [test].preload before any module loads, which also guarantees test runs can never construct a real PostHog client and flush fabricated events into production analytics (consent is default-on and the suite outlives flushInterval). telemetry.ts gains a test-only state reset so construction is observed deterministically regardless of suite order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(telemetry): forward shutdown reason in Windows-managed IPC message Review follow-up: the wrapper IPC path discarded the restart tag, so an external Windows wrapper could only ever report shutdown_reason 'stop'. No wrapper in this repo listens for the message, but the reason now travels with it for any that does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 02:51:45 -07:00
${e}
2026-08-02 10:14:45 -07:00
${n}`}f();function Br(r,e){if(!r)return;let t=(0,qe.resolve)(r);if(t.includes("/.git/")||t.includes("\\.git\\")||t.endsWith("/.git")||t.endsWith("\\.git"))return;let n=(0,qe.dirname)(r);(0,I.existsSync)(n)||(0,I.mkdirSync)(n,{recursive:!0});let o="";(0,I.existsSync)(r)&&(o=(0,I.readFileSync)(r,"utf-8"));let i=`# Memory Context
fix(build): add transcript-watcher.cjs build target (closes #2450) (#2592) * fix(build): add transcript-watcher.cjs build target (closes #2450) src/npx-cli/commands/runtime.ts:230 hard-codes a reference to plugin/scripts/transcript-watcher.cjs, but scripts/build-hooks.js never compiled this entry. The fallback `spawnBunWorkerCommand` path silently no-ops on systems where bun isn't reachable, so `claude-mem transcript watch` is effectively dead in the published npm bundle. This adds: - A thin entry (src/services/transcripts/transcript-watcher-entry.ts) that dispatches process.argv to the existing runTranscriptCommand function in cli.ts (which until now had no caller in the build). - A new esbuild target in build-hooks.js modeled on context-generator (Node 18 CJS, minified, bun:sqlite external) plus the standard stripHardcodedDirname pass and chmod 0755. - Updated build summary so the new artifact shows up in the "compiled successfully" footer. The compiled bundle is 137 KB. `node plugin/scripts/transcript-watcher.cjs` prints the usage line and exits 1 as expected; `... validate`, `... init`, and `... watch` all route correctly. No behavior change on platforms that previously had a working bun fallback - the explicit path now just wins the existsSync check. * fix(build): externalize zod + add bundle-size guard for transcript-watcher Addresses Greptile review on #2592: 1. Add `zod` to the external list. worker-service and server-beta-service both externalize zod and rely on plugin/package.json to provide it at runtime; the watcher build was inconsistent. The current processor.ts chain does not actually pull in zod (bundle size unchanged at 137.64 KB), but the external entry is defensive — any future import in src/services/transcripts/ that touches zod will now resolve against the plugin's declared dependency rather than getting silently inlined (and potentially creating a duplicate-instance hazard). 2. Add a 200 KB size guard mirroring the 600 KB ceiling on mcp-server.cjs. The watcher is meant to be a thin file-tail loop; if a transitive import ever drags a heavy module (worker-service, an SDK runtime) into the chain, this catches it at build time with an informative error pointing at processor.ts / watcher.ts. No functional change to the runtime artifact (size identical, behavior identical). Both follow-ups recommended by Greptile.
2026-06-06 14:21:25 +08:00
2026-08-02 10:14:45 -07:00
${e}`,s=Kr(o,i),c=`${r}.tmp`;try{(0,I.writeFileSync)(c,s),(0,I.renameSync)(c,r)}catch(l){a.error("AGENTS_MD","Failed to write AGENTS.md",{agentsPath:r},l instanceof Error?l:new Error(String(l)))}}f();function Ho(r){let e=r.trim().replace(/^\$\.?/,"");if(!e)return[];let t=[],n=e.split(".");for(let o of n){let i=/([^[\]]+)|\[(\d+)\]/g,s;for(;(s=i.exec(o))!==null;)s[1]?t.push(s[1]):s[2]&&t.push(parseInt(s[2],10))}return t}function Ct(r,e){if(!e)return;let t=Ho(e),n=r;for(let o of t){if(n==null)return;n=n[o]}return n}function Jr(r){return r==null||r===""}function Vr(r,e){if(r.startsWith("$watch.")){let t=r.slice(7);return e.watch[t]}if(r.startsWith("$schema.")){let t=r.slice(8);return e.schema[t]}if(r.startsWith("$session.")){let t=r.slice(9);return e.session?e.session[t]:void 0}if(r==="$cwd")return e.watch.workspace;if(r==="$project")return e.watch.project}function ae(r,e,t){if(r!==void 0){if(typeof r=="string"){let n=Vr(r,t);return n!==void 0?n:Ct(e,r)}if(r.coalesce&&Array.isArray(r.coalesce))for(let n of r.coalesce){let o=ae(n,e,t);if(!Jr(o))return o}if(r.path){let n=Vr(r.path,t);if(n!==void 0)return n;let o=Ct(e,r.path);if(!Jr(o))return o}if(r.value!==void 0)return r.value;if(r.default!==void 0)return r.default}}function qr(r,e,t){let n={};if(!r)return n;for(let[o,i]of Object.entries(r))n[o]=ae(i,e,t);return n}function Xr(r,e,t){if(!e)return!0;let n=e.path||t.eventTypePath||"type",o=n?Ct(r,n):void 0,i=o==null||o==="";if(e.exists!==void 0&&(e.exists&&i||!e.exists&&!i)||e.equals!==void 0&&o!==e.equals||e.not_equals!==void 0&&o===e.not_equals||e.in&&Array.isArray(e.in)&&!e.in.includes(o)||e.not_in&&Array.isArray(e.not_in)&&e.not_in.includes(o)||e.contains!==void 0&&(typeof o!="string"||!o.includes(e.contains))||e.not_contains!==void 0&&typeof o=="string"&&o.includes(e.not_contains))return!1;if(e.regex)try{if(!new RegExp(e.regex).test(String(o??"")))return!1}catch(s){return a.debug("WORKER","Invalid regex in match rule",{regex:e.regex},s instanceof Error?s:void 0),!1}return!0}f();w();f();var Xe=class{static checkUserPromptPrivacy(e,t,n,o,i,s){let c=e.getUserPrompt(t,n,i);return c===null?(a.warn("HOOK",`${o}: no user_prompts row for prompt #${n} \u2014 ingesting anyway (session-init likely raced worker boot; see #2794/#2795)`,{sessionId:i,contentSessionId:t,promptNumber:n,...s}),{allow:!0,prompt:""}):c.trim()===""?(a.debug("HOOK",`Skipping ${o} - user prompt was entirely private`,{sessionId:i,promptNumber:n,...s}),{allow:!1,reason:"private"}):{allow:!0,prompt:c}}};var zr=null;function jo(){if(!zr)throw new Error("ingest helpers used before setIngestContext() \u2014 wiring bug");return zr}async function Qr(r){let{sessionManager:e,dbManager:t,eventBroadcaster:n,ensureGeneratorRunning:o}=jo(),i=x(r.platformSource),s=typeof r.cwd=="string"?r.cwd:"",c=s.trim()?Q(s).primary:"",l=v.loadFromFile(re);if(s&&Be(s,l.CLAUDE_MEM_EXCLUDED_PROJECTS))return{ok:!0,status:"skipped",reason:"project_excluded"};if(new Set(l.CLAUDE_MEM_SKIP_TOOLS.split(",").map(A=>A.trim()).filter(Boolean)).has(r.toolName))return{ok:!0,status:"skipped",reason:"tool_excluded"};if(new Set(["Edit","Write","Read","NotebookEdit"]).has(r.toolName)&&r.toolInput&&typeof r.toolInput=="object"){let A=r.toolInput,q=A.file_path||A.notebook_path;if(q&&q.includes("session-memory"))return{ok:!0,status:"skipped",reason:"session_memory_meta"}}let E=t.getSessionStore(),g,M;try{g=E.createSDKSession(r.contentSessionId,c,"",void 0,i),M=E.getPromptNumberFromUserPrompts(r.contentSessionId,g)}catch(A){let q=A instanceof Error?A.message:String(A);return a.error("INGEST","Observation session resolution failed",{contentSessionId:r.contentSessionId,toolName:r.toolName},A instanceof Error?A:new Error(q)),{ok:!1,reason:q,status:500}}if(!Xe.checkUserPromptPrivacy(E,r.contentSessionId,M,"observation",g,{tool_name:r.toolName}).allow)return{ok:!0,status:"skipped",reason:"private"};let Y=r.toolInput!==void 0?wt(JSON.stringify(r.toolInput)):"{}",_e=r.toolResponse!==void 0?wt(JSON.stringify(r.toolResponse)):"{}";return await e.queueObservation(g,{tool_name:r.toolName,tool_input:Y,tool_response:_e,pro
`);for(let o of n){let i=o.trim();if(i.startsWith("*** Update File: "))t.push(i.replace("*** Update File: ","").trim());else if(i.startsWith("*** Add File: "))t.push(i.replace("*** Add File: ","").trim());else if(i.startsWith("*** Delete File: "))t.push(i.replace("*** Delete File: ","").trim());else if(i.startsWith("*** Move to: "))t.push(i.replace("*** Move to: ","").trim());else if(i.startsWith("+++ ")){let s=i.replace("+++ ","").replace(/^b\//,"").trim();s&&s!=="/dev/null"&&t.push(s)}}return Array.from(new Set(t))}async handleSessionEnd(e,t){await this.queueSummary(e),await this.updateContext(e,t),e.pendingTools?.clear();let n=this.getSessionKey(t,e.sessionId);this.sessions.delete(n)}async queueSummary(e){if(!await $e())return;let n=e.lastAssistantMessage??"",o=JSON.stringify({contentSessionId:e.sessionId,last_assistant_message:n,platformSource:e.platformSource});try{await $("/api/sessions/summarize",{method:"POST",headers:{"Content-Type":"application/json"},body:o})}catch(i){a.warn("TRANSCRIPT","Summary request failed",{error:i instanceof Error?i.message:String(i)})}}async updateContext(e,t){if(!t.context||t.context.mode!=="agents"||Dt(t)||!await $e())return;let o=e.cwd??t.workspace;if(!o)return;let s=Q(o).allProjects.join(","),c=`/api/context/inject?projects=${encodeURIComponent(s)}&platformSource=${encodeURIComponent(e.platformSource)}`,l=b(t.context.path??`${o}/AGENTS.md`),d=Ee.default.resolve(l),u=[Ee.default.resolve(o),Ee.default.resolve(S)];if(!u.some(_=>d.startsWith(_+Ee.default.sep)||d===_)){a.warn("SECURITY","Rejected path traversal attempt in watch.context.path",{original:t.context.path,resolved:d,allowedRoots:u});return}let g;try{g=await $(c)}catch(_){a.warn("TRANSCRIPT","Failed to fetch AGENTS.md context",{error:_ instanceof Error?_.message:String(_)});return}if(!g.ok)return;let M=(await g.text()).trim();M&&(Br(l,M),a.debug("TRANSCRIPT","Updated AGENTS.md context",{agentsPath:l,watch:t.name}))}};var bt=class{constructor(e,t,n,o){this.filePath=e;this.onLine=n;this.onOffset=o;this.tailState={offset:t,partial:""}}filePath;onLine;onOffset;watcher=null;tailState;start(){this.readNewData().catch(()=>{}),this.watcher=(0,C.watch)(this.filePath,{persistent:!0},()=>{this.readNewData().catch(()=>{})})}close(){this.watcher?.close(),this.watcher=null}poke(){this.readNewData().catch(()=>{})}async readNewData(){if(!(0,C.existsSync)(this.filePath))return;let e=0;try{e=(0,C.statSync)(this.filePath).size}catch(s){a.debug("WORKER","Failed to stat transcript file",{file:this.filePath},s instanceof Error?s:void 0);return}if(e<this.tailState.offset&&(this.tailState.offset=0),e===this.tailState.offset)return;let t=(0,C.createReadStream)(this.filePath,{start:this.tailState.offset,end:e-1,encoding:"utf8"}),n="";for await(let s of t)n+=s;this.tailState.offset=e,this.onOffset(this.tailState.offset);let i=(this.tailState.partial+n).split(`
`);this.tailState.partial=i.pop()??"";for(let s of i){let c=s.trim();c&&await this.onLine(c)}}},Qe=class{constructor(e,t){this.config=e;this.statePath=t;this.state=xt(t)}config;statePath;processor=new ze;tailers=new Map;state;rootWatchers=[];async start(){for(let e of this.config.watches)await this.setupWatch(e)}stop(){for(let e of this.tailers.values())e.close();this.tailers.clear();for(let e of this.rootWatchers)e.close();this.rootWatchers=[]}async setupWatch(e){let t=this.resolveSchema(e);if(!t){a.warn("TRANSCRIPT","Missing schema for watch",{watch:e.name});return}let n=b(e.path),o=this.resolveWatchFiles(n);for(let s of o)await this.addTailer(s,e,t);let i=this.deepestNonGlobAncestor(n);if(!i||!(0,C.existsSync)(i)){a.debug("TRANSCRIPT","Watch root does not exist, skipping fs.watch",{watch:e.name,watchRoot:i});return}try{let s=(0,C.watch)(i,{recursive:!0,persistent:!0},(c,l)=>{this.handleRootWatchEvent(i,n,e,t,l)});this.rootWatchers.push(s),a.info("TRANSCRIPT","Watching transcript root recursively",{watch:e.name,watchRoot:i})}catch(s){a.warn("TRANSCRIPT","Failed to start recursive fs.watch on transcript root",{watch:e.name,watchRoot:i},s instanceof Error?s:void 0)}}handleRootWatchEvent(e,t,n,o,i){if(!i)return;let s=(0,O.resolve)(e,i).replace(/\\/g,"/"),c=this.tailers.get(s);if(c){c.poke();return}let l=this.resolveWatchFiles(t);for(let d of l)this.tailers.has(d)||this.addTailer(d,n,o)}deepestNonGlobAncestor(e){if(!this.hasGlob(e)){if((0,C.existsSync)(e))try{return(0,C.statSync)(e).isDirectory()?e:(0,O.resolve)(e,"..")}catch(o){return a.debug("TRANSCRIPT","Failed to stat watch path ancestor, falling back to parent directory",{path:e},o instanceof Error?o:new Error(String(o))),(0,O.resolve)(e,"..")}return e}let t=e.split(/[/\\]/),n=[];for(let o of t){if(/[*?[\]{}()]/.test(o))break;n.push(o)}return n.length===0||n.length===1&&n[0]===""?"":n.join(O.sep)}resolveSchema(e){return typeof e.schema=="string"?this.config.schemas?.[e.schema]??null:e.schema}resolveWatchFiles(e){if(this.hasGlob(e))return this.scanGlob(this.normalizeGlobPattern(e));if((0,C.existsSync)(e))try{if((0,C.statSync)(e).isDirectory()){let n=(0,O.join)(e,"**","*.jsonl");return this.scanGlob(this.normalizeGlobPattern(n))}return[e]}catch(t){return a.debug("WORKER","Failed to stat watch path",{path:e},t instanceof Error?t:void 0),[]}return[]}scanGlob(e){return Array.from(new Bun.Glob(e).scanSync({absolute:!0,onlyFiles:!0}))}normalizeGlobPattern(e){return e.replace(/\\/g,"/")}hasGlob(e){return/[*?[\]{}()]/.test(e)}async addTailer(e,t,n){if(this.tailers.has(e))return;let o=this.extractSessionIdFromPath(e),i=this.state.offsets[e]??0;if(i===0&&t.startAtEnd)try{i=(0,C.statSync)(e).size}catch(c){a.debug("WORKER","Failed to stat file for startAtEnd offset",{file:e},c instanceof Error?c:void 0),i=0}let s=new bt(e,i,async c=>{await this.handleLine(c,t,n,e,o)},c=>{this.state.offsets[e]=c,Nt(this.statePath,this.state)});s.start(),this.tailers.set(e,s),a.info("TRANSCRIPT","Watching transcript file",{file:e,watch:t.name,schema:n.name})}async handleLine(e,t,n,o,i){try{let s=JSON.parse(e);await this.processor.processEntry(s,t,n,i??void 0)}catch(s){s instanceof Error?a.debug("TRANSCRIPT","Failed to parse transcript line",{watch:t.name,file:(0,O.basename)(o)},s):a.warn("TRANSCRIPT","Failed to parse transcript line (non-Error thrown)",{watch:t.name,file:(0,O.basename)(o),error:String(s)})}}extractSessionIdFromPath(e){let t=e.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);return t?t[0]:null}};function vt(r,e){let t=r.indexOf(e);return t===-1?null:r[t+1]??null}async function Zr(r,e){switch(r){case"init":{let t=vt(e,"--config")??ne;return Ce(t),console.log(`Created sample config: ${b(t)}`),0}case"watch":{let t=vt(e,"--config")??ne,n;try{n=pe(t)}catch(c){if(c instanceof Error&&c.message.includes("not found"))Ce(t),console.log(`Created sample config: ${b(t)}`),n=pe(t);else throw c}let o=b(n.stateFile??Me),i=new Qe(n,o);await i.start(),console.log("Transcript watcher running. Press Ctrl+C to stop.");let s=()=>{i.stop(),process.exit(0)};return process.on("SIGINT",