@clauderecallhq/cli 0.95.11 → 0.95.12

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.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /* Claude Recall (proprietary). See LICENSE for terms. */
3
- var vf=Object.defineProperty;var se=(e,t)=>()=>(e&&(t=e(e=0)),t);var Tt=(e,t)=>{for(var n in t)vf(e,n,{get:t[n],enumerable:!0})};import{homedir as Xa}from"node:os";import{join as Xr,basename as mx}from"node:path";import{existsSync as If,mkdirSync as Mf,chmodSync as Df,readdirSync as fx,statSync as _x}from"node:fs";function z(){If(B)||Mf(B,{recursive:!0,mode:448}),process.platform!=="win32"&&Df(B,448)}var an,B,ze,Q=se(()=>{"use strict";an=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Xr(Xa(),".claude","projects"),B=process.env.RECALL_HOME?process.env.RECALL_HOME:Xr(Xa(),".recall"),ze=Xr(B,"db.sqlite")});import{createRequire as r_}from"node:module";var o_,i_,a_,Yr,zr,es,Kr=se(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...n){let s=n[0];return t==="warning"&&s instanceof Error&&s.name==="ExperimentalWarning"&&/SQLite/i.test(s.message)?!1:e(t,...n)}}o_=r_(import.meta.url),i_=["node","sqlite"].join(":"),a_=o_(i_),Yr=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let n=t.length===0?this.inner.run():this.inner.run(...t);return{changes:n.changes,lastInsertRowid:n.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},zr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new a_.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new Yr(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,n={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(n.simple){let s=this.inner.prepare(`PRAGMA ${t}`).get();return s&&typeof s=="object"?Object.values(s)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...s)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...s);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,n){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),n===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,n)}},es=zr});function oc(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(A=>A.name)),s=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[A,v]of s)n.has(A)||e.exec(`ALTER TABLE sessions ADD COLUMN ${A} ${v}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(A=>A.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[A,v]of a)o.has(A)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${A} ${v}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(A=>A.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[A,v]of d)u.has(A)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${A} ${v}`);let m=e.prepare("PRAGMA table_info(threads)").all();new Set(m.map(A=>A.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let f=e.prepare("PRAGMA table_info(thread_folders)").all(),b=new Set(f.map(A=>A.name));b.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),b.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
3
+ var $f=Object.defineProperty;var se=(e,t)=>()=>(e&&(t=e(e=0)),t);var Tt=(e,t)=>{for(var n in t)$f(e,n,{get:t[n],enumerable:!0})};import{homedir as Ka}from"node:os";import{join as Gr,basename as yx}from"node:path";import{existsSync as Uf,mkdirSync as Bf,chmodSync as Hf,readdirSync as Rx,statSync as kx}from"node:fs";function z(){Uf(H)||Bf(H,{recursive:!0,mode:448}),process.platform!=="win32"&&Hf(H,448)}var an,H,ze,Q=se(()=>{"use strict";an=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Gr(Ka(),".claude","projects"),H=process.env.RECALL_HOME?process.env.RECALL_HOME:Gr(Ka(),".recall"),ze=Gr(H,"db.sqlite")});import{createRequire as d_}from"node:module";var p_,m_,g_,Kr,Vr,es,Qr=se(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...n){let s=n[0];return t==="warning"&&s instanceof Error&&s.name==="ExperimentalWarning"&&/SQLite/i.test(s.message)?!1:e(t,...n)}}p_=d_(import.meta.url),m_=["node","sqlite"].join(":"),g_=p_(m_),Kr=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let n=t.length===0?this.inner.run():this.inner.run(...t);return{changes:n.changes,lastInsertRowid:n.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},Vr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new g_.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new Kr(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,n={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(n.simple){let s=this.inner.prepare(`PRAGMA ${t}`).get();return s&&typeof s=="object"?Object.values(s)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...s)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...s);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,n){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),n===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,n)}},es=Vr});function uc(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(A=>A.name)),s=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[A,v]of s)n.has(A)||e.exec(`ALTER TABLE sessions ADD COLUMN ${A} ${v}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(A=>A.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[A,v]of a)o.has(A)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${A} ${v}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(A=>A.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[A,v]of d)u.has(A)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${A} ${v}`);let m=e.prepare("PRAGMA table_info(threads)").all();new Set(m.map(A=>A.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let f=e.prepare("PRAGMA table_info(thread_folders)").all(),b=new Set(f.map(A=>A.name));b.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),b.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
4
4
  CREATE TABLE IF NOT EXISTS message_embeddings (
5
5
  message_uuid TEXT PRIMARY KEY REFERENCES messages(uuid) ON DELETE CASCADE,
6
6
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
@@ -12,7 +12,7 @@ var vf=Object.defineProperty;var se=(e,t)=>()=>(e&&(t=e(e=0)),t);var Tt=(e,t)=>{
12
12
  );
13
13
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_session ON message_embeddings(session_id);
14
14
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_generated ON message_embeddings(generated_at DESC);
15
- `);let T=e.prepare("PRAGMA table_info(projects)").all(),S=new Set(T.map(A=>A.name)),w=[["repo_root","TEXT"],["main_repo","TEXT"],["is_repo","INTEGER NOT NULL DEFAULT 0"],["is_worktree","INTEGER NOT NULL DEFAULT 0"]];for(let[A,v]of w)S.has(A)||e.exec(`ALTER TABLE projects ADD COLUMN ${A} ${v}`)}var rc,ic=se(()=>{"use strict";rc=`
15
+ `);let T=e.prepare("PRAGMA table_info(projects)").all(),S=new Set(T.map(A=>A.name)),R=[["repo_root","TEXT"],["main_repo","TEXT"],["is_repo","INTEGER NOT NULL DEFAULT 0"],["is_worktree","INTEGER NOT NULL DEFAULT 0"]];for(let[A,v]of R)S.has(A)||e.exec(`ALTER TABLE projects ADD COLUMN ${A} ${v}`)}var lc,dc=se(()=>{"use strict";lc=`
16
16
  PRAGMA journal_mode = WAL;
17
17
  PRAGMA synchronous = NORMAL;
18
18
  PRAGMA foreign_keys = ON;
@@ -749,13 +749,13 @@ CREATE TABLE IF NOT EXISTS file_cursor (
749
749
  mtime REAL,
750
750
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
751
751
  );
752
- `});function ac(e,t){let n=t.RECALL_DB_PROFILE;if(n&&c_.has(n))return n;let s=(e??"").split(/[\\/]/).pop()??"";return s==="mcp-server.js"||s==="claude-recall-mcp"?"light":s==="query-worker.js"?"worker":"full"}function cc(e){let t=[["busy_timeout",15e3],["journal_size_limit",67108864],["wal_autocheckpoint",1e3]];return e==="full"?[["cache_size",-64e3],["mmap_size",268435456],...t]:e==="worker"?[["cache_size",-16e3],["mmap_size",0],...t]:[["cache_size",-4e3],["mmap_size",0],...t]}var c_,lc=se(()=>{"use strict";c_=new Set(["light","full","worker"])});import*as dc from"sqlite-vec";function at(e){let t=e;uc.has(t)||(dc.load(e),uc.add(t))}function pc(e){e.prepare("SELECT 1 FROM sqlite_master WHERE name = 'vec_chunks'").get()||(at(e),e.exec(l_))}var uc,l_,ts=se(()=>{"use strict";uc=new WeakSet;l_=`
752
+ `});function pc(e,t){let n=t.RECALL_DB_PROFILE;if(n&&f_.has(n))return n;let s=(e??"").split(/[\\/]/).pop()??"";return s==="mcp-server.js"||s==="claude-recall-mcp"?"light":s==="query-worker.js"?"worker":"full"}function mc(e){let t=[["busy_timeout",15e3],["journal_size_limit",67108864],["wal_autocheckpoint",1e3]];return e==="full"?[["cache_size",-64e3],["mmap_size",268435456],...t]:e==="worker"?[["cache_size",-16e3],["mmap_size",0],...t]:[["cache_size",-4e3],["mmap_size",0],...t]}var f_,gc=se(()=>{"use strict";f_=new Set(["light","full","worker"])});import*as _c from"sqlite-vec";function at(e){let t=e;fc.has(t)||(_c.load(e),fc.add(t))}function hc(e){e.prepare("SELECT 1 FROM sqlite_master WHERE name = 'vec_chunks'").get()||(at(e),e.exec(__))}var fc,__,ts=se(()=>{"use strict";fc=new WeakSet;__=`
753
753
  CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
754
754
  embedding float[768] distance_metric=cosine
755
- );`});function h(){if(Te)return Te;z();let e=ac(process.argv[1],process.env);u_=e,Te=new es(ze);for(let[t,n]of cc(e))Te.pragma(`${t} = ${n}`);Te.pragma("temp_store = MEMORY"),e!=="light"&&at(Te),Te.exec(rc),oc(Te),pc(Te);try{Te.exec("PRAGMA optimize")}catch{}return Te}var Te,u_,U=se(()=>{"use strict";Kr();Q();ic();lc();ts();Te=null,u_="full"});import{existsSync as __}from"node:fs";import{dirname as kc}from"node:path";import{fileURLToPath as h_}from"node:url";function yt(){if(ns)return ns;let e=kc(h_(import.meta.url));for(;!__(`${e}/package.json`);){let t=kc(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return ns=e,ns}var ns,ss=se(()=>{"use strict";ns=null});function rs(e){let t=null;return()=>t||(t=(async()=>{try{return await e()}finally{t=null}})(),t)}var Qr=se(()=>{"use strict"});function E_(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) - or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
756
- `)}var os,ye,un=se(()=>{"use strict";os="BAAI/bge-base-en-v1.5",ye=class extends Error{kind;path;underlying;cause;constructor(t){super(E_(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var cs={};Tt(cs,{embed:()=>x_,embedQuery:()=>N_,getEmbedderStatus:()=>A_,loadEmbedder:()=>k_,unloadEmbedder:()=>O_});import{Worker as b_}from"node:worker_threads";import{join as S_}from"node:path";import{existsSync as T_}from"node:fs";function y_(){return S_(yt(),"dist","daemon","embedder-worker.js")}function Ac(e){for(let t of dn.values())t.reject(e);dn.clear()}function w_(){if(Ue)return Ue;let e=y_();if(!T_(e))throw new ye({kind:"bundle-missing",detail:"embedder-worker bundle not found - run `npm run build:cli` to emit it",path:e});let t=new b_(e);return t.on("message",n=>{let s=dn.get(n.id);s&&(dn.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker] thread error:",n);let s=n instanceof Error?n:new Error(String(n));Ac(s),Ue=null,Ne=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),Ac(new Error(`embedder worker exited with code ${n}`))),Ue=null,Ne=!1}),Ue=t,t}function is(e){return new Promise((t,n)=>{let s;try{s=w_()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}dn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function as(){return Zr=Zr+1>>>0,String(Zr)}function to(e){if(!e.ok)throw e.unavailable?new ye({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function k_(){if(!(Ne&&Ue))try{await R_(),Ne=!0}catch(e){throw Ne=!1,e}}function A_(){return{loaded:Ne,modelId:os,dim:768}}async function x_(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return to(await is({id:as(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function N_(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=to(await is({id:as(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function O_(){if(!Ue){Ne=!1;return}try{await is({id:as(),type:"unload"})}catch{}Ne=!1;let e=Ue;Ue=null;try{await e.terminate()}catch{}}var Ue,dn,Zr,Ne,R_,xc=se(()=>{"use strict";ss();Qr();un();Ue=null,dn=new Map,Zr=0,Ne=!1;R_=rs(async()=>{to(await is({id:as(),type:"load"}))})});var ro={};Tt(ro,{LLAMACPP_MODEL_ID:()=>Oc,MODEL_ID:()=>os,embed:()=>F_,embedQuery:()=>$_,getEmbedderStatus:()=>P_,loadEmbedder:()=>j_,unloadEmbedder:()=>U_});import{Worker as L_}from"node:worker_threads";import{join as C_}from"node:path";import{existsSync as v_}from"node:fs";function I_(){return C_(yt(),"dist","daemon","embedder-worker-llamacpp.js")}function Nc(e){for(let t of pn.values())t.reject(e);pn.clear()}function M_(){if(He)return He;let e=I_();if(!v_(e))throw new ye({kind:"bundle-missing",detail:"embedder-worker-llamacpp bundle not found - run `npm run build:cli` to emit it",path:e});let t=new L_(e);return t.on("message",n=>{let s=pn.get(n.id);s&&(pn.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker-llamacpp] thread error:",n);let s=n instanceof Error?n:new Error(String(n));Nc(s),He=null,Oe=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),Nc(new Error(`embedder worker exited with code ${n}`))),He=null,Oe=!1}),He=t,t}function ls(e){return new Promise((t,n)=>{let s;try{s=M_()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}pn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function us(){return no=no+1>>>0,String(no)}function so(e){if(!e.ok)throw e.unavailable?new ye({kind:"platform-unsupported",detail:"embedder worker reports the llama.cpp runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function j_(){if(!(Oe&&He))try{await D_(),Oe=!0}catch(e){throw Oe=!1,e}}function P_(){return{loaded:Oe,modelId:Oc,dim:768}}async function F_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return so(await ls({id:us(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function $_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=so(await ls({id:us(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function U_(){if(!He){Oe=!1;return}try{await ls({id:us(),type:"unload"})}catch{}Oe=!1;let e=He;He=null;try{await e.terminate()}catch{}}var Oc,He,pn,no,Oe,D_,Lc=se(()=>{"use strict";ss();Qr();un();un();Oc="BAAI/bge-base-en-v1.5-gguf-q8_0";He=null,pn=new Map,no=0,Oe=!1;D_=rs(async()=>{so(await ls({id:us(),type:"load"}))})});var Cc={};Tt(Cc,{EmbedderUnavailableError:()=>ye,embed:()=>Ve,embedQuery:()=>B_,getEmbedderStatus:()=>le,loadEmbedder:()=>Be,unloadEmbedder:()=>W_});function H_(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?ro:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
757
- `),cs)}var mn,Be,le,Ve,B_,W_,We=se(()=>{"use strict";xc();Lc();un();mn=H_(),Be=mn.loadEmbedder,le=mn.getEmbedderStatus,Ve=mn.embed,B_=mn.embedQuery,W_=mn.unloadEmbedder});import{writeFileSync as qh}from"node:fs";import{join as Xh}from"node:path";function ut(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Rt(e,t){let n=ut(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=h(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?{tag:n,added:!1}:(s.transaction(()=>{s.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,n,r),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,n,r)})(),Zc(),{tag:n,added:!0})}function Qc(e,t){let n=ut(t);if(!n)return{tag:"",removed:!1};let s=h(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?(s.transaction(()=>{s.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,n),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,n,r)})(),Zc(),{tag:n,removed:!0}):{tag:n,removed:!1}}function hn(e){return h().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function kt(){return h().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
758
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function Zc(){try{z();let e=h(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),n=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),s={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:n};qh(Jh,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var Jh,At=se(()=>{"use strict";U();Q();Jh=Xh(B,"tags.json")});function Gh(e,t){let n=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)s.add(Math.floor(o*r));return Array.from(s).sort((o,a)=>o-a).slice(0,t).map(o=>n[o])}function xt(e){let t=h(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{n[`sid_${u}`]=c})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
755
+ );`});function h(){if(Te)return Te;z();let e=pc(process.argv[1],process.env);h_=e,Te=new es(ze);for(let[t,n]of mc(e))Te.pragma(`${t} = ${n}`);Te.pragma("temp_store = MEMORY"),e!=="light"&&at(Te),Te.exec(lc),uc(Te),hc(Te);try{Te.exec("PRAGMA optimize")}catch{}return Te}var Te,h_,U=se(()=>{"use strict";Qr();Q();dc();gc();ts();Te=null,h_="full"});import{existsSync as w_}from"node:fs";import{dirname as Cc}from"node:path";import{fileURLToPath as R_}from"node:url";function yt(){if(ns)return ns;let e=Cc(R_(import.meta.url));for(;!w_(`${e}/package.json`);){let t=Cc(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return ns=e,ns}var ns,ss=se(()=>{"use strict";ns=null});function rs(e){let t=null;return()=>t||(t=(async()=>{try{return await e()}finally{t=null}})(),t)}var eo=se(()=>{"use strict"});function k_(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) - or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
756
+ `)}var os,ye,un=se(()=>{"use strict";os="BAAI/bge-base-en-v1.5",ye=class extends Error{kind;path;underlying;cause;constructor(t){super(k_(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var cs={};Tt(cs,{embed:()=>M_,embedQuery:()=>D_,getEmbedderStatus:()=>I_,loadEmbedder:()=>v_,unloadEmbedder:()=>j_});import{Worker as A_}from"node:worker_threads";import{join as x_}from"node:path";import{existsSync as N_}from"node:fs";function O_(){return x_(yt(),"dist","daemon","embedder-worker.js")}function Lc(e){for(let t of dn.values())t.reject(e);dn.clear()}function C_(){if(Ue)return Ue;let e=O_();if(!N_(e))throw new ye({kind:"bundle-missing",detail:"embedder-worker bundle not found - run `npm run build:cli` to emit it",path:e});let t=new A_(e);return t.on("message",n=>{let s=dn.get(n.id);s&&(dn.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker] thread error:",n);let s=n instanceof Error?n:new Error(String(n));Lc(s),Ue=null,Ne=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),Lc(new Error(`embedder worker exited with code ${n}`))),Ue=null,Ne=!1}),Ue=t,t}function is(e){return new Promise((t,n)=>{let s;try{s=C_()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}dn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function as(){return to=to+1>>>0,String(to)}function so(e){if(!e.ok)throw e.unavailable?new ye({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function v_(){if(!(Ne&&Ue))try{await L_(),Ne=!0}catch(e){throw Ne=!1,e}}function I_(){return{loaded:Ne,modelId:os,dim:768}}async function M_(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return so(await is({id:as(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function D_(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=so(await is({id:as(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function j_(){if(!Ue){Ne=!1;return}try{await is({id:as(),type:"unload"})}catch{}Ne=!1;let e=Ue;Ue=null;try{await e.terminate()}catch{}}var Ue,dn,to,Ne,L_,vc=se(()=>{"use strict";ss();eo();un();Ue=null,dn=new Map,to=0,Ne=!1;L_=rs(async()=>{so(await is({id:as(),type:"load"}))})});var io={};Tt(io,{LLAMACPP_MODEL_ID:()=>Mc,MODEL_ID:()=>os,embed:()=>X_,embedQuery:()=>J_,getEmbedderStatus:()=>q_,loadEmbedder:()=>W_,unloadEmbedder:()=>G_});import{Worker as P_}from"node:worker_threads";import{join as F_}from"node:path";import{existsSync as $_}from"node:fs";function U_(){return F_(yt(),"dist","daemon","embedder-worker-llamacpp.js")}function Ic(e){for(let t of pn.values())t.reject(e);pn.clear()}function B_(){if(Be)return Be;let e=U_();if(!$_(e))throw new ye({kind:"bundle-missing",detail:"embedder-worker-llamacpp bundle not found - run `npm run build:cli` to emit it",path:e});let t=new P_(e);return t.on("message",n=>{let s=pn.get(n.id);s&&(pn.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker-llamacpp] thread error:",n);let s=n instanceof Error?n:new Error(String(n));Ic(s),Be=null,Oe=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),Ic(new Error(`embedder worker exited with code ${n}`))),Be=null,Oe=!1}),Be=t,t}function ls(e){return new Promise((t,n)=>{let s;try{s=B_()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}pn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function us(){return ro=ro+1>>>0,String(ro)}function oo(e){if(!e.ok)throw e.unavailable?new ye({kind:"platform-unsupported",detail:"embedder worker reports the llama.cpp runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function W_(){if(!(Oe&&Be))try{await H_(),Oe=!0}catch(e){throw Oe=!1,e}}function q_(){return{loaded:Oe,modelId:Mc,dim:768}}async function X_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return oo(await ls({id:us(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function J_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=oo(await ls({id:us(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function G_(){if(!Be){Oe=!1;return}try{await ls({id:us(),type:"unload"})}catch{}Oe=!1;let e=Be;Be=null;try{await e.terminate()}catch{}}var Mc,Be,pn,ro,Oe,H_,Dc=se(()=>{"use strict";ss();eo();un();un();Mc="BAAI/bge-base-en-v1.5-gguf-q8_0";Be=null,pn=new Map,ro=0,Oe=!1;H_=rs(async()=>{oo(await ls({id:us(),type:"load"}))})});var jc={};Tt(jc,{EmbedderUnavailableError:()=>ye,embed:()=>Ve,embedQuery:()=>z_,getEmbedderStatus:()=>le,loadEmbedder:()=>He,unloadEmbedder:()=>K_});function Y_(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?io:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
757
+ `),cs)}var mn,He,le,Ve,z_,K_,We=se(()=>{"use strict";vc();Dc();un();mn=Y_(),He=mn.loadEmbedder,le=mn.getEmbedderStatus,Ve=mn.embed,z_=mn.embedQuery,K_=mn.unloadEmbedder});import{writeFileSync as Vh}from"node:fs";import{join as Qh}from"node:path";function ut(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Rt(e,t){let n=ut(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=h(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?{tag:n,added:!1}:(s.transaction(()=>{s.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,n,r),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,n,r)})(),rl(),{tag:n,added:!0})}function sl(e,t){let n=ut(t);if(!n)return{tag:"",removed:!1};let s=h(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?(s.transaction(()=>{s.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,n),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,n,r)})(),rl(),{tag:n,removed:!0}):{tag:n,removed:!1}}function hn(e){return h().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function kt(){return h().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
758
+ GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function rl(){try{z();let e=h(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),n=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),s={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:n};Vh(Zh,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var Zh,At=se(()=>{"use strict";U();Q();Zh=Qh(H,"tags.json")});function eE(e,t){let n=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)s.add(Math.floor(o*r));return Array.from(s).sort((o,a)=>o-a).slice(0,t).map(o=>n[o])}function xt(e){let t=h(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{n[`sid_${u}`]=c})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
759
759
  NULLIF(sa.alias, '') AS alias,
760
760
  COALESCE(s.first_user_message, '') AS first_user_message
761
761
  FROM sessions s
@@ -765,15 +765,15 @@ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
765
765
  ORDER BY COALESCE(s.started_at, '') DESC
766
766
  LIMIT @limit`).all(n).map(a=>{let c=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
767
767
  FROM messages WHERE session_id = ?
768
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=Gh(c,5).map(m=>`${m.role}: ${m.content_text.slice(0,400)}`).join(`
768
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=eE(c,5).map(m=>`${m.role}: ${m.content_text.slice(0,400)}`).join(`
769
769
  ---
770
- `);return{id:a.id,project:a.project,git_branch:a.git_branch,alias:a.alias,first_user_message:a.first_user_message,message_sample:d,current_tags:hn(a.id)}})}var fs=se(()=>{"use strict";U();At()});import{z as Ee}from"zod";function fo(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
771
- `)}function Kh(e){let t=e.mode==="detailed";return[`Summarize Claude Recall session ${e.sessionId} using the MCP tools available to you.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "condensed" to get the transcript as markdown.`,t?"2. Write a 1-paragraph overview (\u22643 sentences) of what this session was for, then 5-8 bullet points covering:":"2. Write 3-5 bullet points covering:"," - What was accomplished (shipped, decided, learned)"," - What was tried and abandoned"," - Any explicit open questions or follow-ups","","Rules:",'- Be concrete. Name files, functions, and decisions. Avoid vague "discussed X".',"- If nothing was actually shipped or decided, say so plainly.",'- Reply with just the summary \u2014 no preamble, no "Here is the summary:".'].join(`
772
- `)}function Qh(e){return[`Extract every architectural or product decision made in Claude Recall session ${e.sessionId}.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "full" (so tool I/O is included \u2014 sometimes the decision lives in a commit message or diff).`,"2. For each distinct decision, emit one block:",""," - **Decision:** one sentence."," - **Why:** the stated rationale (quote if possible).",' - **Alternatives considered:** bullet list, or "none mentioned".',' - **Where it landed:** file path / function name / commit SHA if identifiable, otherwise "TBD".',"","Rules:","- Include only decisions that were actually made, not ideas merely discussed.","- If an alternative was rejected, list it with a one-line reason.","- Group related decisions under a short heading when useful.",'- If the session made zero real decisions, reply exactly with: "No decisions made in this session."',"- No preamble, no closing, just the decision blocks."].join(`
773
- `)}function eE(e){let t=e.limit??5;return[`Find ${t} Recall sessions most similar to session ${e.sessionId}.`,"",`1. Call \`get_session\` with id "${e.sessionId}" \u2014 note its alias, first user message, tags, and git branch.`,'2. Derive 2-3 short search queries from that content (topic words, library names, error strings \u2014 NOT generic words like "fix" or "add").',`3. Call \`search\` once per query (limit: ${Math.max(5,t*2)}). Dedupe hits by session_id.`,"4. Also call `list_sessions` with the same tag(s) if the target session has any (pick the most specific tag).","5. Union the results. Exclude the target session itself. Rank by a mix of:"," - Shared tags (strongest signal)"," - Matching search hits across multiple queries"," - Recency as a tiebreaker","",`6. Return the top ${t} as a numbered list. For each:`," - **<short_id> \xB7 <project>** \u2014 <alias or first_user_message truncated>",' - One sentence on WHY it is similar (not a generic "same topic" \u2014 be specific).',"","If fewer than 2 genuinely-similar sessions exist, say so rather than padding with weak matches.","No preamble. Just the ranked list."].join(`
774
- `)}function el(e){return _o.find(t=>t.name===e)}var Yh,zh,Vh,Zh,tE,nE,sE,rE,_o,ho=se(()=>{"use strict";Yh={project:Ee.string().optional().describe("Exact project name match (optional)."),collectionId:Ee.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:Ee.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:Ee.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:Ee.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:Ee.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:Ee.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};zh={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:Ee.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};Vh={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};Zh={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:Ee.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};tE={name:"auto_tag_sessions",title:"Auto-tag Recall sessions",description:"Have the agent auto-tag Recall sessions using the Recall MCP tools. Scope can be restricted to a project, collection, or single session.",argsSchema:Yh,build:fo,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},nE={name:"summarize_session",title:"Summarize a session",description:"Produce a concise, concrete summary of one session \u2014 what shipped, what was tried, what's still open.",argsSchema:zh,build:Kh,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},sE={name:"extract_decisions",title:"Extract architectural decisions",description:"Scan a session and emit one structured block per architectural / product decision: what, why, alternatives, where it landed.",argsSchema:Vh,build:Qh,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},rE={name:"find_similar_sessions",title:"Find similar sessions",description:"Given a session, find other sessions that touched the same topic / library / error \u2014 ranked with reasons.",argsSchema:Zh,build:eE,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},_o=[tE,nE,sE,rE]});function _s(e,t){let n=En.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}function tl(e,t){let n=En.get(e);return n||(n=new Set,En.set(e,n)),n.add(t),()=>{let s=En.get(e);s&&(s.delete(t),s.size===0&&En.delete(e))}}var En,Eo=se(()=>{"use strict";En=new Map});import{existsSync as oE,statSync as iE}from"node:fs";import{delimiter as aE,join as cE}from"node:path";function Nt(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(aE).filter(Boolean),n=process.platform==="win32"?[`${e}.exe`,`${e}.cmd`,`${e}.bat`,e]:[e];for(let s of t)for(let r of n){let o=cE(s,r);try{if(oE(o)&&iE(o).isFile())return o}catch{}}return null}function hs(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var Es=se(()=>{"use strict"});var pt={};Tt(pt,{_resetClaudePathCacheForTests:()=>pE,buildScanPrompt:()=>sl,isClaudeCliAvailable:()=>me,runClaudeCliScan:()=>bo,spawnClaudePrompt:()=>dt});import{spawn as lE}from"node:child_process";function nl(){if(Ot!==void 0&&bn!==void 0)return{path:Ot,available:bn};let e=Nt("claude");return Ot=e??"claude",bn=e!==null,{path:Ot,available:bn}}function dE(){return nl().path}function me(){return nl().available}function pE(){Ot=void 0,bn=void 0}function sl(e){return fo({project:e.project,collectionId:e.collectionId,sessionId:e.sessionIds&&e.sessionIds.length===1?e.sessionIds[0]:void 0,untaggedOnly:e.untaggedOnly,limit:e.limit})}function mE(e,t){let n=t.get(e);return n||e.slice(0,8)}function gE(e){try{return xt(e).map(n=>({id:n.id,label:n.alias&&n.alias.trim().length>0?n.alias:n.first_user_message&&n.first_user_message.trim().length>0?n.first_user_message.slice(0,60):n.id.slice(0,8)}))}catch{return[]}}function fE(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return o=>{let a=o.trim();if(!a.startsWith("{"))return;let c;try{c=JSON.parse(a)}catch{return}if(!c||typeof c!="object")return;let u=c;if(!(u.type!=="assistant"||!u.message?.content))for(let d of u.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let m=d.input,f=typeof m?.sessionId=="string"?m.sessionId:null;!f||r.has(f)||(r.add(f),_s(t,{type:"progress",current:r.size,total:n,sessionId:f,sessionLabel:mE(f,s)}))}}}function _E(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
770
+ `);return{id:a.id,project:a.project,git_branch:a.git_branch,alias:a.alias,first_user_message:a.first_user_message,message_sample:d,current_tags:hn(a.id)}})}var fs=se(()=>{"use strict";U();At()});import{z as Ee}from"zod";function ho(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
771
+ `)}function sE(e){let t=e.mode==="detailed";return[`Summarize Claude Recall session ${e.sessionId} using the MCP tools available to you.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "condensed" to get the transcript as markdown.`,t?"2. Write a 1-paragraph overview (\u22643 sentences) of what this session was for, then 5-8 bullet points covering:":"2. Write 3-5 bullet points covering:"," - What was accomplished (shipped, decided, learned)"," - What was tried and abandoned"," - Any explicit open questions or follow-ups","","Rules:",'- Be concrete. Name files, functions, and decisions. Avoid vague "discussed X".',"- If nothing was actually shipped or decided, say so plainly.",'- Reply with just the summary \u2014 no preamble, no "Here is the summary:".'].join(`
772
+ `)}function oE(e){return[`Extract every architectural or product decision made in Claude Recall session ${e.sessionId}.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "full" (so tool I/O is included \u2014 sometimes the decision lives in a commit message or diff).`,"2. For each distinct decision, emit one block:",""," - **Decision:** one sentence."," - **Why:** the stated rationale (quote if possible).",' - **Alternatives considered:** bullet list, or "none mentioned".',' - **Where it landed:** file path / function name / commit SHA if identifiable, otherwise "TBD".',"","Rules:","- Include only decisions that were actually made, not ideas merely discussed.","- If an alternative was rejected, list it with a one-line reason.","- Group related decisions under a short heading when useful.",'- If the session made zero real decisions, reply exactly with: "No decisions made in this session."',"- No preamble, no closing, just the decision blocks."].join(`
773
+ `)}function aE(e){let t=e.limit??5;return[`Find ${t} Recall sessions most similar to session ${e.sessionId}.`,"",`1. Call \`get_session\` with id "${e.sessionId}" \u2014 note its alias, first user message, tags, and git branch.`,'2. Derive 2-3 short search queries from that content (topic words, library names, error strings \u2014 NOT generic words like "fix" or "add").',`3. Call \`search\` once per query (limit: ${Math.max(5,t*2)}). Dedupe hits by session_id.`,"4. Also call `list_sessions` with the same tag(s) if the target session has any (pick the most specific tag).","5. Union the results. Exclude the target session itself. Rank by a mix of:"," - Shared tags (strongest signal)"," - Matching search hits across multiple queries"," - Recency as a tiebreaker","",`6. Return the top ${t} as a numbered list. For each:`," - **<short_id> \xB7 <project>** \u2014 <alias or first_user_message truncated>",' - One sentence on WHY it is similar (not a generic "same topic" \u2014 be specific).',"","If fewer than 2 genuinely-similar sessions exist, say so rather than padding with weak matches.","No preamble. Just the ranked list."].join(`
774
+ `)}function ol(e){return Eo.find(t=>t.name===e)}var tE,nE,rE,iE,cE,lE,uE,dE,Eo,bo=se(()=>{"use strict";tE={project:Ee.string().optional().describe("Exact project name match (optional)."),collectionId:Ee.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:Ee.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:Ee.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:Ee.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:Ee.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:Ee.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};nE={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:Ee.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};rE={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};iE={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:Ee.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};cE={name:"auto_tag_sessions",title:"Auto-tag Recall sessions",description:"Have the agent auto-tag Recall sessions using the Recall MCP tools. Scope can be restricted to a project, collection, or single session.",argsSchema:tE,build:ho,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},lE={name:"summarize_session",title:"Summarize a session",description:"Produce a concise, concrete summary of one session \u2014 what shipped, what was tried, what's still open.",argsSchema:nE,build:sE,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},uE={name:"extract_decisions",title:"Extract architectural decisions",description:"Scan a session and emit one structured block per architectural / product decision: what, why, alternatives, where it landed.",argsSchema:rE,build:oE,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},dE={name:"find_similar_sessions",title:"Find similar sessions",description:"Given a session, find other sessions that touched the same topic / library / error \u2014 ranked with reasons.",argsSchema:iE,build:aE,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},Eo=[cE,lE,uE,dE]});function _s(e,t){let n=En.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}function il(e,t){let n=En.get(e);return n||(n=new Set,En.set(e,n)),n.add(t),()=>{let s=En.get(e);s&&(s.delete(t),s.size===0&&En.delete(e))}}var En,So=se(()=>{"use strict";En=new Map});import{existsSync as pE,statSync as mE}from"node:fs";import{delimiter as gE,join as fE}from"node:path";function Nt(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(gE).filter(Boolean),n=process.platform==="win32"?[`${e}.exe`,`${e}.cmd`,`${e}.bat`,e]:[e];for(let s of t)for(let r of n){let o=fE(s,r);try{if(pE(o)&&mE(o).isFile())return o}catch{}}return null}function hs(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var Es=se(()=>{"use strict"});var pt={};Tt(pt,{_resetClaudePathCacheForTests:()=>bE,buildScanPrompt:()=>cl,isClaudeCliAvailable:()=>me,runClaudeCliScan:()=>To,spawnClaudePrompt:()=>dt});import{spawn as _E}from"node:child_process";function al(){if(Ot!==void 0&&bn!==void 0)return{path:Ot,available:bn};let e=Nt("claude");return Ot=e??"claude",bn=e!==null,{path:Ot,available:bn}}function EE(){return al().path}function me(){return al().available}function bE(){Ot=void 0,bn=void 0}function cl(e){return ho({project:e.project,collectionId:e.collectionId,sessionId:e.sessionIds&&e.sessionIds.length===1?e.sessionIds[0]:void 0,untaggedOnly:e.untaggedOnly,limit:e.limit})}function SE(e,t){let n=t.get(e);return n||e.slice(0,8)}function TE(e){try{return xt(e).map(n=>({id:n.id,label:n.alias&&n.alias.trim().length>0?n.alias:n.first_user_message&&n.first_user_message.trim().length>0?n.first_user_message.slice(0,60):n.id.slice(0,8)}))}catch{return[]}}function yE(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return o=>{let a=o.trim();if(!a.startsWith("{"))return;let c;try{c=JSON.parse(a)}catch{return}if(!c||typeof c!="object")return;let u=c;if(!(u.type!=="assistant"||!u.message?.content))for(let d of u.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let m=d.input,f=typeof m?.sessionId=="string"?m.sessionId:null;!f||r.has(f)||(r.add(f),_s(t,{type:"progress",current:r.size,total:n,sessionId:f,sessionLabel:SE(f,s)}))}}}function wE(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
775
775
  `);for(;s!==-1;){let r=t.slice(0,s);t=t.slice(s+1),r.length>0&&e(r),s=t.indexOf(`
776
- `)}}}async function bo(e,t={},n){let s=!!t.scanId,r=s?gE(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return s&&t.scanId&&(c=fE({scanId:t.scanId,total:a,labelTable:o})),rl({prompt:sl(e),allowedTools:uE.split(","),opts:t,onProgress:n,onStdoutLine:c,outputFormat:s?"stream-json":"json"})}async function dt(e,t,n={},s){return rl({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function rl(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return a==="stream-json"&&c.push("--verbose"),s.model&&c.push("--model",s.model),new Promise(u=>{let d=dE(),m=lE(d,c,{stdio:["ignore","pipe","pipe"],shell:hs(d)||process.platform==="win32"&&Ot==="claude"}),f=[],b=[],T=o?_E(o):void 0;m.stdout.on("data",w=>{f.push(w),T&&T(w)}),m.stderr.on("data",w=>{if(b.push(w),r){let A=w.toString("utf8").trim();A&&r(A)}});let S=setTimeout(()=>{m.kill("SIGKILL")},1800*1e3);m.on("close",w=>{clearTimeout(S),u({success:w===0,stdout:Buffer.concat(f).toString("utf8"),stderr:Buffer.concat(b).toString("utf8"),exitCode:w})}),m.on("error",w=>{clearTimeout(S),u({success:!1,stdout:"",stderr:String(w),exitCode:null})})})}var uE,Ot,bn,we=se(()=>{"use strict";fs();ho();Eo();Es();uE=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var wl={};Tt(wl,{RetentionConfigSchema:()=>xo,readRetentionConfig:()=>No,writeRetentionConfig:()=>xs});import{existsSync as El,mkdirSync as ob,readFileSync as ib,writeFileSync as ab}from"node:fs";import{homedir as cb}from"node:os";import{join as bl}from"node:path";import{z as ks}from"zod";function Sl(){return process.env.RECALL_HOME??bl(cb(),".recall")}function lb(){let e=Sl();El(e)||ob(e,{recursive:!0})}function Tl(){return bl(Sl(),"config.json")}function yl(){let e=Tl();if(!El(e))return{};try{return JSON.parse(ib(e,"utf8"))}catch(t){let n=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${n}`),{}}}function No(){let e=yl().retention;if(!e)return{...As};let t=xo.safeParse({...As,...e});return t.success?t.data:{...As}}function xs(e){lb();let t=yl(),n=xo.parse({...As,...t.retention??{},...e}),s={...t,retention:n};return ab(Tl(),JSON.stringify(s,null,2)),n}var xo,As,Oo=se(()=>{"use strict";xo=ks.object({autoArchiveEnabled:ks.boolean().default(!1),autoArchiveAfterDays:ks.number().int().min(7).max(3650).default(90),lastRunAt:ks.string().nullable().default(null)}),As={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Ce from"chalk";import{formatDistanceToNowStrict as nL,parseISO as sL}from"date-fns";var Re,Ns=se(()=>{"use strict";Re={dim:Ce.gray,bold:Ce.bold,project:Ce.cyan,user:Ce.blue,assistant:Ce.green,tool:Ce.magenta,warn:Ce.yellow,err:Ce.red,ok:Ce.green,accent:Ce.hex("#f97316")}});import{existsSync as ub}from"node:fs";import{join as db}from"node:path";function Tn(){if(Rl&&ub(Sn))return;z();let e=h(),t=Sn.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
776
+ `)}}}async function To(e,t={},n){let s=!!t.scanId,r=s?TE(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return s&&t.scanId&&(c=yE({scanId:t.scanId,total:a,labelTable:o})),ll({prompt:cl(e),allowedTools:hE.split(","),opts:t,onProgress:n,onStdoutLine:c,outputFormat:s?"stream-json":"json"})}async function dt(e,t,n={},s){return ll({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function ll(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return a==="stream-json"&&c.push("--verbose"),s.model&&c.push("--model",s.model),new Promise(u=>{let d=EE(),m=_E(d,c,{stdio:["ignore","pipe","pipe"],shell:hs(d)||process.platform==="win32"&&Ot==="claude"}),f=[],b=[],T=o?wE(o):void 0;m.stdout.on("data",R=>{f.push(R),T&&T(R)}),m.stderr.on("data",R=>{if(b.push(R),r){let A=R.toString("utf8").trim();A&&r(A)}});let S=setTimeout(()=>{m.kill("SIGKILL")},1800*1e3);m.on("close",R=>{clearTimeout(S),u({success:R===0,stdout:Buffer.concat(f).toString("utf8"),stderr:Buffer.concat(b).toString("utf8"),exitCode:R})}),m.on("error",R=>{clearTimeout(S),u({success:!1,stdout:"",stderr:String(R),exitCode:null})})})}var hE,Ot,bn,we=se(()=>{"use strict";fs();bo();So();Es();hE=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var Nl={};Tt(Nl,{RetentionConfigSchema:()=>Oo,readRetentionConfig:()=>Co,writeRetentionConfig:()=>xs});import{existsSync as wl,mkdirSync as pb,readFileSync as mb,writeFileSync as gb}from"node:fs";import{homedir as fb}from"node:os";import{join as Rl}from"node:path";import{z as ks}from"zod";function kl(){return process.env.RECALL_HOME??Rl(fb(),".recall")}function _b(){let e=kl();wl(e)||pb(e,{recursive:!0})}function Al(){return Rl(kl(),"config.json")}function xl(){let e=Al();if(!wl(e))return{};try{return JSON.parse(mb(e,"utf8"))}catch(t){let n=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${n}`),{}}}function Co(){let e=xl().retention;if(!e)return{...As};let t=Oo.safeParse({...As,...e});return t.success?t.data:{...As}}function xs(e){_b();let t=xl(),n=Oo.parse({...As,...t.retention??{},...e}),s={...t,retention:n};return gb(Al(),JSON.stringify(s,null,2)),n}var Oo,As,Lo=se(()=>{"use strict";Oo=ks.object({autoArchiveEnabled:ks.boolean().default(!1),autoArchiveAfterDays:ks.number().int().min(7).max(3650).default(90),lastRunAt:ks.string().nullable().default(null)}),As={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Le from"chalk";import{formatDistanceToNowStrict as dC,parseISO as pC}from"date-fns";var Re,Ns=se(()=>{"use strict";Re={dim:Le.gray,bold:Le.bold,project:Le.cyan,user:Le.blue,assistant:Le.green,tool:Le.magenta,warn:Le.yellow,err:Le.red,ok:Le.green,accent:Le.hex("#f97316")}});import{existsSync as hb}from"node:fs";import{join as Eb}from"node:path";function Tn(){if(Ol&&hb(Sn))return;z();let e=h(),t=Sn.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
777
777
  CREATE TABLE IF NOT EXISTS archive.messages_archive (
778
778
  uuid TEXT PRIMARY KEY,
779
779
  session_id TEXT NOT NULL,
@@ -788,12 +788,12 @@ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
788
788
  archived_at TEXT NOT NULL DEFAULT (datetime('now'))
789
789
  );
790
790
  CREATE INDEX IF NOT EXISTS archive.idx_messages_archive_session ON messages_archive(session_id);
791
- `)}finally{e.exec("DETACH DATABASE archive")}Rl=!0}function kl(){let e=h();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;Tn();let n=Sn.replace(/'/g,"''"),s=0;e.exec(`ATTACH DATABASE '${n}' AS archive`);try{e.transaction(()=>{s=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
791
+ `)}finally{e.exec("DETACH DATABASE archive")}Ol=!0}function Cl(){let e=h();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;Tn();let n=Sn.replace(/'/g,"''"),s=0;e.exec(`ATTACH DATABASE '${n}' AS archive`);try{e.transaction(()=>{s=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
792
792
  (uuid, session_id, parent_uuid, type, role, timestamp,
793
793
  is_sidechain, content_text, tool_names, raw_json, archived_at)
794
794
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
795
795
  is_sidechain, content_text, tool_names, raw_json, archived_at
796
- FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return s}var Sn,Rl,Lo=se(()=>{"use strict";Q();U();Sn=db(B,"archive.sqlite"),Rl=!1});var xl={};Tt(xl,{runArchive:()=>bb});function Co(e){Tn();let t=h(),n=Sn.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${n}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function pb(){return Co(e=>{let t=e.prepare(`SELECT
796
+ FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return s}var Sn,Ol,vo=se(()=>{"use strict";Q();U();Sn=Eb(H,"archive.sqlite"),Ol=!1});var vl={};Tt(vl,{runArchive:()=>Ab});function Io(e){Tn();let t=h(),n=Sn.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${n}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function bb(){return Io(e=>{let t=e.prepare(`SELECT
797
797
  SUM(CASE WHEN archive_status = 'archived' THEN 1 ELSE 0 END) AS archived,
798
798
  SUM(CASE WHEN archive_status != 'archived' THEN 1 ELSE 0 END) AS live
799
799
  FROM sessions`).get(),n=e.prepare("SELECT COUNT(*) AS n FROM messages").get().n,s=e.prepare(`SELECT
@@ -802,15 +802,15 @@ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
802
802
  SELECT MAX(timestamp) AS t FROM main.messages_archive WHERE timestamp IS NOT NULL
803
803
  UNION ALL
804
804
  SELECT MAX(timestamp) AS t FROM archive.messages_archive WHERE timestamp IS NOT NULL
805
- )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function Al(e){return e?e.slice(0,10):"\u2014"}function mb(){let e=pb();return console.log(Re.dim("\u2014 Archive state \u2014")),console.log(` Live sessions ${e.liveSessions.toLocaleString()}`),console.log(` Archived sessions ${e.archivedSessions.toLocaleString()}`),console.log(` Live messages ${e.liveMessages.toLocaleString()}`),console.log(` Archived messages ${e.archivedMessages.toLocaleString()}`),console.log(` Oldest live ${Al(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${Al(e.newestArchivedTimestamp)}`),console.log(""),console.log(Re.dim(" recall archive run --before YYYY-MM-DD")),console.log(Re.dim(" recall archive restore <session-id>")),0}function gb(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let n=h().prepare(`SELECT s.id, s.ended_at, s.message_count
805
+ )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function Ll(e){return e?e.slice(0,10):"\u2014"}function Sb(){let e=bb();return console.log(Re.dim("\u2014 Archive state \u2014")),console.log(` Live sessions ${e.liveSessions.toLocaleString()}`),console.log(` Archived sessions ${e.archivedSessions.toLocaleString()}`),console.log(` Live messages ${e.liveMessages.toLocaleString()}`),console.log(` Archived messages ${e.archivedMessages.toLocaleString()}`),console.log(` Oldest live ${Ll(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${Ll(e.newestArchivedTimestamp)}`),console.log(""),console.log(Re.dim(" recall archive run --before YYYY-MM-DD")),console.log(Re.dim(" recall archive restore <session-id>")),0}function Tb(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let n=h().prepare(`SELECT s.id, s.ended_at, s.message_count
806
806
  FROM sessions s
807
807
  WHERE s.archive_status != 'archived'
808
- AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(n.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let s=n.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${n.length.toLocaleString()} session(s), ${s.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(Re.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=n.map(c=>c.id),o=Date.now(),a=Co(c=>c.transaction(d=>{let m=0,f=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
808
+ AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(n.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let s=n.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${n.length.toLocaleString()} session(s), ${s.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(Re.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=n.map(c=>c.id),o=Date.now(),a=Io(c=>c.transaction(d=>{let m=0,f=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
809
809
  (uuid, session_id, parent_uuid, type, role, timestamp,
810
810
  is_sidechain, content_text, tool_names, raw_json, archived_at)
811
811
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
812
812
  is_sidechain, content_text, tool_names, raw_json, datetime('now')
813
- FROM messages WHERE session_id = ?`),b=c.prepare("DELETE FROM messages WHERE session_id = ?"),T=c.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let S of d){f.run(S);let w=b.run(S);m+=Number(w.changes??0),T.run(S)}return m})(r));return console.log(`Archived ${n.length.toLocaleString()} session(s), moved ${a.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(Re.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function fb(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let n=h().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!n)return console.error(`Session ${e} not found.`),1;if(n.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${n.archive_status}).`),1;let s=Co(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
813
+ FROM messages WHERE session_id = ?`),b=c.prepare("DELETE FROM messages WHERE session_id = ?"),T=c.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let S of d){f.run(S);let R=b.run(S);m+=Number(R.changes??0),T.run(S)}return m})(r));return console.log(`Archived ${n.length.toLocaleString()} session(s), moved ${a.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(Re.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function yb(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let n=h().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!n)return console.error(`Session ${e} not found.`),1;if(n.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${n.archive_status}).`),1;let s=Io(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
814
814
  (uuid, session_id, parent_uuid, type, role, timestamp,
815
815
  is_sidechain, content_text, tool_names, raw_json)
816
816
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
@@ -820,32 +820,32 @@ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
820
820
  is_sidechain, content_text, tool_names, raw_json)
821
821
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
822
822
  is_sidechain, content_text, tool_names, raw_json
823
- FROM archive.messages_archive WHERE session_id = ?`),u=r.prepare("DELETE FROM main.messages_archive WHERE session_id = ?"),d=r.prepare("DELETE FROM archive.messages_archive WHERE session_id = ?"),m=Number(a.run(e).changes??0),f=Number(c.run(e).changes??0);return u.run(e),d.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),m+f})());return console.log(`Restored ${s.toLocaleString()} message(s) for session ${e}.`),0}function _b(){let e=No();return console.log(Re.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?Re.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function hb(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let n=xs({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${n.autoArchiveAfterDays} days).`),console.log(Re.dim(" The daemon will run a daily archive pass on the next tick.")),0}function Eb(){return xs({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function bb(e){let t=e._action??"list";if(t==="list"||t==="stats")return mb();if(t==="run")return e.before?gb({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return fb(e._sessionId??"");if(t==="auto"){let n=e._subAction??"status";return n==="status"?_b():n==="on"||n==="enable"?hb(e.after):n==="off"||n==="disable"?Eb():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
823
+ FROM archive.messages_archive WHERE session_id = ?`),u=r.prepare("DELETE FROM main.messages_archive WHERE session_id = ?"),d=r.prepare("DELETE FROM archive.messages_archive WHERE session_id = ?"),m=Number(a.run(e).changes??0),f=Number(c.run(e).changes??0);return u.run(e),d.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),m+f})());return console.log(`Restored ${s.toLocaleString()} message(s) for session ${e}.`),0}function wb(){let e=Co();return console.log(Re.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?Re.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function Rb(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let n=xs({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${n.autoArchiveAfterDays} days).`),console.log(Re.dim(" The daemon will run a daily archive pass on the next tick.")),0}function kb(){return xs({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function Ab(e){let t=e._action??"list";if(t==="list"||t==="stats")return Sb();if(t==="run")return e.before?Tb({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return yb(e._sessionId??"");if(t==="auto"){let n=e._subAction??"status";return n==="status"?wb():n==="on"||n==="enable"?Rb(e.after):n==="off"||n==="disable"?kb():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
824
824
  list \u2014 show archive counts
825
825
  run --before YYYY-MM-DD [--dry-run] \u2014 move sessions older than DATE
826
826
  restore <session-id> \u2014 pull a session back from archive
827
- auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var Nl=se(()=>{"use strict";Ns();U();Lo();Oo()});var qa=se(()=>{"use strict";U()});import{cpus as KA}from"node:os";import{Hono as mA}from"hono";import{serve as gA}from"@hono/node-server";Q();import{existsSync as jf,readFileSync as Pf,writeFileSync as bx,unlinkSync as Sx}from"node:fs";import{join as Ff}from"node:path";var Ja=Ff(B,"license.json");function cn(){if(!jf(Ja))return null;try{let e=Pf(Ja,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as $f,importSPKI as Uf}from"jose";var Ga=`-----BEGIN PUBLIC KEY-----
827
+ auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var Il=se(()=>{"use strict";Ns();U();vo();Lo()});var za=se(()=>{"use strict";U()});import{cpus as ox}from"node:os";import{Hono as yA}from"hono";import{serve as wA}from"@hono/node-server";Q();import{existsSync as Wf,readFileSync as qf,writeFileSync as Nx,unlinkSync as Ox}from"node:fs";import{join as Xf}from"node:path";var Va=Xf(H,"license.json");function cn(){if(!Wf(Va))return null;try{let e=qf(Va,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as Jf,importSPKI as Gf}from"jose";var Qa=`-----BEGIN PUBLIC KEY-----
828
828
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
829
829
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
830
830
  -----END PUBLIC KEY-----
831
- `,Jr="ES256",Ya="clauderecall.com",za="clauderecall-cli";var Zn=null;async function Hf(){return Zn||(Zn=await Uf(Ga,Jr),Zn)}async function Ka(e){try{let t=await Hf(),{payload:n}=await $f(e,t,{issuer:Ya,audience:za,algorithms:[Jr]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Bf}from"node:crypto";import{hostname as Wf,userInfo as qf,platform as Xf,arch as Jf}from"node:os";function Va(){let e="unknown";try{e=qf().username}catch{}let t=[Wf(),e,Xf(),Jf()];return Bf("sha256").update(t.join("\0")).digest("hex")}Q();import{existsSync as Gf,readFileSync as Yf,writeFileSync as zf}from"node:fs";import{join as Kf}from"node:path";function Qa(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),n;try{n=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let s=n.hostname==="127.0.0.1"||n.hostname==="localhost"||n.hostname==="::1";if(n.protocol==="https:"||n.protocol==="http:"&&s)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var Gr=Kf(B,"license-check.json"),Vf=1440*60*1e3,Qf=720*60*60*1e3,Zf=1e4;function Za(){if(!Gf(Gr))return null;try{let e=JSON.parse(Yf(Gr,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function e_(e){z(),zf(Gr,JSON.stringify(e,null,2)+`
832
- `,{mode:384})}async function t_(e,t){let n=null,s=null;try{n=new AbortController,s=setTimeout(()=>n?.abort(),Zf);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:n.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{s&&clearTimeout(s)}}async function ec(e,t={}){let n=Za(),s=t.apiUrl??`${Qa()}/api/license/check`,r=n?.license_key===e,o=!n||!r||Date.now()-new Date(n.last_checked_at).getTime()>=Vf;if(!t.force&&!o)return n;let a=await t_(e,s);if(!a)return r?n:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return e_(c),c}function tc(e){let t=Za();return!t||t.license_key!==e?null:t.revoked?{revoked:!0,reason:t.reason?`license revoked: ${t.reason}`:"license revoked by issuer"}:Date.now()-new Date(t.last_checked_at).getTime()>Qf?{revoked:!0,reason:"license has not been validated with the server in 30+ days. Reconnect to the internet and run `recall license check`"}:null}var n_=1440*60*1e3,Ux=60*n_;async function it(){let e=cn();if(!e)return{tier:"free"};let t=await Ka(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Va())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=tc(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:s_(e,t.claims)}async function nc(e){let t=cn();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let n=await ec(t.license_key,{force:e?.force??!1});return n?{ran:!0,revoked:n.revoked,reason:n.reason,last_checked_at:n.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function s_(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function sc(){return(await it()).tier==="pro"}U();U();import{createHash as q_}from"node:crypto";U();Q();import{writeFileSync as d_,readFileSync as lN,existsSync as p_,mkdirSync as m_,readdirSync as uN,unlinkSync as dN}from"node:fs";import{join as mc}from"node:path";import{randomUUID as gc}from"node:crypto";var Vr=mc(B,"bug-patterns");function g_(){z(),p_(Vr)||m_(Vr,{recursive:!0})}function Ke(e){return{id:e.id,signature_hash:e.signature_hash,example_message:e.example_message,occurrence_count:e.occurrence_count,first_seen_at:e.first_seen_at,last_seen_at:e.last_seen_at,resolved_in_session_id:e.resolved_in_session_id,fix_summary:e.fix_summary}}function fc(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function _c(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=h(),n=new Date().toISOString(),s=e.id??gc(),r=e.first_seen_at??n,o=e.last_seen_at??n,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
831
+ `,Yr="ES256",Za="clauderecall.com",ec="clauderecall-cli";var Zn=null;async function Yf(){return Zn||(Zn=await Gf(Qa,Yr),Zn)}async function tc(e){try{let t=await Yf(),{payload:n}=await Jf(e,t,{issuer:Za,audience:ec,algorithms:[Yr]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as zf}from"node:crypto";import{hostname as Kf,userInfo as Vf,platform as Qf,arch as Zf}from"node:os";function nc(){let e="unknown";try{e=Vf().username}catch{}let t=[Kf(),e,Qf(),Zf()];return zf("sha256").update(t.join("\0")).digest("hex")}Q();import{existsSync as e_,readFileSync as t_,writeFileSync as n_}from"node:fs";import{join as s_}from"node:path";function sc(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),n;try{n=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let s=n.hostname==="127.0.0.1"||n.hostname==="localhost"||n.hostname==="::1";if(n.protocol==="https:"||n.protocol==="http:"&&s)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var zr=s_(H,"license-check.json"),r_=1440*60*1e3,o_=720*60*60*1e3,i_=1e4;function rc(){if(!e_(zr))return null;try{let e=JSON.parse(t_(zr,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function a_(e){z(),n_(zr,JSON.stringify(e,null,2)+`
832
+ `,{mode:384})}async function c_(e,t){let n=null,s=null;try{n=new AbortController,s=setTimeout(()=>n?.abort(),i_);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:n.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{s&&clearTimeout(s)}}async function oc(e,t={}){let n=rc(),s=t.apiUrl??`${sc()}/api/license/check`,r=n?.license_key===e,o=!n||!r||Date.now()-new Date(n.last_checked_at).getTime()>=r_;if(!t.force&&!o)return n;let a=await c_(e,s);if(!a)return r?n:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return a_(c),c}function ic(e){let t=rc();return!t||t.license_key!==e?null:t.revoked?{revoked:!0,reason:t.reason?`license revoked: ${t.reason}`:"license revoked by issuer"}:Date.now()-new Date(t.last_checked_at).getTime()>o_?{revoked:!0,reason:"license has not been validated with the server in 30+ days. Reconnect to the internet and run `recall license check`"}:null}var l_=1440*60*1e3,zx=60*l_;async function it(){let e=cn();if(!e)return{tier:"free"};let t=await tc(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==nc())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=ic(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:u_(e,t.claims)}async function ac(e){let t=cn();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let n=await oc(t.license_key,{force:e?.force??!1});return n?{ran:!0,revoked:n.revoked,reason:n.reason,last_checked_at:n.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function u_(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function cc(){return(await it()).tier==="pro"}U();U();import{createHash as V_}from"node:crypto";U();Q();import{writeFileSync as E_,readFileSync as EN,existsSync as b_,mkdirSync as S_,readdirSync as bN,unlinkSync as SN}from"node:fs";import{join as Ec}from"node:path";import{randomUUID as bc}from"node:crypto";var Zr=Ec(H,"bug-patterns");function T_(){z(),b_(Zr)||S_(Zr,{recursive:!0})}function Ke(e){return{id:e.id,signature_hash:e.signature_hash,example_message:e.example_message,occurrence_count:e.occurrence_count,first_seen_at:e.first_seen_at,last_seen_at:e.last_seen_at,resolved_in_session_id:e.resolved_in_session_id,fix_summary:e.fix_summary}}function Sc(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function Tc(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=h(),n=new Date().toISOString(),s=e.id??bc(),r=e.first_seen_at??n,o=e.last_seen_at??n,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
833
833
  (id, signature_hash, example_message, occurrence_count,
834
834
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
835
835
  VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(s,e.signature_hash,e.example_message,a.length,r,o);let u=t.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
836
836
  VALUES (?, ?, ?)
837
- ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(s,d,n)})();let c=hc(s);if(!c)throw new Error("createCluster succeeded but read-back failed");return ln(s),c}function hc(e){let t=h(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:Ke(n),members:s.map(fc)}}function Ec(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let n=h();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;n.transaction(()=>{let c=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
837
+ ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(s,d,n)})();let c=yc(s);if(!c)throw new Error("createCluster succeeded but read-back failed");return ln(s),c}function yc(e){let t=h(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:Ke(n),members:s.map(Sc)}}function wc(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let n=h();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;n.transaction(()=>{let c=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
838
838
  VALUES (?, ?, ?)
839
839
  ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let u of Array.from(new Set(t)))c.run(e,u,r).changes>0&&(o+=1);if(o>0){let u=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;n.prepare(`UPDATE bug_pattern_clusters
840
840
  SET occurrence_count = ?, last_seen_at = ?
841
- WHERE id = ?`).run(u,r,e)}})(),o>0&&ln(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Ke(a),added:o}}function bc(e={}){let t=h(),n=[],s=[];typeof e.minOccurrenceCount=="number"&&(n.push("c.occurrence_count >= ?"),s.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&n.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(n.push(`EXISTS (
841
+ WHERE id = ?`).run(u,r,e)}})(),o>0&&ln(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Ke(a),added:o}}function Rc(e={}){let t=h(),n=[],s=[];typeof e.minOccurrenceCount=="number"&&(n.push("c.occurrence_count >= ?"),s.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&n.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(n.push(`EXISTS (
842
842
  SELECT 1 FROM bug_pattern_members m
843
843
  JOIN sessions s ON s.id = m.session_id
844
844
  JOIN projects p ON p.id = s.project_id
845
845
  WHERE m.cluster_id = c.id AND p.name = ?
846
846
  )`),s.push(e.project));let r=n.length>0?`WHERE ${n.join(" AND ")}`:"",o=t.prepare(`SELECT COUNT(*) AS n FROM bug_pattern_clusters c ${r}`).get(...s),a=Math.max(1,Math.min(5e3,e.limit??100)),c=Math.max(0,Math.floor(e.offset??0));return{clusters:t.prepare(`SELECT c.* FROM bug_pattern_clusters c ${r}
847
847
  ORDER BY c.occurrence_count DESC, c.last_seen_at DESC
848
- LIMIT ? OFFSET ?`).all(...s,a,c).map(Ke),total:o.n}}function f_(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,n=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:n,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function Sc(e){let t=h(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
848
+ LIMIT ? OFFSET ?`).all(...s,a,c).map(Ke),total:o.n}}function y_(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,n=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:n,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function kc(e){let t=h(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
849
849
  NULLIF(sa.alias, '') AS alias,
850
850
  s.auto_title,
851
851
  s.first_user_message,
@@ -856,31 +856,31 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
856
856
  LEFT JOIN session_aliases sa ON sa.session_id = m.session_id
857
857
  LEFT JOIN projects p ON p.id = s.project_id
858
858
  WHERE m.cluster_id = ?
859
- ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:Ke(n),members:s.map(f_)}}function Tc(e,t,n){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof n!="string"||!n.trim())throw new Error("fixSummary is required");let s=h();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!s.prepare("SELECT 1 FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?").get(e,t))throw new Error(`session ${t} is not a member of cluster ${e}`);s.prepare(`UPDATE bug_pattern_clusters
859
+ ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:Ke(n),members:s.map(y_)}}function Ac(e,t,n){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof n!="string"||!n.trim())throw new Error("fixSummary is required");let s=h();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!s.prepare("SELECT 1 FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?").get(e,t))throw new Error(`session ${t} is not a member of cluster ${e}`);s.prepare(`UPDATE bug_pattern_clusters
860
860
  SET resolved_in_session_id = ?, fix_summary = ?
861
- WHERE id = ?`).run(t,n.trim(),e),ln(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Ke(a)}}function yc(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("memberSessionIds must be a non-empty array");let n=h(),s=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=n.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
862
- WHERE cluster_id = ? AND session_id IN (${o})`).all(e,...r);if(a.length===0)throw new Error(`none of the supplied session_ids are members of cluster ${e}`);let c=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;if(a.length>=c)throw new Error(`cannot split: would leave the original cluster empty (move ${a.length} of ${c})`);let u=gc(),d=new Date().toISOString(),m=[];n.transaction(()=>{n.prepare(`INSERT INTO bug_pattern_clusters
861
+ WHERE id = ?`).run(t,n.trim(),e),ln(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Ke(a)}}function xc(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("memberSessionIds must be a non-empty array");let n=h(),s=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=n.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
862
+ WHERE cluster_id = ? AND session_id IN (${o})`).all(e,...r);if(a.length===0)throw new Error(`none of the supplied session_ids are members of cluster ${e}`);let c=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;if(a.length>=c)throw new Error(`cannot split: would leave the original cluster empty (move ${a.length} of ${c})`);let u=bc(),d=new Date().toISOString(),m=[];n.transaction(()=>{n.prepare(`INSERT INTO bug_pattern_clusters
863
863
  (id, signature_hash, example_message, occurrence_count,
864
864
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
865
865
  VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(u,s.signature_hash,s.example_message,a.length,s.first_seen_at,d);let T=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
866
- VALUES (?, ?, ?)`),S=n.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let A of a)T.run(u,A.session_id,A.matched_at),S.run(e,A.session_id);let w=c-a.length;n.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(w,e),m=n.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),ln(e),ln(u);let f=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),b=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:Ke(f),split:Ke(b),movedMembers:m.map(fc)}}function ln(e){try{g_();let t=hc(e);if(!t)return;let n=mc(Vr,`${e}.json`),s={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};d_(n,JSON.stringify(s,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function wc(e){return e?h().prepare(`SELECT * FROM bug_pattern_clusters
866
+ VALUES (?, ?, ?)`),S=n.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let A of a)T.run(u,A.session_id,A.matched_at),S.run(e,A.session_id);let R=c-a.length;n.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(R,e),m=n.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),ln(e),ln(u);let f=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),b=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:Ke(f),split:Ke(b),movedMembers:m.map(Sc)}}function ln(e){try{T_();let t=yc(e);if(!t)return;let n=Ec(Zr,`${e}.json`),s={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};E_(n,JSON.stringify(s,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function Nc(e){return e?h().prepare(`SELECT * FROM bug_pattern_clusters
867
867
  WHERE signature_hash = ?
868
- ORDER BY last_seen_at DESC, id ASC`).all(e).map(Ke):[]}function Rc(e){if(!e)return new Set;let n=h().prepare(`SELECT DISTINCT m.session_id
868
+ ORDER BY last_seen_at DESC, id ASC`).all(e).map(Ke):[]}function Oc(e){if(!e)return new Set;let n=h().prepare(`SELECT DISTINCT m.session_id
869
869
  FROM bug_pattern_members m
870
870
  JOIN bug_pattern_clusters c ON c.id = m.cluster_id
871
- WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var X_=/\b0x[0-9a-fA-F]+\b/g,J_=/\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g,G_=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,Y_=/:\d+:\d+/g,z_=/\bline\s+\d+\b/gi,K_=/\bcolumn\s+\d+\b/gi,V_=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,Q_=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,Z_=/\b\d{4,}\b/g,eh=/(['"`])[^'"`\n]{1,128}\1/g;function th(e){if(!e)return"";let t=String(e);return t=t.replace(X_,"<hex>"),t=t.replace(J_,"<uuid>"),t=t.replace(G_,"<ts>"),t=t.replace(Y_,":<line>:<col>"),t=t.replace(z_,"line <n>"),t=t.replace(K_,"column <n>"),t=t.replace(V_,"pid <n>"),t=t.replace(Q_,"port <n>"),t=t.replace(Z_,"<num>"),t=t.replace(eh,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function nh(e){let t=(e.error_type??"unknown").toLowerCase().trim(),n=th(e.snippet??e.message_hash??""),s=`${t}|${n}`;return q_("sha256").update(s).digest("hex").slice(0,16)}function sh(e){let t=h(),n=["oi.bug_signatures IS NOT NULL"],s=[];e&&(n.push("p.name = ?"),s.push(e));let r=`WHERE ${n.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
871
+ WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var Q_=/\b0x[0-9a-fA-F]+\b/g,Z_=/\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g,eh=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,th=/:\d+:\d+/g,nh=/\bline\s+\d+\b/gi,sh=/\bcolumn\s+\d+\b/gi,rh=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,oh=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,ih=/\b\d{4,}\b/g,ah=/(['"`])[^'"`\n]{1,128}\1/g;function ch(e){if(!e)return"";let t=String(e);return t=t.replace(Q_,"<hex>"),t=t.replace(Z_,"<uuid>"),t=t.replace(eh,"<ts>"),t=t.replace(th,":<line>:<col>"),t=t.replace(nh,"line <n>"),t=t.replace(sh,"column <n>"),t=t.replace(rh,"pid <n>"),t=t.replace(oh,"port <n>"),t=t.replace(ih,"<num>"),t=t.replace(ah,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function lh(e){let t=(e.error_type??"unknown").toLowerCase().trim(),n=ch(e.snippet??e.message_hash??""),s=`${t}|${n}`;return V_("sha256").update(s).digest("hex").slice(0,16)}function uh(e){let t=h(),n=["oi.bug_signatures IS NOT NULL"],s=[];e&&(n.push("p.name = ?"),s.push(e));let r=`WHERE ${n.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
872
872
  p.name AS project,
873
873
  s.started_at AS started_at,
874
874
  oi.bug_signatures AS bug_signatures
875
875
  FROM session_output_index oi
876
876
  LEFT JOIN sessions s ON s.id = oi.session_id
877
877
  LEFT JOIN projects p ON p.id = s.project_id
878
- ${r}`).all(...s),a=[];for(let c of o){if(!c.bug_signatures)continue;let u=[];try{let d=JSON.parse(c.bug_signatures);Array.isArray(d)&&(u=d)}catch{continue}for(let d of u){if(!d||typeof d!="object"||!(d.snippet??"").trim())continue;let f=nh(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:f})}}return a}function rh(e){let t=new Map;for(let s of e){let r=t.get(s.fingerprint);r||(r=[],t.set(s.fingerprint,r)),r.push(s)}let n=[];for(let[s,r]of t){let o=new Set,a=[];for(let m of r)o.has(m.session_id)||(o.add(m.session_id),a.push(m));let c=[...a].sort((m,f)=>{let b=m.started_at??"",T=f.started_at??"";return b&&T?b<T?-1:b>T?1:0:b?-1:T?1:0}),u=c.find(m=>m.started_at),d=[...c].reverse().find(m=>m.started_at);n.push({fingerprint:s,example_message:a[0].signature.snippet??a[0].signature.message_hash??"",members:a,first_seen_at:u?.started_at??new Date().toISOString(),last_seen_at:d?.started_at??new Date().toISOString()})}return n}function oh(e,t){if(e.length!==t.length)return 0;let n=0,s=0,r=0;for(let a=0;a<e.length;a++)n+=e[a]*t[a],s+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(s)*Math.sqrt(r);return o>0?n/o:0}function ih(e){let{records:t,vectors:n,epsilon:s,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let m=0;m<o;m++){let f=[];for(let b=0;b<o;b++){if(m===b)continue;1-oh(n[m],n[b])<=s&&f.push(b)}a.push(f)}let c=new Array(o).fill(!1),u=new Array(o).fill(-1),d=[];for(let m=0;m<o;m++){if(c[m])continue;c[m]=!0;let f=a[m];if(f.length<r)continue;let b=d.length;d.push({members:[t[m]]}),u[m]=b;let T=[...f];for(;T.length>0;){let S=T.shift();if(!c[S]&&(c[S]=!0,a[S].length>=r))for(let w of a[S])(!c[w]||u[w]===-1)&&T.push(w);u[S]===-1&&(u[S]=b,d[b].members.push(t[S]))}}return d}async function ah(e,t,n,s){if(e.length===0)return[];let r=e.map(u=>{let d=u.signature.snippet??u.signature.message_hash??"";return`${u.signature.error_type??""}: ${d}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let a=ih({records:e,vectors:o,epsilon:n,minPts:s}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,m=[];for(let A of u.members)d.has(A.session_id)||(d.add(A.session_id),m.push(A));if(m.length===0)continue;let b=`sem:${[...m.map(A=>A.fingerprint)].sort()[0]}`,T=[...m].sort((A,v)=>{let D=A.started_at??"",X=v.started_at??"";return D<X?-1:D>X?1:0}),S=T.find(A=>A.started_at),w=[...T].reverse().find(A=>A.started_at);c.push({fingerprint:b,example_message:m[0].signature.snippet??m[0].signature.message_hash??"",members:m,first_seen_at:S?.started_at??new Date().toISOString(),last_seen_at:w?.started_at??new Date().toISOString()})}return c}function vc(e,t){let n={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let s of e){if(s.members.length<t)continue;let r=wc(s.fingerprint),o=Rc(s.fingerprint),a=s.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=_c({signature_hash:s.fingerprint,example_message:s.example_message.slice(0,256),member_session_ids:s.members.map(m=>m.session_id),first_seen_at:s.first_seen_at,last_seen_at:s.last_seen_at});n.clusters_created+=1,n.members_added+=d.members.length,n.cluster_ids.push(d.cluster.id);continue}if(a.length===0){n.cluster_ids.push(r[0].id);continue}let c=r[0],u=Ec(c.id,a);u.added>0&&(n.clusters_merged+=1,n.members_added+=u.added),n.cluster_ids.push(c.id)}return n}async function Ic(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),n=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),s=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=sh(e.project),a=new Set(o.map(D=>D.session_id)),c=rh(o),u=[],d=!1;if(e.semantic){let D=e.embedder??null;if(!D)try{D=await uh()}catch(X){let P=(X instanceof Error?X.message:String(X)).split(`
878
+ ${r}`).all(...s),a=[];for(let c of o){if(!c.bug_signatures)continue;let u=[];try{let d=JSON.parse(c.bug_signatures);Array.isArray(d)&&(u=d)}catch{continue}for(let d of u){if(!d||typeof d!="object"||!(d.snippet??"").trim())continue;let f=lh(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:f})}}return a}function dh(e){let t=new Map;for(let s of e){let r=t.get(s.fingerprint);r||(r=[],t.set(s.fingerprint,r)),r.push(s)}let n=[];for(let[s,r]of t){let o=new Set,a=[];for(let m of r)o.has(m.session_id)||(o.add(m.session_id),a.push(m));let c=[...a].sort((m,f)=>{let b=m.started_at??"",T=f.started_at??"";return b&&T?b<T?-1:b>T?1:0:b?-1:T?1:0}),u=c.find(m=>m.started_at),d=[...c].reverse().find(m=>m.started_at);n.push({fingerprint:s,example_message:a[0].signature.snippet??a[0].signature.message_hash??"",members:a,first_seen_at:u?.started_at??new Date().toISOString(),last_seen_at:d?.started_at??new Date().toISOString()})}return n}function ph(e,t){if(e.length!==t.length)return 0;let n=0,s=0,r=0;for(let a=0;a<e.length;a++)n+=e[a]*t[a],s+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(s)*Math.sqrt(r);return o>0?n/o:0}function mh(e){let{records:t,vectors:n,epsilon:s,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let m=0;m<o;m++){let f=[];for(let b=0;b<o;b++){if(m===b)continue;1-ph(n[m],n[b])<=s&&f.push(b)}a.push(f)}let c=new Array(o).fill(!1),u=new Array(o).fill(-1),d=[];for(let m=0;m<o;m++){if(c[m])continue;c[m]=!0;let f=a[m];if(f.length<r)continue;let b=d.length;d.push({members:[t[m]]}),u[m]=b;let T=[...f];for(;T.length>0;){let S=T.shift();if(!c[S]&&(c[S]=!0,a[S].length>=r))for(let R of a[S])(!c[R]||u[R]===-1)&&T.push(R);u[S]===-1&&(u[S]=b,d[b].members.push(t[S]))}}return d}async function gh(e,t,n,s){if(e.length===0)return[];let r=e.map(u=>{let d=u.signature.snippet??u.signature.message_hash??"";return`${u.signature.error_type??""}: ${d}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let a=mh({records:e,vectors:o,epsilon:n,minPts:s}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,m=[];for(let A of u.members)d.has(A.session_id)||(d.add(A.session_id),m.push(A));if(m.length===0)continue;let b=`sem:${[...m.map(A=>A.fingerprint)].sort()[0]}`,T=[...m].sort((A,v)=>{let D=A.started_at??"",X=v.started_at??"";return D<X?-1:D>X?1:0}),S=T.find(A=>A.started_at),R=[...T].reverse().find(A=>A.started_at);c.push({fingerprint:b,example_message:m[0].signature.snippet??m[0].signature.message_hash??"",members:m,first_seen_at:S?.started_at??new Date().toISOString(),last_seen_at:R?.started_at??new Date().toISOString()})}return c}function Pc(e,t){let n={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let s of e){if(s.members.length<t)continue;let r=Nc(s.fingerprint),o=Oc(s.fingerprint),a=s.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=Tc({signature_hash:s.fingerprint,example_message:s.example_message.slice(0,256),member_session_ids:s.members.map(m=>m.session_id),first_seen_at:s.first_seen_at,last_seen_at:s.last_seen_at});n.clusters_created+=1,n.members_added+=d.members.length,n.cluster_ids.push(d.cluster.id);continue}if(a.length===0){n.cluster_ids.push(r[0].id);continue}let c=r[0],u=wc(c.id,a);u.added>0&&(n.clusters_merged+=1,n.members_added+=u.added),n.cluster_ids.push(c.id)}return n}async function Fc(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),n=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),s=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=uh(e.project),a=new Set(o.map(D=>D.session_id)),c=dh(o),u=[],d=!1;if(e.semantic){let D=e.embedder??null;if(!D)try{D=await hh()}catch(X){let P=(X instanceof Error?X.message:String(X)).split(`
879
879
  `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${P}
880
- Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(D){let X=[];for(let I of c)I.members.length===1&&X.push(I.members[0]);X.length>=2&&(u=await ah(X,D,s,r))}}let m=c.filter(D=>D.members.length>=t),f=u.filter(D=>D.members.length>=t),b=vc(m,t),T=vc(f,t),S=[...b.cluster_ids,...T.cluster_ids],w=Array.from(new Set(S)),A=[];if(w.length>0){let D=h(),X=w.map(()=>"?").join(","),I=D.prepare(`SELECT * FROM bug_pattern_clusters
880
+ Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(D){let X=[];for(let I of c)I.members.length===1&&X.push(I.members[0]);X.length>=2&&(u=await gh(X,D,s,r))}}let m=c.filter(D=>D.members.length>=t),f=u.filter(D=>D.members.length>=t),b=Pc(m,t),T=Pc(f,t),S=[...b.cluster_ids,...T.cluster_ids],R=Array.from(new Set(S)),A=[];if(R.length>0){let D=h(),X=R.map(()=>"?").join(","),I=D.prepare(`SELECT * FROM bug_pattern_clusters
881
881
  WHERE id IN (${X})
882
882
  ORDER BY occurrence_count DESC, last_seen_at DESC
883
- LIMIT ?`).all(...w,n);for(let P of I)A.push({id:P.id,signature_hash:P.signature_hash,example_message:P.example_message,occurrence_count:P.occurrence_count,first_seen_at:P.first_seen_at,last_seen_at:P.last_seen_at,resolved_in_session_id:P.resolved_in_session_id,fix_summary:P.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:m.length,semantic_groups:f.length,clusters_created:b.clusters_created+T.clusters_created,clusters_merged:b.clusters_merged+T.clusters_merged,members_added:b.members_added+T.members_added,semantic_skipped:d},clusters:A}}var ch=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:n}=await Promise.resolve().then(()=>(We(),Cc));return n().loaded||await t(),e},lh=ch;async function uh(){return lh()}U();U();Q();import{writeFileSync as Mc,readFileSync as BN,existsSync as Dc,mkdirSync as jc,readdirSync as WN}from"node:fs";import{join as ds}from"node:path";var dh=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),Pc=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),ph=new Set(["pending","approved","rejected"]),mh=new Set(["L1","L2","L3","L4","user"]),oo=ds(B,"links"),io=ds(B,"suggestions"),gh=ds(io,"index.json");function fh(){z(),Dc(oo)||jc(oo,{recursive:!0})}function _h(){z(),Dc(io)||jc(io,{recursive:!0})}function Fc(e){try{return JSON.parse(e)}catch{return e}}function ps(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,source:e.source,evidence:Fc(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function ao(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,evidence:Fc(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function $c(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function co(e){if(!dh.has(e))throw new Error(`invalid link_type: ${e}`)}function hh(e){if(!Pc.has(e))throw new Error(`invalid source: ${e}`)}function Uc(e){if(!mh.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Hc(e,t){if(!e||!t)throw new Error("source_session_id and target_session_id are required");if(e===t)throw new Error("a session cannot link to itself")}function Bc(e){Hc(e.source_session_id,e.target_session_id),co(e.link_type),hh(e.source),$c(e.confidence);let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
883
+ LIMIT ?`).all(...R,n);for(let P of I)A.push({id:P.id,signature_hash:P.signature_hash,example_message:P.example_message,occurrence_count:P.occurrence_count,first_seen_at:P.first_seen_at,last_seen_at:P.last_seen_at,resolved_in_session_id:P.resolved_in_session_id,fix_summary:P.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:m.length,semantic_groups:f.length,clusters_created:b.clusters_created+T.clusters_created,clusters_merged:b.clusters_merged+T.clusters_merged,members_added:b.members_added+T.members_added,semantic_skipped:d},clusters:A}}var fh=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:n}=await Promise.resolve().then(()=>(We(),jc));return n().loaded||await t(),e},_h=fh;async function hh(){return _h()}U();U();Q();import{writeFileSync as $c,readFileSync as VN,existsSync as Uc,mkdirSync as Bc,readdirSync as QN}from"node:fs";import{join as ds}from"node:path";var Eh=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),Hc=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),bh=new Set(["pending","approved","rejected"]),Sh=new Set(["L1","L2","L3","L4","user"]),ao=ds(H,"links"),co=ds(H,"suggestions"),Th=ds(co,"index.json");function yh(){z(),Uc(ao)||Bc(ao,{recursive:!0})}function wh(){z(),Uc(co)||Bc(co,{recursive:!0})}function Wc(e){try{return JSON.parse(e)}catch{return e}}function ps(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,source:e.source,evidence:Wc(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function lo(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,evidence:Wc(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function qc(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function uo(e){if(!Eh.has(e))throw new Error(`invalid link_type: ${e}`)}function Rh(e){if(!Hc.has(e))throw new Error(`invalid source: ${e}`)}function Xc(e){if(!Sh.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Jc(e,t){if(!e||!t)throw new Error("source_session_id and target_session_id are required");if(e===t)throw new Error("a session cannot link to itself")}function Gc(e){Jc(e.source_session_id,e.target_session_id),uo(e.link_type),Rh(e.source),qc(e.confidence);let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
884
884
  (source_session_id, target_session_id, link_type,
885
885
  confidence, source, evidence, approved,
886
886
  created_at, updated_at)
@@ -893,11 +893,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
893
893
  updated_at = excluded.updated_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,e.source,s,r,n,n);let o=t.prepare(`SELECT * FROM session_links
894
894
  WHERE source_session_id = ?
895
895
  AND target_session_id = ?
896
- AND link_type = ?`).get(e.source_session_id,e.target_session_id,e.link_type);if(!o)throw new Error("createLink succeeded but read-back failed");return uo(e.source_session_id),ps(o)}function ms(e={}){let t=h(),n=[],s=[];e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.linkType&&(co(e.linkType),n.push("link_type = ?"),s.push(e.linkType)),e.approvedOnly&&n.push("approved = 1");let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
896
+ AND link_type = ?`).get(e.source_session_id,e.target_session_id,e.link_type);if(!o)throw new Error("createLink succeeded but read-back failed");return mo(e.source_session_id),ps(o)}function ms(e={}){let t=h(),n=[],s=[];e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.linkType&&(uo(e.linkType),n.push("link_type = ?"),s.push(e.linkType)),e.approvedOnly&&n.push("approved = 1");let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
897
897
  ORDER BY confidence DESC, updated_at DESC
898
898
  LIMIT ?`).all(...s,o).map(ps)}function gn(e){return h().prepare(`SELECT * FROM session_links
899
899
  WHERE source_session_id = ? OR target_session_id = ?
900
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(ps)}function Wc(e){let t=h(),n=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!n)return{removed:0,sourceSessionId:null};let s=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return s.changes>0&&uo(n.source_session_id),{removed:s.changes,sourceSessionId:n.source_session_id}}function fn(e,t={}){Hc(e.source_session_id,e.target_session_id),co(e.link_type),$c(e.confidence),Uc(e.inferred_by);let n=h(),s=new Date().toISOString(),r=JSON.stringify(e.evidence??null);n.prepare(`INSERT INTO session_link_suggestions
900
+ ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(ps)}function Yc(e){let t=h(),n=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!n)return{removed:0,sourceSessionId:null};let s=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return s.changes>0&&mo(n.source_session_id),{removed:s.changes,sourceSessionId:n.source_session_id}}function fn(e,t={}){Jc(e.source_session_id,e.target_session_id),uo(e.link_type),qc(e.confidence),Xc(e.inferred_by);let n=h(),s=new Date().toISOString(),r=JSON.stringify(e.evidence??null);n.prepare(`INSERT INTO session_link_suggestions
901
901
  (source_session_id, target_session_id, link_type,
902
902
  confidence, evidence, status, inferred_by,
903
903
  created_at, decided_at)
@@ -917,9 +917,9 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
917
917
  WHERE source_session_id = ?
918
918
  AND target_session_id = ?
919
919
  AND link_type = ?
920
- AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!o)throw new Error("createSuggestion succeeded but read-back failed");return t.deferMirror||ct(),ao(o)}function wt(e={}){let t=h(),n=[],s=[];if(e.status){if(!ph.has(e.status))throw new Error(`invalid status: ${e.status}`);n.push("status = ?"),s.push(e.status)}e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.inferredBy&&(Uc(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
920
+ AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!o)throw new Error("createSuggestion succeeded but read-back failed");return t.deferMirror||ct(),lo(o)}function wt(e={}){let t=h(),n=[],s=[];if(e.status){if(!bh.has(e.status))throw new Error(`invalid status: ${e.status}`);n.push("status = ?"),s.push(e.status)}e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.inferredBy&&(Xc(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
921
921
  ORDER BY confidence DESC, created_at DESC
922
- LIMIT ?`).all(...s,o).map(ao)}function lo(e,t,n={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let s=n.source??"manual";if(!Pc.has(s))throw new Error(`invalid source: ${s}`);let r=h(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let a=new Date().toISOString(),c;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
922
+ LIMIT ?`).all(...s,o).map(lo)}function po(e,t,n={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let s=n.source??"manual";if(!Hc.has(s))throw new Error(`invalid source: ${s}`);let r=h(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let a=new Date().toISOString(),c;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
923
923
  SET status = ?, decided_at = ?
924
924
  WHERE id = ?`).run(t,a,e),t==="approved"&&(r.prepare(`INSERT INTO session_links
925
925
  (source_session_id, target_session_id, link_type,
@@ -934,7 +934,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
934
934
  updated_at = excluded.updated_at`).run(o.source_session_id,o.target_session_id,o.link_type,o.confidence,s,o.evidence,a,a),c=r.prepare(`SELECT * FROM session_links
935
935
  WHERE source_session_id = ?
936
936
  AND target_session_id = ?
937
- AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),ct(),t==="approved"&&uo(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:ao(u),link:c?ps(c):null}}function uo(e){try{fh();let t=ms({sourceSessionId:e}),n=ds(oo,`${e}.json`);if(t.length===0)return;let s={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};Mc(n,JSON.stringify(s,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function ct(){try{_h();let e=wt({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};Mc(gh,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}U();Q();import{writeFileSync as Eh,readFileSync as zN,existsSync as bh,mkdirSync as Sh,readdirSync as KN}from"node:fs";import{join as qc}from"node:path";var po=qc(B,"output-index");function Th(){z(),bh(po)||Sh(po,{recursive:!0})}function _n(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function yh(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function Xc(e){return{session_id:e.session_id,files_written:_n(e.files_written),brands_mentioned:_n(e.brands_mentioned),terms_introduced:_n(e.terms_introduced),plan_ids_referenced:_n(e.plan_ids_referenced),bug_signatures:_n(e.bug_signatures),raw_extraction:yh(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function Jc(e){if(!e.session_id)throw new Error("session_id is required");let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),a=JSON.stringify(e.plan_ids_referenced??[]),c=JSON.stringify(e.bug_signatures??[]),u=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),d=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
937
+ AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),ct(),t==="approved"&&mo(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:lo(u),link:c?ps(c):null}}function mo(e){try{yh();let t=ms({sourceSessionId:e}),n=ds(ao,`${e}.json`);if(t.length===0)return;let s={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};$c(n,JSON.stringify(s,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function ct(){try{wh();let e=wt({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};$c(Th,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}U();Q();import{writeFileSync as kh,readFileSync as rO,existsSync as Ah,mkdirSync as xh,readdirSync as oO}from"node:fs";import{join as zc}from"node:path";var go=zc(H,"output-index");function Nh(){z(),Ah(go)||xh(go,{recursive:!0})}function _n(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Oh(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function Kc(e){return{session_id:e.session_id,files_written:_n(e.files_written),brands_mentioned:_n(e.brands_mentioned),terms_introduced:_n(e.terms_introduced),plan_ids_referenced:_n(e.plan_ids_referenced),bug_signatures:_n(e.bug_signatures),raw_extraction:Oh(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function Vc(e){if(!e.session_id)throw new Error("session_id is required");let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),a=JSON.stringify(e.plan_ids_referenced??[]),c=JSON.stringify(e.bug_signatures??[]),u=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),d=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
938
938
  (session_id, files_written, brands_mentioned, terms_introduced,
939
939
  plan_ids_referenced, bug_signatures, raw_extraction,
940
940
  extracted_at, extractor_version)
@@ -947,14 +947,14 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
947
947
  bug_signatures = excluded.bug_signatures,
948
948
  raw_extraction = excluded.raw_extraction,
949
949
  extracted_at = excluded.extracted_at,
950
- extractor_version = excluded.extractor_version`).run(e.session_id,s,r,o,a,c,u,n,d);let m=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!m)throw new Error("setOutputIndex succeeded but read-back failed");let f=Xc(m);return wh(e.session_id),f}function lt(e){let n=h().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return n?Xc(n):null}function wh(e){try{Th();let t=lt(e);if(!t)return;let n=qc(po,`${e}.json`),s={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};Eh(n,JSON.stringify(s,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var mo={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},Rh=2,kh=.25,Ah=5,xh=60,Nh=25;function gs(e){return e.trim().toLowerCase()}function Oh(e){let t=new Set;for(let n of e.files_written)t.add(`file:${gs(n)}`);for(let n of e.brands_mentioned)t.add(`brand:${gs(n)}`);for(let n of e.terms_introduced)t.add(`term:${gs(n)}`);for(let n of e.plan_ids_referenced)t.add(`plan:${gs(n)}`);return t}function Lh(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/xh);return Math.max(.2,t)}function Ch(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function vh(e){let t=lt(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(n=>n.term),plan_ids_referenced:t.plan_ids_referenced}:null}function Ih(e){return h().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at,
950
+ extractor_version = excluded.extractor_version`).run(e.session_id,s,r,o,a,c,u,n,d);let m=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!m)throw new Error("setOutputIndex succeeded but read-back failed");let f=Kc(m);return Ch(e.session_id),f}function lt(e){let n=h().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return n?Kc(n):null}function Ch(e){try{Nh();let t=lt(e);if(!t)return;let n=zc(go,`${e}.json`),s={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};kh(n,JSON.stringify(s,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var fo={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},Lh=2,vh=.25,Ih=5,Mh=60,Dh=25;function gs(e){return e.trim().toLowerCase()}function jh(e){let t=new Set;for(let n of e.files_written)t.add(`file:${gs(n)}`);for(let n of e.brands_mentioned)t.add(`brand:${gs(n)}`);for(let n of e.terms_introduced)t.add(`term:${gs(n)}`);for(let n of e.plan_ids_referenced)t.add(`plan:${gs(n)}`);return t}function Ph(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Mh);return Math.max(.2,t)}function Fh(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function $h(e){let t=lt(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(n=>n.term),plan_ids_referenced:t.plan_ids_referenced}:null}function Uh(e){return h().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at,
951
951
  s.rowid AS rowid,
952
952
  (SELECT COALESCE(MAX(m.rowid), 0) FROM messages m WHERE m.session_id = s.id)
953
953
  AS maxMessageRowid
954
954
  FROM sessions s
955
955
  JOIN session_output_index oi ON oi.session_id = s.id
956
956
  WHERE s.project_id = ?
957
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function Mh(e,t){let n=new Map,s=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let a=t.get(o.id);if(!a)continue;let c=Oh(a);if(c.size!==0){s.set(o.id,c);for(let u of c){let d=n.get(u);d?d.push(o.id):n.set(u,[o.id])}}}return{posting:n,vocab:s,startedAt:r}}function Dh(e,t){let n=t.vocab.get(e),s=t.startedAt.get(e)??null;if(!n||!s)return[];let r=new Map;for(let a of n){let c=t.posting.get(a);if(c)for(let u of c){if(u===e)continue;let d=t.startedAt.get(u);if(!d||d>=s)continue;let m=r.get(u);m?m.push(a):r.set(u,[a])}}let o=[];for(let[a,c]of r){let u=c.length;if(u<Rh)continue;let d=t.startedAt.get(a)??null,m=Ch(s,d),f=Lh(m),b=Math.min(1,u/Ah*f);if(b<kh)continue;let T=c.slice(0,12);o.push({target_session_id:a,matched_terms:T,overlap:u,days_apart:Math.round(m*10)/10,recency:Math.round(f*1e3)/1e3,confidence:Math.round(b*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,Nh)}async function Gc(e){if(mo.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=Ih(e.projectId),n=new Map;for(let c of t){let u=vh(c.id);u&&n.set(c.id,u)}let s=Mh(t,n),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let c of t){if(e.signal?.aborted)break;if(typeof e.sinceRowid=="number"&&c.maxMessageRowid<=e.sinceRowid)continue;r.current_session_id=c.id,e.onProgress?.({...r});let u=Dh(c.id,s);for(let d of u)o.push({source_session_id:c.id,target_session_id:d.target_session_id,confidence:d.confidence,matched_terms:d.matched_terms,overlap:d.overlap,recency:d.recency,days_apart:d.days_apart});r.processed_sessions+=1,e.onProgress?.({...r})}let a=[];return o.length>0&&(h().transaction(()=>{for(let d of o)try{let m=fn({source_session_id:d.source_session_id,target_session_id:d.target_session_id,link_type:"citation",confidence:d.confidence,evidence:{matched_terms:d.matched_terms,overlap_count:d.overlap,recency:d.recency,days_apart:d.days_apart},inferred_by:"L2"},{deferMirror:!0});a.push(m.id),r.suggestions_created+=1}catch(m){console.error("[citation-inference] createSuggestion failed:",m)}})(),ct()),r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:a}}U();var jh=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,Ph=[/\bv\d+\.\d+(?:\.[A-Za-z]+)?\b/g,/\bPhase\s+[A-Ha-h]\b/g,/\bL\d+\b/g,/MASTER-PLAN-[A-Za-z-]+/g],Fh=.95,$h=.85;var go=50;function Uh(e){if(!e)return[];let t=new Set,n=[],s=e.match(jh);if(!s)return n;for(let r of s){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),n.push(o),n.length>=go))break}return n}function Hh(e){if(!e)return[];let t=new Set,n=[];for(let s of Ph){s.lastIndex=0;let r=e.match(s);if(r){for(let o of r){let a=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!a||a.length>64)&&!t.has(a)&&(t.add(a),n.push(a),n.length>=go))break}if(n.length>=go)break}}return n}function Yc(e){let t=h();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function zc(e,t){let n=h(),s=typeof t=="number";if(typeof e=="number"){let o=`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
957
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function Bh(e,t){let n=new Map,s=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let a=t.get(o.id);if(!a)continue;let c=jh(a);if(c.size!==0){s.set(o.id,c);for(let u of c){let d=n.get(u);d?d.push(o.id):n.set(u,[o.id])}}}return{posting:n,vocab:s,startedAt:r}}function Hh(e,t){let n=t.vocab.get(e),s=t.startedAt.get(e)??null;if(!n||!s)return[];let r=new Map;for(let a of n){let c=t.posting.get(a);if(c)for(let u of c){if(u===e)continue;let d=t.startedAt.get(u);if(!d||d>=s)continue;let m=r.get(u);m?m.push(a):r.set(u,[a])}}let o=[];for(let[a,c]of r){let u=c.length;if(u<Lh)continue;let d=t.startedAt.get(a)??null,m=Fh(s,d),f=Ph(m),b=Math.min(1,u/Ih*f);if(b<vh)continue;let T=c.slice(0,12);o.push({target_session_id:a,matched_terms:T,overlap:u,days_apart:Math.round(m*10)/10,recency:Math.round(f*1e3)/1e3,confidence:Math.round(b*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,Dh)}async function Qc(e){if(fo.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=Uh(e.projectId),n=new Map;for(let c of t){let u=$h(c.id);u&&n.set(c.id,u)}let s=Bh(t,n),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let c of t){if(e.signal?.aborted)break;if(typeof e.sinceRowid=="number"&&c.maxMessageRowid<=e.sinceRowid)continue;r.current_session_id=c.id,e.onProgress?.({...r});let u=Hh(c.id,s);for(let d of u)o.push({source_session_id:c.id,target_session_id:d.target_session_id,confidence:d.confidence,matched_terms:d.matched_terms,overlap:d.overlap,recency:d.recency,days_apart:d.days_apart});r.processed_sessions+=1,e.onProgress?.({...r})}let a=[];return o.length>0&&(h().transaction(()=>{for(let d of o)try{let m=fn({source_session_id:d.source_session_id,target_session_id:d.target_session_id,link_type:"citation",confidence:d.confidence,evidence:{matched_terms:d.matched_terms,overlap_count:d.overlap,recency:d.recency,days_apart:d.days_apart},inferred_by:"L2"},{deferMirror:!0});a.push(m.id),r.suggestions_created+=1}catch(m){console.error("[citation-inference] createSuggestion failed:",m)}})(),ct()),r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:a}}U();var Wh=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,qh=[/\bv\d+\.\d+(?:\.[A-Za-z]+)?\b/g,/\bPhase\s+[A-Ha-h]\b/g,/\bL\d+\b/g,/MASTER-PLAN-[A-Za-z-]+/g],Xh=.95,Jh=.85;var _o=50;function Gh(e){if(!e)return[];let t=new Set,n=[],s=e.match(Wh);if(!s)return n;for(let r of s){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),n.push(o),n.length>=_o))break}return n}function Yh(e){if(!e)return[];let t=new Set,n=[];for(let s of qh){s.lastIndex=0;let r=e.match(s);if(r){for(let o of r){let a=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!a||a.length>64)&&!t.has(a)&&(t.add(a),n.push(a),n.length>=_o))break}if(n.length>=_o)break}}return n}function Zc(e){let t=h();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function el(e,t){let n=h(),s=typeof t=="number";if(typeof e=="number"){let o=`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
958
958
  s.project_id
959
959
  FROM messages m
960
960
  JOIN sessions s ON s.id = m.session_id
@@ -969,17 +969,17 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
969
969
  WHERE m.is_sidechain = 0
970
970
  AND m.content_text IS NOT NULL
971
971
  AND length(m.content_text) > 0`+(s?`
972
- AND m.rowid > ?`:"");return s?n.prepare(r).all(t):n.prepare(r).all()}function Bh(e){let t=h(),n=typeof e=="number"?"WHERE s.project_id = ?":"",s=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
972
+ AND m.rowid > ?`:"");return s?n.prepare(r).all(t):n.prepare(r).all()}function zh(e){let t=h(),n=typeof e=="number"?"WHERE s.project_id = ?":"",s=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
973
973
  s.project_id AS project_id,
974
974
  oi.plan_ids_referenced AS plan_ids_json
975
975
  FROM session_output_index oi
976
976
  JOIN sessions s ON s.id = oi.session_id
977
- ${n}`).all(...s)}function Wh(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>typeof n=="string")}catch{}return[]}function Kc(e={}){if(mo.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Yc(e.projectId),n=new Set(t.map(c=>c.id)),s=new Map;for(let c of t)s.set(c.id,c.project_id);let r=h(),o=0;return r.transaction(()=>{for(let c of zc(e.projectId,e.sinceRowid)){if(e.signal?.aborted)break;let u=Uh(c.content_text);if(u.length===0)continue;let d=s.get(c.session_id);if(d!==void 0)for(let m of u){if(m===c.session_id)continue;let f=s.get(m);if(!(f===void 0&&!n.has(m))&&!(f!==void 0&&d!==void 0&&f!==d))try{fn({source_session_id:c.session_id,target_session_id:m,link_type:"citation",confidence:Fh,evidence:{matched_uuid:m,source_message_uuid:c.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"},{deferMirror:!0}),o+=1}catch{}}}})(),o>0&&ct(),{created:o}}function Vc(e={}){let t=Bh(e.projectId);if(t.length===0)return{created:0};let n=new Map;for(let d of t){let m=Wh(d.plan_ids_json);for(let f of m){let b=f.trim().toLowerCase();if(!b)continue;let T=n.get(b);T?T.push({id:d.session_id,project_id:d.project_id}):n.set(b,[{id:d.session_id,project_id:d.project_id}])}}if(n.size===0)return{created:0};let s=Yc(e.projectId),r=new Map;for(let d of s)r.set(d.id,d.project_id);let o=0,a=new Map;for(let d of zc(e.projectId,e.sinceRowid)){if(e.signal?.aborted)break;let m=Hh(d.content_text);if(m.length===0)continue;let f=r.get(d.session_id);if(f!==void 0)for(let b of m){let T=n.get(b);if(T)for(let S of T){if(S.id===d.session_id||S.project_id!==f)continue;let w=a.get(d.session_id);w||(w=new Map,a.set(d.session_id,w));let A=w.get(S.id);A||(A=new Set,w.set(S.id,A)),A.add(b)}}}return h().transaction(()=>{for(let[d,m]of a)for(let[f,b]of m)try{fn({source_session_id:d,target_session_id:f,link_type:"citation",confidence:$h,evidence:{matched_plan_ids:Array.from(b).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"},{deferMirror:!0}),o+=1}catch{}})(),o>0&&ct(),{created:o}}U();we();import{existsSync as yE,mkdirSync as wE,writeFileSync as RE}from"node:fs";import{homedir as kE}from"node:os";import{join as To}from"node:path";Kr();U();Q();import{existsSync as ol,mkdirSync as hE,readFileSync as EE,writeFileSync as bE}from"node:fs";import{homedir as SE}from"node:os";import{join as il}from"node:path";import{z as Le}from"zod";function al(){return process.env.RECALL_HOME??il(SE(),".recall")}function TE(){let e=al();ol(e)||hE(e,{recursive:!0})}function cl(){return il(al(),"config.json")}var Ss=Le.object({enabled:Le.boolean().default(!1),model:Le.string().optional(),ratePerMinute:Le.number().int().min(1).max(600).default(30),lastProcessedSessionId:Le.string().nullable().default(null),backfillPaused:Le.boolean().default(!1),autoExtractEnabled:Le.boolean().default(!1),autoExtractIntervalMinutes:Le.number().int().min(5).max(720).default(60),autoExtractBatchSize:Le.number().int().min(1).max(20).default(1),autoResumeWorker:Le.boolean().default(!1)}),bs={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function ll(){let e=cl();if(!ol(e))return{};try{return JSON.parse(EE(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ae(){let e=ll().semantic;if(!e)return{...bs};let t=Ss.safeParse({...bs,...e});return t.success?t.data:{...bs}}function Ts(e,t="unknown"){TE();let n=ll(),s=Ss.parse({...bs,...n.semantic??{},...e}),r={...n,semantic:s};return bE(cl(),JSON.stringify(r,null,2)),So(s.enabled,t),s}function So(e,t="unknown"){let n=e?"1":"0",s=null;try{h().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
978
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=pooled at=${new Date().toISOString()}`);return}catch(r){s=r;let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] pooled getDb() failed while syncing semantic_enabled=${n}: ${o} \u2014 falling back to raw connection`)}try{let r=new es(ze);try{r.exec(`CREATE TABLE IF NOT EXISTS app_settings (
977
+ ${n}`).all(...s)}function Kh(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>typeof n=="string")}catch{}return[]}function tl(e={}){if(fo.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Zc(e.projectId),n=new Set(t.map(c=>c.id)),s=new Map;for(let c of t)s.set(c.id,c.project_id);let r=h(),o=0;return r.transaction(()=>{for(let c of el(e.projectId,e.sinceRowid)){if(e.signal?.aborted)break;let u=Gh(c.content_text);if(u.length===0)continue;let d=s.get(c.session_id);if(d!==void 0)for(let m of u){if(m===c.session_id)continue;let f=s.get(m);if(!(f===void 0&&!n.has(m))&&!(f!==void 0&&d!==void 0&&f!==d))try{fn({source_session_id:c.session_id,target_session_id:m,link_type:"citation",confidence:Xh,evidence:{matched_uuid:m,source_message_uuid:c.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"},{deferMirror:!0}),o+=1}catch{}}}})(),o>0&&ct(),{created:o}}function nl(e={}){let t=zh(e.projectId);if(t.length===0)return{created:0};let n=new Map;for(let d of t){let m=Kh(d.plan_ids_json);for(let f of m){let b=f.trim().toLowerCase();if(!b)continue;let T=n.get(b);T?T.push({id:d.session_id,project_id:d.project_id}):n.set(b,[{id:d.session_id,project_id:d.project_id}])}}if(n.size===0)return{created:0};let s=Zc(e.projectId),r=new Map;for(let d of s)r.set(d.id,d.project_id);let o=0,a=new Map;for(let d of el(e.projectId,e.sinceRowid)){if(e.signal?.aborted)break;let m=Yh(d.content_text);if(m.length===0)continue;let f=r.get(d.session_id);if(f!==void 0)for(let b of m){let T=n.get(b);if(T)for(let S of T){if(S.id===d.session_id||S.project_id!==f)continue;let R=a.get(d.session_id);R||(R=new Map,a.set(d.session_id,R));let A=R.get(S.id);A||(A=new Set,R.set(S.id,A)),A.add(b)}}}return h().transaction(()=>{for(let[d,m]of a)for(let[f,b]of m)try{fn({source_session_id:d,target_session_id:f,link_type:"citation",confidence:Jh,evidence:{matched_plan_ids:Array.from(b).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"},{deferMirror:!0}),o+=1}catch{}})(),o>0&&ct(),{created:o}}U();we();import{existsSync as OE,mkdirSync as CE,writeFileSync as LE}from"node:fs";import{homedir as vE}from"node:os";import{join as wo}from"node:path";Qr();U();Q();import{existsSync as ul,mkdirSync as RE,readFileSync as kE,writeFileSync as AE}from"node:fs";import{homedir as xE}from"node:os";import{join as dl}from"node:path";import{z as Ce}from"zod";function pl(){return process.env.RECALL_HOME??dl(xE(),".recall")}function NE(){let e=pl();ul(e)||RE(e,{recursive:!0})}function ml(){return dl(pl(),"config.json")}var Ss=Ce.object({enabled:Ce.boolean().default(!1),model:Ce.string().optional(),ratePerMinute:Ce.number().int().min(1).max(600).default(30),lastProcessedSessionId:Ce.string().nullable().default(null),backfillPaused:Ce.boolean().default(!1),autoExtractEnabled:Ce.boolean().default(!1),autoExtractIntervalMinutes:Ce.number().int().min(5).max(720).default(60),autoExtractBatchSize:Ce.number().int().min(1).max(20).default(1),autoResumeWorker:Ce.boolean().default(!1)}),bs={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function gl(){let e=ml();if(!ul(e))return{};try{return JSON.parse(kE(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ae(){let e=gl().semantic;if(!e)return{...bs};let t=Ss.safeParse({...bs,...e});return t.success?t.data:{...bs}}function Ts(e,t="unknown"){NE();let n=gl(),s=Ss.parse({...bs,...n.semantic??{},...e}),r={...n,semantic:s};return AE(ml(),JSON.stringify(r,null,2)),yo(s.enabled,t),s}function yo(e,t="unknown"){let n=e?"1":"0",s=null;try{h().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
978
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=pooled at=${new Date().toISOString()}`);return}catch(r){s=r;let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] pooled getDb() failed while syncing semantic_enabled=${n}: ${o} \u2014 falling back to raw connection`)}try{let r=new es(ze);try{r.pragma("busy_timeout = 15000")}catch(o){console.error(`[semantic-config] failed to set busy_timeout on raw fallback connection (will fail instantly under contention): ${o instanceof Error?o.message:String(o)}`)}try{r.exec(`CREATE TABLE IF NOT EXISTS app_settings (
979
979
  key TEXT PRIMARY KEY,
980
980
  value TEXT NOT NULL
981
981
  );`),r.prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
982
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=fallback at=${new Date().toISOString()}`)}finally{r.close()}}catch(r){let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] raw-connection fallback ALSO failed for semantic_enabled=${n}: ${o} (original: ${s instanceof Error?s.message:String(s)})`)}}var AE=1,xE=12e3,ul=3,NE=[];function OE(){return process.env.RECALL_HOME??To(kE(),".recall")}function dl(){return To(OE(),"semantic")}function LE(){let e=dl();yE(e)||wE(e,{recursive:!0})}function CE(e){let t=h(),n=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
982
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=fallback at=${new Date().toISOString()}`)}finally{r.close()}}catch(r){let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] raw-connection fallback ALSO failed for semantic_enabled=${n}: ${o} (original: ${s instanceof Error?s.message:String(s)})`)}}var IE=1,ME=12e3,fl=3,DE=[];function jE(){return process.env.RECALL_HOME??wo(vE(),".recall")}function _l(){return wo(jE(),"semantic")}function PE(){let e=_l();OE(e)||CE(e,{recursive:!0})}function FE(e){let t=h(),n=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
983
983
  p.name AS project,
984
984
  NULLIF(sa.alias, '') AS alias
985
985
  FROM sessions s
@@ -988,10 +988,10 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
988
988
  WHERE s.id = ?`).get(e);if(!n)return null;let s=t.prepare(`SELECT role, content_text
989
989
  FROM messages
990
990
  WHERE session_id = ? AND is_sidechain = 0
991
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){if(!a.content_text)continue;let c=a.role??"system",u=a.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!u)continue;let d=u.length>1500?u.slice(0,1500)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>xE)break;r.push(m),o+=m.length}return{id:n.id,alias:n.alias,project:n.project,firstUserMessage:n.first_user_message,excerpt:r.join(`
991
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){if(!a.content_text)continue;let c=a.role??"system",u=a.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!u)continue;let d=u.length>1500?u.slice(0,1500)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>ME)break;r.push(m),o+=m.length}return{id:n.id,alias:n.alias,project:n.project,firstUserMessage:n.first_user_message,excerpt:r.join(`
992
992
 
993
- `),messageCount:n.message_count}}function vE(e){return["You are summarizing a Claude Code session for a local semantic-search index. The summary will be stored as plain text and matched against future natural-language queries.","",`Session: ${e.alias??e.id}`,`Project: ${e.project}`,e.firstUserMessage?`Opening prompt: ${e.firstUserMessage}`:"","","Transcript excerpt (truncated):","---",e.excerpt,"---","","Output a single JSON object on one line, with no Markdown fences and no commentary:",'{"summary": "<3 sentences describing what the user was trying to do, what was built or debugged, and the outcome>", "keywords": ["<concept>", "<technology>", "<problem>", ...]}',"","Constraints:","- summary: 3 sentences, plain prose, no bullet points",'- keywords: 10\u201315 lowercase tokens, multi-word entries hyphenated (e.g. "memory-leak"); no duplicates; no generic words like "code" or "session"',"- Output JSON only. Do not echo this prompt."].filter(Boolean).join(`
994
- `)}function IE(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1);try{let o=JSON.parse(r),a=typeof o.summary=="string"?o.summary.trim():"",u=(Array.isArray(o.keywords)?o.keywords:[]).filter(d=>typeof d=="string").map(d=>d.trim().toLowerCase()).filter(d=>d.length>0&&d.length<64);return!a||u.length===0?null:{summary:a,keywords:Array.from(new Set(u)).slice(0,20)}}catch{return null}}function ME(e){let t=h(),n=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
993
+ `),messageCount:n.message_count}}function $E(e){return["You are summarizing a Claude Code session for a local semantic-search index. The summary will be stored as plain text and matched against future natural-language queries.","",`Session: ${e.alias??e.id}`,`Project: ${e.project}`,e.firstUserMessage?`Opening prompt: ${e.firstUserMessage}`:"","","Transcript excerpt (truncated):","---",e.excerpt,"---","","Output a single JSON object on one line, with no Markdown fences and no commentary:",'{"summary": "<3 sentences describing what the user was trying to do, what was built or debugged, and the outcome>", "keywords": ["<concept>", "<technology>", "<problem>", ...]}',"","Constraints:","- summary: 3 sentences, plain prose, no bullet points",'- keywords: 10\u201315 lowercase tokens, multi-word entries hyphenated (e.g. "memory-leak"); no duplicates; no generic words like "code" or "session"',"- Output JSON only. Do not echo this prompt."].filter(Boolean).join(`
994
+ `)}function UE(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1);try{let o=JSON.parse(r),a=typeof o.summary=="string"?o.summary.trim():"",u=(Array.isArray(o.keywords)?o.keywords:[]).filter(d=>typeof d=="string").map(d=>d.trim().toLowerCase()).filter(d=>d.length>0&&d.length<64);return!a||u.length===0?null:{summary:a,keywords:Array.from(new Set(u)).slice(0,20)}}catch{return null}}function BE(e){let t=h(),n=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
995
995
  (session_id, summary, keywords, model, source_message_count, generated_at)
996
996
  VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
997
997
  ON CONFLICT(session_id) DO UPDATE SET
@@ -999,16 +999,16 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
999
999
  keywords = excluded.keywords,
1000
1000
  model = excluded.model,
1001
1001
  source_message_count = excluded.source_message_count,
1002
- generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:n,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),LE();let s=To(dl(),`${e.sessionId}.json`);RE(s,JSON.stringify({version:AE,session_id:e.sessionId,summary:e.summary,keywords:e.keywords,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt},null,2))}var ys=null;function DE(){let t=ae().ratePerMinute,n=t/6e4;return(!ys||ys.capacity!==t)&&(ys={tokens:t,capacity:t,refillPerMs:n,lastRefill:Date.now()}),ys}function jE(e){let t=Date.now(),n=t-e.lastRefill;n>0&&(e.tokens=Math.min(e.capacity,e.tokens+n*e.refillPerMs),e.lastRefill=t)}async function PE(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=DE();if(jE(t),t.tokens>=1){t.tokens-=1;return}let n=1-t.tokens,s=Math.max(50,Math.ceil(n/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(s,5e3)))}}async function ws(e,t={}){let n=ae();if(!n.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!me())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let s=CE(e);if(!s)return{sessionId:e,ok:!1,reason:"session-not-found"};if(s.messageCount<ul)return{sessionId:e,ok:!1,reason:"too-short"};if(!s.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await PE(t.signal);let r=vE(s),o=await dt(r,NE,{model:n.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:n.model??null};let a=IE(o.stdout);return a?(ME({sessionId:s.id,summary:a.summary,keywords:a.keywords,model:n.model??null,sourceMessageCount:s.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:n.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:n.model??null}}async function Rs(e={}){let t=ae();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let n=h(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let a=n.prepare(`SELECT s.id
1002
+ generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:n,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),PE();let s=wo(_l(),`${e.sessionId}.json`);LE(s,JSON.stringify({version:IE,session_id:e.sessionId,summary:e.summary,keywords:e.keywords,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt},null,2))}var ys=null;function HE(){let t=ae().ratePerMinute,n=t/6e4;return(!ys||ys.capacity!==t)&&(ys={tokens:t,capacity:t,refillPerMs:n,lastRefill:Date.now()}),ys}function WE(e){let t=Date.now(),n=t-e.lastRefill;n>0&&(e.tokens=Math.min(e.capacity,e.tokens+n*e.refillPerMs),e.lastRefill=t)}async function qE(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=HE();if(WE(t),t.tokens>=1){t.tokens-=1;return}let n=1-t.tokens,s=Math.max(50,Math.ceil(n/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(s,5e3)))}}async function ws(e,t={}){let n=ae();if(!n.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!me())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let s=FE(e);if(!s)return{sessionId:e,ok:!1,reason:"session-not-found"};if(s.messageCount<fl)return{sessionId:e,ok:!1,reason:"too-short"};if(!s.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await qE(t.signal);let r=$E(s),o=await dt(r,DE,{model:n.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:n.model??null};let a=UE(o.stdout);return a?(BE({sessionId:s.id,summary:a.summary,keywords:a.keywords,model:n.model??null,sourceMessageCount:s.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:n.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:n.model??null}}async function Rs(e={}){let t=ae();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let n=h(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let a=n.prepare(`SELECT s.id
1003
1003
  FROM sessions s
1004
1004
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
1005
1005
  WHERE ${o}
1006
1006
  ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
1007
- LIMIT @limit`).all(r),c={total:a.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(c);for(let{id:u}of a){if(e.signal?.aborted||ae().backfillPaused)break;c.currentSessionId=u,e.onProgress?.({...c});try{(await ws(u,{signal:e.signal})).ok?c.ok+=1:c.failed+=1}catch(m){c.failed+=1,console.error("[semantic.backfill] failed for",u,m)}c.processed+=1,Ts({lastProcessedSessionId:u},"pipeline"),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function pl(e){if(!ae().enabled)return;let s=h().prepare(`SELECT s.message_count, s.ended_at,
1007
+ LIMIT @limit`).all(r),c={total:a.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(c);for(let{id:u}of a){if(e.signal?.aborted||ae().backfillPaused)break;c.currentSessionId=u,e.onProgress?.({...c});try{(await ws(u,{signal:e.signal})).ok?c.ok+=1:c.failed+=1}catch(m){c.failed+=1,console.error("[semantic.backfill] failed for",u,m)}c.processed+=1,Ts({lastProcessedSessionId:u},"pipeline"),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function hl(e){if(!ae().enabled)return;let s=h().prepare(`SELECT s.message_count, s.ended_at,
1008
1008
  ss.generated_at, ss.source_message_count
1009
1009
  FROM sessions s
1010
1010
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
1011
- WHERE s.id = ?`).get(e);if(s&&!(s.message_count<ul)&&!(s.generated_at&&s.source_message_count!=null&&s.source_message_count>=s.message_count))try{await ws(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function yo(){let e=ae(),t=h(),n=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,s=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:me(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:n,processedSessions:s,pendingSessions:Math.max(0,n-s),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}U();import{createHash as WE}from"node:crypto";var FE=[{name:"Anthropic API key",regex:/sk-ant-[a-zA-Z0-9_\-]{40,}/g,severity:"high"},{name:"OpenAI API key",regex:/sk-(?:proj-)?[a-zA-Z0-9]{32,}/g,severity:"high"},{name:"AWS access key ID",regex:/AKIA[0-9A-Z]{16}/g,severity:"high"},{name:"GitHub PAT",regex:/gh[pousr]_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Stripe live/test key",regex:/(?:sk|rk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,severity:"high"},{name:"Slack token",regex:/xox[abprs]-[A-Za-z0-9\-]{10,}/g,severity:"high"},{name:"Google API key",regex:/AIza[0-9A-Za-z_\-]{35}/g,severity:"high"},{name:"Private key block",regex:/-----BEGIN (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,severity:"high"},{name:"Apify token",regex:/apify_api_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Notion integration",regex:/(?:secret_|ntn_)[A-Za-z0-9]{40,}/g,severity:"high"},{name:"Vercel token",regex:/vercel_[A-Za-z0-9]{24,}/g,severity:"high"},{name:"Supabase service key",regex:/sbp_[A-Za-z0-9]{40,}/g,severity:"high"},{name:"SendGrid key",regex:/SG\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}/g,severity:"high"},{name:"Mailgun key",regex:/key-[a-f0-9]{32}/g,severity:"high"},{name:"Twilio SID",regex:/AC[a-f0-9]{32}/g,severity:"high"},{name:"Discord bot token",regex:/[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27,38}/g,severity:"high"},{name:"npm token",regex:/npm_[A-Za-z0-9]{36}/g,severity:"high"},{name:"HuggingFace token",regex:/hf_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"Replicate token",regex:/r8_[A-Za-z0-9]{32,}/g,severity:"high"},{name:"Figma token",regex:/figd_[A-Za-z0-9_\-]{30,}/g,severity:"high"},{name:"Linear key",regex:/lin_api_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"DigitalOcean token",regex:/dop_v1_[a-f0-9]{64}/g,severity:"high"},{name:"Generic provider token",regex:/\b[a-z][a-z0-9]{2,20}_(?:api|pat|token|sk|pk|key|auth)_(?=[A-Za-z0-9_\-]*\d)[A-Za-z0-9_\-]{20,}\b/g,severity:"high"},{name:"Bearer token",regex:/\b[Bb]earer\s+[A-Za-z0-9_\-\.=]{24,}\b/g,severity:"medium"},{name:"Slack webhook URL",regex:/https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Discord webhook URL",regex:/https:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_\-]{40,}/g,severity:"high"},{name:"Teams webhook URL",regex:/https:\/\/[a-zA-Z0-9.\-]+\.webhook\.office\.com\/webhookb2\/[A-Za-z0-9@_\-\/]{30,}/g,severity:"high"},{name:"Secret near keyword",regex:/\b(?:webhook[_\s\-]?secret|signing[_\s\-]?secret|webhook[_\s\-]?signing[_\s\-]?secret|api[_\s\-]?secret|client[_\s\-]?secret|private[_\s\-]?key|access[_\s\-]?token|auth[_\s\-]?token|api[_\s\-]?key)\b[\s\S]{0,200}?\b(?:[a-fA-F0-9]{32,}|[A-Za-z0-9+/_\-]{20,}(?:\.[A-Za-z0-9+/_\-]{10,}){1,2}|[A-Za-z0-9+/_\-]{40,}={0,2})\b/gi,severity:"high"},{name:"JWT",regex:/eyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}/g,severity:"medium"},{name:"URL with password",regex:/https?:\/\/[^:\s/@]+:[^@\s]{6,}@[^\s/]+/g,severity:"high"},{name:"Password assignment",regex:/(?<![A-Za-z])(?:password|passwd|pwd|secret|token|api[_\-]?key|access[_\-]?key|auth[_\-]?token|webhook[_\-]?secret|client[_\-]?secret|private[_\-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_\-+/=]{16,}/gi,severity:"high"}],ml=64*1024,$E="[OVERSIZED-BLOB-SKIPPED]";function gl(e){return e===32||e===9||e===10||e===13||e===12||e===11}function UE(e){let t=e.length,n=-1,s=-1,r=0;for(let c=0;c<t;c++)if(gl(e.charCodeAt(c)))r=0;else if(++r>ml){s=c;break}if(s===-1)return e;let o=[],a=0;n=-1;for(let c=0;c<=t;c++){let u=c===t||gl(e.charCodeAt(c));!u&&n===-1?n=c:u&&n!==-1&&(c-n>ml&&(o.push(e.slice(a,n)),o.push($E),a=c),n=-1)}return o.push(e.slice(a)),o.join("")}function HE(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),n=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-n.length))}${n}`}function BE(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(36)}function be(e){if(!e)return{redacted:e,count:0};e=UE(e);let t=e,n=0,s=new Set;for(let r of FE)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${BE(o)}`;return s.has(a)||(s.add(a),n+=1),`[REDACTED ${r.name}: ${HE(o)}]`});return{redacted:t,count:n}}we();var ko=1,mt="claude-haiku-4-5-20251001",qE=3,XE=32e3,fl=2e3,JE=30,GE=30,YE=30,zE=30;function KE(e){let n=h().prepare(`SELECT s.id,
1011
+ WHERE s.id = ?`).get(e);if(s&&!(s.message_count<fl)&&!(s.generated_at&&s.source_message_count!=null&&s.source_message_count>=s.message_count))try{await ws(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function Ro(){let e=ae(),t=h(),n=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,s=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:me(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:n,processedSessions:s,pendingSessions:Math.max(0,n-s),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}U();import{createHash as KE}from"node:crypto";var XE=[{name:"Anthropic API key",regex:/sk-ant-[a-zA-Z0-9_\-]{40,}/g,severity:"high"},{name:"OpenAI API key",regex:/sk-(?:proj-)?[a-zA-Z0-9]{32,}/g,severity:"high"},{name:"AWS access key ID",regex:/AKIA[0-9A-Z]{16}/g,severity:"high"},{name:"GitHub PAT",regex:/gh[pousr]_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Stripe live/test key",regex:/(?:sk|rk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,severity:"high"},{name:"Slack token",regex:/xox[abprs]-[A-Za-z0-9\-]{10,}/g,severity:"high"},{name:"Google API key",regex:/AIza[0-9A-Za-z_\-]{35}/g,severity:"high"},{name:"Private key block",regex:/-----BEGIN (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,severity:"high"},{name:"Apify token",regex:/apify_api_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Notion integration",regex:/(?:secret_|ntn_)[A-Za-z0-9]{40,}/g,severity:"high"},{name:"Vercel token",regex:/vercel_[A-Za-z0-9]{24,}/g,severity:"high"},{name:"Supabase service key",regex:/sbp_[A-Za-z0-9]{40,}/g,severity:"high"},{name:"SendGrid key",regex:/SG\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}/g,severity:"high"},{name:"Mailgun key",regex:/key-[a-f0-9]{32}/g,severity:"high"},{name:"Twilio SID",regex:/AC[a-f0-9]{32}/g,severity:"high"},{name:"Discord bot token",regex:/[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27,38}/g,severity:"high"},{name:"npm token",regex:/npm_[A-Za-z0-9]{36}/g,severity:"high"},{name:"HuggingFace token",regex:/hf_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"Replicate token",regex:/r8_[A-Za-z0-9]{32,}/g,severity:"high"},{name:"Figma token",regex:/figd_[A-Za-z0-9_\-]{30,}/g,severity:"high"},{name:"Linear key",regex:/lin_api_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"DigitalOcean token",regex:/dop_v1_[a-f0-9]{64}/g,severity:"high"},{name:"Generic provider token",regex:/\b[a-z][a-z0-9]{2,20}_(?:api|pat|token|sk|pk|key|auth)_(?=[A-Za-z0-9_\-]*\d)[A-Za-z0-9_\-]{20,}\b/g,severity:"high"},{name:"Bearer token",regex:/\b[Bb]earer\s+[A-Za-z0-9_\-\.=]{24,}\b/g,severity:"medium"},{name:"Slack webhook URL",regex:/https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Discord webhook URL",regex:/https:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_\-]{40,}/g,severity:"high"},{name:"Teams webhook URL",regex:/https:\/\/[a-zA-Z0-9.\-]+\.webhook\.office\.com\/webhookb2\/[A-Za-z0-9@_\-\/]{30,}/g,severity:"high"},{name:"Secret near keyword",regex:/\b(?:webhook[_\s\-]?secret|signing[_\s\-]?secret|webhook[_\s\-]?signing[_\s\-]?secret|api[_\s\-]?secret|client[_\s\-]?secret|private[_\s\-]?key|access[_\s\-]?token|auth[_\s\-]?token|api[_\s\-]?key)\b[\s\S]{0,200}?\b(?:[a-fA-F0-9]{32,}|[A-Za-z0-9+/_\-]{20,}(?:\.[A-Za-z0-9+/_\-]{10,}){1,2}|[A-Za-z0-9+/_\-]{40,}={0,2})\b/gi,severity:"high"},{name:"JWT",regex:/eyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}/g,severity:"medium"},{name:"URL with password",regex:/https?:\/\/[^:\s/@]+:[^@\s]{6,}@[^\s/]+/g,severity:"high"},{name:"Password assignment",regex:/(?<![A-Za-z])(?:password|passwd|pwd|secret|token|api[_\-]?key|access[_\-]?key|auth[_\-]?token|webhook[_\-]?secret|client[_\-]?secret|private[_\-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_\-+/=]{16,}/gi,severity:"high"}],El=64*1024,JE="[OVERSIZED-BLOB-SKIPPED]";function bl(e){return e===32||e===9||e===10||e===13||e===12||e===11}function GE(e){let t=e.length,n=-1,s=-1,r=0;for(let c=0;c<t;c++)if(bl(e.charCodeAt(c)))r=0;else if(++r>El){s=c;break}if(s===-1)return e;let o=[],a=0;n=-1;for(let c=0;c<=t;c++){let u=c===t||bl(e.charCodeAt(c));!u&&n===-1?n=c:u&&n!==-1&&(c-n>El&&(o.push(e.slice(a,n)),o.push(JE),a=c),n=-1)}return o.push(e.slice(a)),o.join("")}function YE(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),n=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-n.length))}${n}`}function zE(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(36)}function be(e){if(!e)return{redacted:e,count:0};e=GE(e);let t=e,n=0,s=new Set;for(let r of XE)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${zE(o)}`;return s.has(a)||(s.add(a),n+=1),`[REDACTED ${r.name}: ${YE(o)}]`});return{redacted:t,count:n}}we();var xo=1,mt="claude-haiku-4-5-20251001",VE=3,QE=32e3,Sl=2e3,ZE=30,eb=30,tb=30,nb=30;function sb(e){let n=h().prepare(`SELECT s.id,
1012
1012
  NULLIF(sa.alias, '') AS alias,
1013
1013
  s.auto_title,
1014
1014
  s.auto_title_source,
@@ -1019,15 +1019,15 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1019
1019
  FROM sessions s
1020
1020
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1021
1021
  LEFT JOIN projects p ON p.id = s.project_id
1022
- WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function _l(e,t={}){if(e.message_count<qE)return{eligible:!1,reason:"too-short"};let n=e.title_quality;if(n==="programmatic"||n==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let s=lt(e.id);if(s&&s.extractor_version>=ko)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function VE(e){let t=KE(e);if(!t)return null;let s=h().prepare(`SELECT role, content_text
1022
+ WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function Tl(e,t={}){if(e.message_count<VE)return{eligible:!1,reason:"too-short"};let n=e.title_quality;if(n==="programmatic"||n==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let s=lt(e.id);if(s&&s.extractor_version>=xo)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function rb(e){let t=sb(e);if(!t)return null;let s=h().prepare(`SELECT role, content_text
1023
1023
  FROM messages
1024
1024
  WHERE session_id = ?
1025
1025
  AND is_sidechain = 0
1026
1026
  AND content_text IS NOT NULL
1027
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>fl?u.slice(0,fl)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>XE)break;r.push(m),o+=m.length}return{meta:t,excerpt:r.join(`
1027
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>Sl?u.slice(0,Sl)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>QE)break;r.push(m),o+=m.length}return{meta:t,excerpt:r.join(`
1028
1028
 
1029
- `)}}function QE(e){let{meta:t}=e;return["You are extracting a structured Output Index from a Claude Code session for a local knowledge graph.","","Read the transcript and produce a single JSON object with EXACTLY these fields. Output JSON only \u2014 no Markdown fences, no commentary, no explanation.","","Fields (every field MUST appear; use [] for empty):","- files_written: array of strings. Relative or absolute file paths the assistant created, edited, or extensively discussed (Write/Edit tool calls or paths quoted verbatim).",'- brands_mentioned: array of strings. Distinct proper-noun company / product / brand names mentioned. Examples of valid: "Acme Group", "Apollo", "TikTok", "Cloudflare R2". NOT generic terms like "the company" or "users".','- terms_introduced: array of objects { "term": "<lowercase phrase>", "freq": <int> }. Distinctive multi-word noun phrases the session introduced or extensively discussed. Skip generic words. Cap at 30 entries.','- plan_ids_referenced: array of strings. Planning identifiers like "v0.18.A", "Phase D", "L3", "MASTER-PLAN-cognitive-graph". Empty array if none.','- bug_signatures: array of objects { "error_type": "<class or null>", "snippet": "<first line of the error>", "file": "<path or null>" }. Errors actually encountered in the session (not hypothetical). The error_type is the exception class (e.g. "TypeError", "ReferenceError") or null if unspecified.',"","Hard constraints:","- Do NOT fabricate. If a field has no concrete content from the transcript, output an empty array.","- Output JSON ONLY. No backticks, no markdown, no preamble.","- terms_introduced \u2264 30 entries; brands_mentioned \u2264 30 distinct entries; plan_ids_referenced \u2264 30; bug_signatures \u2264 30.","",`Session: ${t.alias??t.id.slice(0,8)}`,`Project: ${t.project??"unknown"}`,t.first_user_message?`Opening prompt: ${t.first_user_message.slice(0,500)}`:"","","Transcript excerpt (may be truncated):","---",e.excerpt,"---"].filter(Boolean).join(`
1030
- `)}function ZE(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function wo(e,t,n=!1){if(!Array.isArray(e))return[];let s=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=n?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!s.has(a)&&(s.add(a),r.push(a),r.length>=t))break}return r}function eb(e,t){if(!Array.isArray(e))return[];let n=new Set,s=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,a=r.freq;if(typeof o!="string")continue;let c=o.trim().toLowerCase();if(!c||c.length>128||n.has(c))continue;n.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(s.push({term:c,frequency:u}),s.length>=t)break}return s}function tb(e,t){if(!Array.isArray(e))return[];let n=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.error_type,o=s.snippet,a=s.file,c=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",u=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!u)continue;let d=typeof a=="string"&&a.trim().length>0?a.trim().slice(0,256):null,m=WE("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(n.push({error_type:c,message_hash:m,snippet:u,file:d}),n.length>=t)break}return n}function nb(e){let t=e.trim();try{let b=JSON.parse(t);typeof b.result=="string"&&(t=b.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1),o;try{o=JSON.parse(r)}catch{return null}let a=wo(o.files_written,200),c=wo(o.brands_mentioned,GE),u=eb(o.terms_introduced,JE),d=wo(o.plan_ids_referenced,YE),m=tb(o.bug_signatures,zE);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&m.length===0&&!ZE(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:m}}var Ro=null;async function sb(e,t){return Ro?Ro(e,t):dt(e,[],{model:t})}async function Ao(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let n=VE(e);if(!n)return{session_id:e,ok:!1,skipped:"session-not-found"};let s=_l(n.meta,{force:t.force});if(!s.eligible)return{session_id:e,ok:!1,skipped:s.reason};if(!Ro&&!me())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=QE(n),o=t.model??mt,a=await sb(r,o);if(!a.success){let m=(a.stderr||a.stdout||"").slice(0,400),f=m?be(m).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:f}}let c=nb(a.stdout);if(!c){let m=a.stdout.slice(0,400),f=m?be(m).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:f}}let u=rb(a.stdout),d=Jc({session_id:e,files_written:c.files_written,brands_mentioned:c.brands_mentioned,terms_introduced:c.terms_introduced,plan_ids_referenced:c.plan_ids_referenced,bug_signatures:c.bug_signatures,raw_extraction:{model:o,usage:u,raw_response_excerpt:a.stdout.slice(0,4e3)},extractor_version:ko});return{session_id:e,ok:!0,index:d,usage:u}}function rb(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let n=t.usage,s={};return typeof n.input_tokens=="number"&&(s.input_tokens=n.input_tokens),typeof n.output_tokens=="number"&&(s.output_tokens=n.output_tokens),s}}catch{}return null}function Lt(e={}){let t=h(),n=[],s=[];typeof e.projectId=="number"&&(n.push("s.project_id = ?"),s.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=n.length?`WHERE ${n.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
1029
+ `)}}function ob(e){let{meta:t}=e;return["You are extracting a structured Output Index from a Claude Code session for a local knowledge graph.","","Read the transcript and produce a single JSON object with EXACTLY these fields. Output JSON only \u2014 no Markdown fences, no commentary, no explanation.","","Fields (every field MUST appear; use [] for empty):","- files_written: array of strings. Relative or absolute file paths the assistant created, edited, or extensively discussed (Write/Edit tool calls or paths quoted verbatim).",'- brands_mentioned: array of strings. Distinct proper-noun company / product / brand names mentioned. Examples of valid: "Acme Group", "Apollo", "TikTok", "Cloudflare R2". NOT generic terms like "the company" or "users".','- terms_introduced: array of objects { "term": "<lowercase phrase>", "freq": <int> }. Distinctive multi-word noun phrases the session introduced or extensively discussed. Skip generic words. Cap at 30 entries.','- plan_ids_referenced: array of strings. Planning identifiers like "v0.18.A", "Phase D", "L3", "MASTER-PLAN-cognitive-graph". Empty array if none.','- bug_signatures: array of objects { "error_type": "<class or null>", "snippet": "<first line of the error>", "file": "<path or null>" }. Errors actually encountered in the session (not hypothetical). The error_type is the exception class (e.g. "TypeError", "ReferenceError") or null if unspecified.',"","Hard constraints:","- Do NOT fabricate. If a field has no concrete content from the transcript, output an empty array.","- Output JSON ONLY. No backticks, no markdown, no preamble.","- terms_introduced \u2264 30 entries; brands_mentioned \u2264 30 distinct entries; plan_ids_referenced \u2264 30; bug_signatures \u2264 30.","",`Session: ${t.alias??t.id.slice(0,8)}`,`Project: ${t.project??"unknown"}`,t.first_user_message?`Opening prompt: ${t.first_user_message.slice(0,500)}`:"","","Transcript excerpt (may be truncated):","---",e.excerpt,"---"].filter(Boolean).join(`
1030
+ `)}function ib(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function ko(e,t,n=!1){if(!Array.isArray(e))return[];let s=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=n?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!s.has(a)&&(s.add(a),r.push(a),r.length>=t))break}return r}function ab(e,t){if(!Array.isArray(e))return[];let n=new Set,s=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,a=r.freq;if(typeof o!="string")continue;let c=o.trim().toLowerCase();if(!c||c.length>128||n.has(c))continue;n.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(s.push({term:c,frequency:u}),s.length>=t)break}return s}function cb(e,t){if(!Array.isArray(e))return[];let n=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.error_type,o=s.snippet,a=s.file,c=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",u=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!u)continue;let d=typeof a=="string"&&a.trim().length>0?a.trim().slice(0,256):null,m=KE("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(n.push({error_type:c,message_hash:m,snippet:u,file:d}),n.length>=t)break}return n}function lb(e){let t=e.trim();try{let b=JSON.parse(t);typeof b.result=="string"&&(t=b.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1),o;try{o=JSON.parse(r)}catch{return null}let a=ko(o.files_written,200),c=ko(o.brands_mentioned,eb),u=ab(o.terms_introduced,ZE),d=ko(o.plan_ids_referenced,tb),m=cb(o.bug_signatures,nb);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&m.length===0&&!ib(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:m}}var Ao=null;async function ub(e,t){return Ao?Ao(e,t):dt(e,[],{model:t})}async function No(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let n=rb(e);if(!n)return{session_id:e,ok:!1,skipped:"session-not-found"};let s=Tl(n.meta,{force:t.force});if(!s.eligible)return{session_id:e,ok:!1,skipped:s.reason};if(!Ao&&!me())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=ob(n),o=t.model??mt,a=await ub(r,o);if(!a.success){let m=(a.stderr||a.stdout||"").slice(0,400),f=m?be(m).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:f}}let c=lb(a.stdout);if(!c){let m=a.stdout.slice(0,400),f=m?be(m).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:f}}let u=db(a.stdout),d=Vc({session_id:e,files_written:c.files_written,brands_mentioned:c.brands_mentioned,terms_introduced:c.terms_introduced,plan_ids_referenced:c.plan_ids_referenced,bug_signatures:c.bug_signatures,raw_extraction:{model:o,usage:u,raw_response_excerpt:a.stdout.slice(0,4e3)},extractor_version:xo});return{session_id:e,ok:!0,index:d,usage:u}}function db(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let n=t.usage,s={};return typeof n.input_tokens=="number"&&(s.input_tokens=n.input_tokens),typeof n.output_tokens=="number"&&(s.output_tokens=n.output_tokens),s}}catch{}return null}function Ct(e={}){let t=h(),n=[],s=[];typeof e.projectId=="number"&&(n.push("s.project_id = ?"),s.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=n.length?`WHERE ${n.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
1031
1031
  NULLIF(sa.alias, '') AS alias,
1032
1032
  s.auto_title,
1033
1033
  s.auto_title_source,
@@ -1039,21 +1039,21 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1039
1039
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1040
1040
  LEFT JOIN projects p ON p.id = s.project_id
1041
1041
  ${o}
1042
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(...s),c=[],u=new Map;for(let d of a){let m={...d,alias_source:d.alias?"manual":null},f=_l(m,{force:e.force});if(!f.eligible){let b=f.reason??"session-not-found";u.set(b,(u.get(b)??0)+1);continue}if(c.push(m),c.length>=r)break}return{eligible:c,skipped:u}}async function hl(e={}){let t=Lt({projectId:e.projectId,limit:e.limit,force:e.force}),n={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())n.skipped+=r;e.onProgress?.({...n});let s=[];for(let r of t.eligible){if(e.signal?.aborted)break;n.current_session_id=r.id,e.onProgress?.({...n});let o=await Ao(r.id,{model:e.model,force:e.force,signal:e.signal});s.push(o),e.onResult?.(o),o.ok?n.ok+=1:o.skipped?n.skipped+=1:n.failed+=1,o.usage?.input_tokens&&(n.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(n.total_output_tokens+=o.usage.output_tokens),n.processed+=1,e.onProgress?.({...n})}return n.current_session_id=null,e.onProgress?.({...n}),{progress:n,results:s}}we();var re="[daemon:inference]",ft=!1;function Sb(){ft=!0}var vo=!1,Io=!1,Mo=!1,Do=!1,jo=!1,Ol=0,Po=!1,Fo=!1,Os=3,gt=0,Ct=!1,Ls=null,vt=null,Ll=!1;function Tb(){return h().prepare("SELECT id, name FROM projects").all()}async function Cl(){if(vo||ft)return;vo=!0;let e=Date.now();try{let n=(await Ic({minClusterSize:2})).progress;(n.clusters_created||n.clusters_merged||n.members_added)&&console.log(`${re} bug-patterns: created=${n.clusters_created} merged=${n.clusters_merged} members_added=${n.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} bug-patterns failed:`,t)}finally{vo=!1}}async function vl(){if(Io||ft)return;Io=!0;let e=Date.now();try{let t=h(),n=$l(t,Ml),s=t.prepare("SELECT COALESCE(MAX(rowid), 0) AS m FROM messages").get(),r=0,o=0,a=!1;for(let c of Tb())try{let u=await Gc({projectId:c.id,sinceRowid:n});r+=u.progress.suggestions_created,o+=1}catch(u){a=!0,console.error(`${re} citations failed for project "${c.name}":`,u)}a||Ul(t,Ml,s.m),r>0&&console.log(`${re} citations: ${r} suggestion(s) across ${o} project(s) since_rowid=${n} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} citations failed:`,t)}finally{Io=!1}}var Il="l1_inference_watermark_rowid",Ml="citations_inference_watermark_rowid";function $l(e,t){let n=e.prepare("SELECT value FROM app_settings WHERE key = ?").get(t);if(!n)return 0;let s=Number(n.value);return Number.isFinite(s)&&s>=0?s:0}function Ul(e,t,n){e.prepare("INSERT INTO app_settings(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(t,String(n))}function Dl(){if(Mo||ft)return;Mo=!0;let e=Date.now();try{let t=h(),n=$l(t,Il),s=t.prepare("SELECT COALESCE(MAX(rowid), 0) AS m FROM messages").get(),r=Kc({sinceRowid:n}),o=Vc({sinceRowid:n});Ul(t,Il,s.m),r.created+o.created>0&&console.log(`${re} l1: uuid=${r.created} plan=${o.created} since_rowid=${n} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} l1 failed:`,t)}finally{Mo=!1}}async function jl(){if(Do||ft)return;let e=ae();if(!e.enabled||e.backfillPaused)return;Do=!0;let t=Date.now();try{let n=await Rs({limit:25});n.processed>0&&console.log(`${re} backfill: processed=${n.processed} ok=${n.ok} failed=${n.failed} (${Date.now()-t}ms)`)}catch(n){console.error(`${re} backfill failed:`,n)}finally{Do=!1}}async function Pl(){if(jo||ft)return;let e=ae();if(e.autoExtractEnabled&&!Ll&&Ct&&(yb(),console.log(`${re} auto-extract: circuit breaker reset (config toggled on).`)),Ll=e.autoExtractEnabled,!e.autoExtractEnabled||Ct)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-Ol<t)return;if(!me()){Po||(console.log(`${re} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),Po=!0);return}if(Po=!1,!await sc().catch(()=>!1)){Fo||(console.log(`${re} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),Fo=!0);return}Fo=!1,jo=!0,Ol=Date.now();let s=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=Lt({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,m=new Map,f=null;for(let S of o){let w=await Ao(S.id,{model:mt});if(w.ok)a+=1;else if(!w.skipped){c+=1;let A=w.failed??"unknown";m.set(A,(m.get(A)??0)+1),!f&&w.stderr_excerpt&&(f=w.stderr_excerpt)}w.usage?.input_tokens&&(u+=w.usage.input_tokens),w.usage?.output_tokens&&(d+=w.usage.output_tokens)}let b=m.size>0?" reasons="+Array.from(m.entries()).map(([S,w])=>`${S}:${w}`).join(","):"";if(console.log(`${re} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-s}ms)${b}`),f){let w=be(f).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${re} auto-extract: first failure excerpt: ${w}`)}o.length>0&&u===0&&d===0?(gt+=1,gt>=Os&&(Ct=!0,Ls=Date.now(),vt=`${Os} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${re} auto-extract: CIRCUIT BREAKER tripped \u2014 ${vt}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(gt=0)}catch(r){console.error(`${re} auto-extract failed:`,r),gt+=1,gt>=Os&&(Ct=!0,Ls=Date.now(),vt=`${Os} consecutive thrown errors`,console.log(`${re} auto-extract: CIRCUIT BREAKER tripped \u2014 ${vt}. Pausing nibbler.`))}finally{jo=!1}}function Hl(){return{broken:Ct,brokenAt:Ls,reason:vt,consecutiveZeroTokenRuns:gt}}function yb(){Ct=!1,Ls=null,vt=null,gt=0}var $o=!1;async function Fl(){if($o||ft)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(Oo(),wl)),n=e();if(!n.autoArchiveEnabled)return;let s=1380*60*1e3;if(n.lastRunAt&&Date.now()-Date.parse(n.lastRunAt)<s)return;$o=!0;let r=Date.now();try{let a=new Date(Date.now()-n.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(Nl(),xl)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${re} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${re} auto-archive failed: ${a}`)}finally{$o=!1}}function Bl(){let m=[],f=[];m.push(setTimeout(()=>{Cl()},9e4)),f.push(setInterval(()=>{Cl()},18e5)),m.push(setTimeout(()=>{vl()},18e4)),f.push(setInterval(()=>{vl()},36e5)),m.push(setTimeout(()=>Dl(),12e4)),f.push(setInterval(()=>Dl(),18e5)),m.push(setTimeout(()=>{jl()},24e4)),f.push(setInterval(()=>{jl()},9e5)),m.push(setTimeout(()=>{Pl()},3e5)),f.push(setInterval(()=>{Pl()},9e5));let b=3600*1e3,T=300*1e3;return m.push(setTimeout(()=>{Fl()},T)),f.push(setInterval(()=>{Fl()},b)),console.log(`${re} scheduled: bug-patterns (30m), citations (60m), l1 (30m), backfill (15m, when enabled), auto-extract (60m, when enabled), auto-archive (24h, when enabled)`),{startupTimers:m,intervalTimers:f,stop:()=>{Sb();for(let S of m)clearTimeout(S);for(let S of f)clearInterval(S)}}}U();import{serveStatic as mf}from"@hono/node-server/serve-static";import{existsSync as fA,readFileSync as Fa}from"node:fs";import{stat as _A,readFile as hA,realpath as EA}from"node:fs/promises";import{timingSafeEqual as bA}from"node:crypto";import{homedir as SA}from"node:os";import{dirname as Ha,join as qr}from"node:path";import{fileURLToPath as Ba}from"node:url";import{existsSync as Pi,readdirSync as _w,readFileSync as Qd,statSync as hw,statfsSync as Zd}from"node:fs";import{homedir as Ew}from"node:os";import{join as ep}from"node:path";Ns();U();ts();Q();Q();U();import{watch as ty}from"chokidar";import{readdirSync as ny,statSync as Ut}from"node:fs";import{createReadStream as wb}from"node:fs";function Cs(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Bo(e){let t=e?.usage;if(!t||typeof t!="object")return null;let n={inputTokens:Cs(t.input_tokens),outputTokens:Cs(t.output_tokens),cacheCreateTokens:Cs(t.cache_creation_input_tokens),cacheReadTokens:Cs(t.cache_read_input_tokens)};return n.inputTokens===0&&n.outputTokens===0&&n.cacheCreateTokens===0&&n.cacheReadTokens===0?null:n}var Rb=/\x1B\[[0-9;]*[a-zA-Z]/g;function Uo(e){return e.replace(Rb,"")}var Ho=12e3;function Wl(e,t){if(e.length<=Ho)return e;let n=e.slice(0,Ho),s=e.length-Ho;return`${n}
1042
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(...s),c=[],u=new Map;for(let d of a){let m={...d,alias_source:d.alias?"manual":null},f=Tl(m,{force:e.force});if(!f.eligible){let b=f.reason??"session-not-found";u.set(b,(u.get(b)??0)+1);continue}if(c.push(m),c.length>=r)break}return{eligible:c,skipped:u}}async function yl(e={}){let t=Ct({projectId:e.projectId,limit:e.limit,force:e.force}),n={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())n.skipped+=r;e.onProgress?.({...n});let s=[];for(let r of t.eligible){if(e.signal?.aborted)break;n.current_session_id=r.id,e.onProgress?.({...n});let o=await No(r.id,{model:e.model,force:e.force,signal:e.signal});s.push(o),e.onResult?.(o),o.ok?n.ok+=1:o.skipped?n.skipped+=1:n.failed+=1,o.usage?.input_tokens&&(n.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(n.total_output_tokens+=o.usage.output_tokens),n.processed+=1,e.onProgress?.({...n})}return n.current_session_id=null,e.onProgress?.({...n}),{progress:n,results:s}}we();var re="[daemon:inference]",ft=!1;function xb(){ft=!0}var Mo=!1,Do=!1,jo=!1,Po=!1,Fo=!1,Ml=0,$o=!1,Uo=!1,Os=3,gt=0,Lt=!1,Cs=null,vt=null,Dl=!1;function Nb(){return h().prepare("SELECT id, name FROM projects").all()}async function jl(){if(Mo||ft)return;Mo=!0;let e=Date.now();try{let n=(await Fc({minClusterSize:2})).progress;(n.clusters_created||n.clusters_merged||n.members_added)&&console.log(`${re} bug-patterns: created=${n.clusters_created} merged=${n.clusters_merged} members_added=${n.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} bug-patterns failed:`,t)}finally{Mo=!1}}async function Pl(){if(Do||ft)return;Do=!0;let e=Date.now();try{let t=h(),n=ql(t,$l),s=t.prepare("SELECT COALESCE(MAX(rowid), 0) AS m FROM messages").get(),r=0,o=0,a=!1;for(let c of Nb())try{let u=await Qc({projectId:c.id,sinceRowid:n});r+=u.progress.suggestions_created,o+=1}catch(u){a=!0,console.error(`${re} citations failed for project "${c.name}":`,u)}a||Xl(t,$l,s.m),r>0&&console.log(`${re} citations: ${r} suggestion(s) across ${o} project(s) since_rowid=${n} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} citations failed:`,t)}finally{Do=!1}}var Fl="l1_inference_watermark_rowid",$l="citations_inference_watermark_rowid";function ql(e,t){let n=e.prepare("SELECT value FROM app_settings WHERE key = ?").get(t);if(!n)return 0;let s=Number(n.value);return Number.isFinite(s)&&s>=0?s:0}function Xl(e,t,n){e.prepare("INSERT INTO app_settings(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(t,String(n))}function Ul(){if(jo||ft)return;jo=!0;let e=Date.now();try{let t=h(),n=ql(t,Fl),s=t.prepare("SELECT COALESCE(MAX(rowid), 0) AS m FROM messages").get(),r=tl({sinceRowid:n}),o=nl({sinceRowid:n});Xl(t,Fl,s.m),r.created+o.created>0&&console.log(`${re} l1: uuid=${r.created} plan=${o.created} since_rowid=${n} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} l1 failed:`,t)}finally{jo=!1}}async function Bl(){if(Po||ft)return;let e=ae();if(!e.enabled||e.backfillPaused)return;Po=!0;let t=Date.now();try{let n=await Rs({limit:25});n.processed>0&&console.log(`${re} backfill: processed=${n.processed} ok=${n.ok} failed=${n.failed} (${Date.now()-t}ms)`)}catch(n){console.error(`${re} backfill failed:`,n)}finally{Po=!1}}async function Hl(){if(Fo||ft)return;let e=ae();if(e.autoExtractEnabled&&!Dl&&Lt&&(Ob(),console.log(`${re} auto-extract: circuit breaker reset (config toggled on).`)),Dl=e.autoExtractEnabled,!e.autoExtractEnabled||Lt)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-Ml<t)return;if(!me()){$o||(console.log(`${re} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),$o=!0);return}if($o=!1,!await cc().catch(()=>!1)){Uo||(console.log(`${re} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),Uo=!0);return}Uo=!1,Fo=!0,Ml=Date.now();let s=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=Ct({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,m=new Map,f=null;for(let S of o){let R=await No(S.id,{model:mt});if(R.ok)a+=1;else if(!R.skipped){c+=1;let A=R.failed??"unknown";m.set(A,(m.get(A)??0)+1),!f&&R.stderr_excerpt&&(f=R.stderr_excerpt)}R.usage?.input_tokens&&(u+=R.usage.input_tokens),R.usage?.output_tokens&&(d+=R.usage.output_tokens)}let b=m.size>0?" reasons="+Array.from(m.entries()).map(([S,R])=>`${S}:${R}`).join(","):"";if(console.log(`${re} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-s}ms)${b}`),f){let R=be(f).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${re} auto-extract: first failure excerpt: ${R}`)}o.length>0&&u===0&&d===0?(gt+=1,gt>=Os&&(Lt=!0,Cs=Date.now(),vt=`${Os} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${re} auto-extract: CIRCUIT BREAKER tripped \u2014 ${vt}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(gt=0)}catch(r){console.error(`${re} auto-extract failed:`,r),gt+=1,gt>=Os&&(Lt=!0,Cs=Date.now(),vt=`${Os} consecutive thrown errors`,console.log(`${re} auto-extract: CIRCUIT BREAKER tripped \u2014 ${vt}. Pausing nibbler.`))}finally{Fo=!1}}function Jl(){return{broken:Lt,brokenAt:Cs,reason:vt,consecutiveZeroTokenRuns:gt}}function Ob(){Lt=!1,Cs=null,vt=null,gt=0}var Bo=!1;async function Wl(){if(Bo||ft)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(Lo(),Nl)),n=e();if(!n.autoArchiveEnabled)return;let s=1380*60*1e3;if(n.lastRunAt&&Date.now()-Date.parse(n.lastRunAt)<s)return;Bo=!0;let r=Date.now();try{let a=new Date(Date.now()-n.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(Il(),vl)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${re} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${re} auto-archive failed: ${a}`)}finally{Bo=!1}}function Gl(){let m=[],f=[];m.push(setTimeout(()=>{jl()},9e4)),f.push(setInterval(()=>{jl()},18e5)),m.push(setTimeout(()=>{Pl()},18e4)),f.push(setInterval(()=>{Pl()},36e5)),m.push(setTimeout(()=>Ul(),12e4)),f.push(setInterval(()=>Ul(),18e5)),m.push(setTimeout(()=>{Bl()},24e4)),f.push(setInterval(()=>{Bl()},9e5)),m.push(setTimeout(()=>{Hl()},3e5)),f.push(setInterval(()=>{Hl()},9e5));let b=3600*1e3,T=300*1e3;return m.push(setTimeout(()=>{Wl()},T)),f.push(setInterval(()=>{Wl()},b)),console.log(`${re} scheduled: bug-patterns (30m), citations (60m), l1 (30m), backfill (15m, when enabled), auto-extract (60m, when enabled), auto-archive (24h, when enabled)`),{startupTimers:m,intervalTimers:f,stop:()=>{xb();for(let S of m)clearTimeout(S);for(let S of f)clearInterval(S)}}}U();import{serveStatic as Sf}from"@hono/node-server/serve-static";import{existsSync as RA,readFileSync as Wa}from"node:fs";import{stat as kA,readFile as AA,realpath as xA}from"node:fs/promises";import{timingSafeEqual as NA}from"node:crypto";import{homedir as OA}from"node:os";import{dirname as Ja,join as Jr}from"node:path";import{fileURLToPath as Ga}from"node:url";import{existsSync as Hi,readdirSync as kw,readFileSync as op,statSync as Aw,statfsSync as ip}from"node:fs";import{homedir as xw}from"node:os";import{join as ap}from"node:path";Ns();U();ts();Q();Q();U();import{watch as ly}from"chokidar";import{readdirSync as uy,statSync as Ut}from"node:fs";import{createReadStream as Cb}from"node:fs";function Ls(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function qo(e){let t=e?.usage;if(!t||typeof t!="object")return null;let n={inputTokens:Ls(t.input_tokens),outputTokens:Ls(t.output_tokens),cacheCreateTokens:Ls(t.cache_creation_input_tokens),cacheReadTokens:Ls(t.cache_read_input_tokens)};return n.inputTokens===0&&n.outputTokens===0&&n.cacheCreateTokens===0&&n.cacheReadTokens===0?null:n}var Lb=/\x1B\[[0-9;]*[a-zA-Z]/g;function Ho(e){return e.replace(Lb,"")}var Wo=12e3;function Yl(e,t){if(e.length<=Wo)return e;let n=e.slice(0,Wo),s=e.length-Wo;return`${n}
1043
1043
 
1044
- \u27E8\u2026 ${s.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function kb(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function Ab(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let n of e.content)if(n&&typeof n=="object"){let s=n;s.type==="text"&&typeof s.text=="string"?t.push(s.text):s.type==="image"&&t.push("[image]")}return t.join(`
1045
- `)}return""}function xb(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Uo(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],n=[];for(let s of e.content)if(!(!s||typeof s!="object")){if(s.type==="text"&&typeof s.text=="string"){t.push(Uo(s.text));continue}if(s.type==="tool_use"&&typeof s.name=="string"){n.push(s.name);let r=s.input!=null?kb(s.input):"",o=Wl(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${s.name}\`**
1044
+ \u27E8\u2026 ${s.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function vb(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function Ib(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let n of e.content)if(n&&typeof n=="object"){let s=n;s.type==="text"&&typeof s.text=="string"?t.push(s.text):s.type==="image"&&t.push("[image]")}return t.join(`
1045
+ `)}return""}function Mb(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Ho(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],n=[];for(let s of e.content)if(!(!s||typeof s!="object")){if(s.type==="text"&&typeof s.text=="string"){t.push(Ho(s.text));continue}if(s.type==="tool_use"&&typeof s.name=="string"){n.push(s.name);let r=s.input!=null?vb(s.input):"",o=Yl(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${s.name}\`**
1046
1046
 
1047
1047
  \`\`\`json
1048
1048
  ${o}
1049
- \`\`\``);continue}if(s.type==="tool_result"){let r=Uo(Ab(s));if(r){let o=Wl(r,"tool result");t.push(`**Tool result**
1049
+ \`\`\``);continue}if(s.type==="tool_result"){let r=Ho(Ib(s));if(r){let o=Yl(r,"tool result");t.push(`**Tool result**
1050
1050
 
1051
1051
  \`\`\`
1052
1052
  ${o}
1053
1053
  \`\`\``)}else t.push("_(tool result was empty or image-only)_");continue}if(s.type==="image"){t.push("_(image)_");continue}t.push(`_(unknown block: ${s.type})_`)}return{text:t.join(`
1054
1054
 
1055
- `),toolNames:n}}function Nb(e){if(e.endsWith("\r")&&(e=e.slice(0,-1)),!e.trim())return null;let t;try{t=JSON.parse(e)}catch{return null}if(!t.uuid||!t.sessionId)return null;let{text:n,toolNames:s}=xb(t.message);return{uuid:t.uuid,parentUuid:t.parentUuid??null,sessionId:t.sessionId,type:t.type??"unknown",role:t.message?.role??null,timestamp:t.timestamp??null,isSidechain:t.isSidechain===!0,cwd:t.cwd??null,gitBranch:t.gitBranch??null,version:t.version??null,contentText:n,toolNames:s,raw:e,usage:Bo(t.message),model:t.message?.model??null}}async function ql(e,t){let n=[];await new Promise((u,d)=>{let m=wb(e,{start:t});m.on("data",f=>{n.push(f)}),m.on("end",u),m.on("error",d)});let s=Buffer.concat(n),r=s.lastIndexOf(10);if(r===-1)return{entries:[],endOffset:t};let o=r+1,a=s.subarray(0,o).toString("utf8"),c=[];for(let u of a.split(`
1056
- `)){let d=Nb(u);d!==null&&c.push(d)}return{entries:c,endOffset:t+o}}function Xl(e,t,n){return e?t.inode!==e.inode?{mode:"full"}:t.size<e.byteOffset?{mode:"full"}:n?{mode:"incremental",start:e.byteOffset}:{mode:"full"}:{mode:"full"}}import{createHash as Jl}from"node:crypto";import{openSync as Ob,readSync as Lb,closeSync as Cb}from"node:fs";var Gl=4096;function vs(e,t=Gl,n=Gl){let s=Math.max(0,Math.min(n,t));if(s===0)return Jl("sha256").update(Buffer.alloc(0)).digest("hex");let r=Ob(e,"r");try{let o=Buffer.allocUnsafe(s),a=Lb(r,o,0,s,0);return Jl("sha256").update(o.subarray(0,a)).digest("hex")}finally{Cb(r)}}function Yl(e,t){let n=e.prepare("SELECT byte_offset, size_bytes, inode, prefix_hash FROM file_cursor WHERE file_path = ? LIMIT 1").get(t);return!n||n.inode==null||n.prefix_hash==null?null:{byteOffset:n.byte_offset,sizeBytes:n.size_bytes,inode:n.inode,prefixHash:n.prefix_hash}}function Wo(e,t,n){e.prepare(`
1055
+ `),toolNames:n}}function Db(e){if(e.endsWith("\r")&&(e=e.slice(0,-1)),!e.trim())return null;let t;try{t=JSON.parse(e)}catch{return null}if(!t.uuid||!t.sessionId)return null;let{text:n,toolNames:s}=Mb(t.message);return{uuid:t.uuid,parentUuid:t.parentUuid??null,sessionId:t.sessionId,type:t.type??"unknown",role:t.message?.role??null,timestamp:t.timestamp??null,isSidechain:t.isSidechain===!0,cwd:t.cwd??null,gitBranch:t.gitBranch??null,version:t.version??null,contentText:n,toolNames:s,raw:e,usage:qo(t.message),model:t.message?.model??null}}async function zl(e,t){let n=[];await new Promise((u,d)=>{let m=Cb(e,{start:t});m.on("data",f=>{n.push(f)}),m.on("end",u),m.on("error",d)});let s=Buffer.concat(n),r=s.lastIndexOf(10);if(r===-1)return{entries:[],endOffset:t};let o=r+1,a=s.subarray(0,o).toString("utf8"),c=[];for(let u of a.split(`
1056
+ `)){let d=Db(u);d!==null&&c.push(d)}return{entries:c,endOffset:t+o}}function Kl(e,t,n){return e?t.inode!==e.inode?{mode:"full"}:t.size<e.byteOffset?{mode:"full"}:n?{mode:"incremental",start:e.byteOffset}:{mode:"full"}:{mode:"full"}}import{createHash as Vl}from"node:crypto";import{openSync as jb,readSync as Pb,closeSync as Fb}from"node:fs";var Ql=4096;function vs(e,t=Ql,n=Ql){let s=Math.max(0,Math.min(n,t));if(s===0)return Vl("sha256").update(Buffer.alloc(0)).digest("hex");let r=jb(e,"r");try{let o=Buffer.allocUnsafe(s),a=Pb(r,o,0,s,0);return Vl("sha256").update(o.subarray(0,a)).digest("hex")}finally{Fb(r)}}function Zl(e,t){let n=e.prepare("SELECT byte_offset, size_bytes, inode, prefix_hash FROM file_cursor WHERE file_path = ? LIMIT 1").get(t);return!n||n.inode==null||n.prefix_hash==null?null:{byteOffset:n.byte_offset,sizeBytes:n.size_bytes,inode:n.inode,prefixHash:n.prefix_hash}}function Xo(e,t,n){e.prepare(`
1057
1057
  INSERT INTO file_cursor (file_path, byte_offset, size_bytes, line_count, inode, prefix_hash, mtime, updated_at)
1058
1058
  VALUES (@file_path, @byte_offset, @size_bytes, @line_count, @inode, @prefix_hash, @mtime, datetime('now'))
1059
1059
  ON CONFLICT(file_path) DO UPDATE SET
@@ -1064,20 +1064,20 @@ ${o}
1064
1064
  prefix_hash = excluded.prefix_hash,
1065
1065
  mtime = excluded.mtime,
1066
1066
  updated_at = datetime('now')
1067
- `).run({file_path:t,byte_offset:n.byteOffset,size_bytes:n.sizeBytes,line_count:n.lineCount,inode:n.inode,prefix_hash:n.prefixHash,mtime:n.mtime})}import{basename as rr,join as sy}from"node:path";import{execFile as eS}from"node:child_process";import{promisify as tS}from"node:util";import{existsSync as nS}from"node:fs";import{basename as sS}from"node:path";Q();import{existsSync as vb,readFileSync as Ib,writeFileSync as Mb}from"node:fs";import{join as Db}from"node:path";import{z as fe}from"zod";var qo=Db(B,"terminals.json"),zl=1440*60*1e3,jb=3e4,Pb=6e4,Fb=fe.object({shell_pid:fe.number(),tab_name:fe.string(),cwd:fe.string().nullable().optional(),opened_at:fe.string(),last_seen_at:fe.string()}),$b=fe.object({schema:fe.string().optional(),saved_at:fe.string().optional(),terminals:fe.array(Fb).max(500).default([]),sessions_by_pid:fe.record(fe.string(),fe.array(fe.string()).max(50)).optional().default({})}),Kl=/^[⠀-⣿✳\s]+/,Vl=/^\d+(\.\d+){1,3}$/;function ue(e){let t=e.trim();return!!(!t||Kl.test(t)||Vl.test(t))}function It(e){let t=e.trim();if(!t||Vl.test(t))return null;let n=t.replace(Kl,"").trim();return n.length>0?n:null}function Xo(e,t){if(!ue(e))return e;let n=It(e);return n||(t&&!ue(t)?t:e)}function Ub(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var Jo=class{entries=new Map;origins=new Map;sessionsByPid=new Map;pendingClaudeStarts=[];deferredLinks=new Map;pidOwnership=new Map;loaded=!1;ensureLoaded(){if(!this.loaded&&(this.loaded=!0,!!vb(qo)))try{let t=Ib(qo,"utf8"),n=JSON.parse(t),s=$b.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,a]of Object.entries(r.sessions_by_pid??{})){let c=Number(o);if(!Number.isFinite(c))continue;let u=new Set;for(let d of a)d.length>0&&u.add(d);u.size>0&&this.sessionsByPid.set(c,u)}this.gc()}catch{}}save(){try{z();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};Mb(qo,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=Xo(t.tab_name,s?.tab_name),o=s?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:n};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=Xo(n,s.tab_name),o={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>Pb?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=Xo(o.tab_name,a?.tab_name),u=a?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:c,opened_at:u,last_seen_at:n}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=Ub({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(n.kind==="pid-match"||n.kind==="singleton-cwd"){let s=this.pendingClaudeStarts.indexOf(n.entry);s>=0&&this.pendingClaudeStarts.splice(s,1)}return n}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,n,s,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:n,queued_at:Date.now(),cwd:s,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,n])=>({session_id:t,...n}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[n,s]of this.deferredLinks)s.queued_at<t&&this.deferredLinks.delete(n)}gcPending(){let t=Date.now()-jb;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(n=>{let s=Date.parse(n.started_at);return Number.isFinite(s)&&s>=t}))}outputTails=new Map;setOutputTail(t,n,s){this.ensureLoaded(),this.outputTails.set(t,{text:n,captured_at:s})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,n){this.ensureLoaded(),this.origins.set(t,n),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-zl;for(let[n,s]of this.entries){let r=Date.parse(s.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(n)}}gcOrigins(){let t=Date.now()-zl;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,o=0;for(let[a,c]of this.sessionsByPid){let u=this.entries.get(a);if(u){let d=Date.parse(u.last_seen_at);if(Number.isFinite(d)&&d>=s)continue}o+=c.size,this.sessionsByPid.delete(a),u&&(this.entries.delete(a),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let o=this.sessionsByPid.get(s);o&&(n+=o.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},M=new Jo;U();Q();import{writeFileSync as Hb}from"node:fs";import{join as Bb}from"node:path";var Wb=Bb(B,"aliases.json");function Go(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Se(e){return h().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function qb(){return h().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:Go(t.previous_aliases)}))}function _e(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=h(),r=new Date().toISOString(),o=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=Go(o.previous_aliases),o.alias!==n&&a.push({alias:o.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
1067
+ `).run({file_path:t,byte_offset:n.byteOffset,size_bytes:n.sizeBytes,line_count:n.lineCount,inode:n.inode,prefix_hash:n.prefixHash,mtime:n.mtime})}function Is(e){if(!(e instanceof Error))return!1;let t=e.errcode;return typeof t=="number"&&t===5||typeof t=="number"&&t===6?!0:/database (?:table )?is locked|SQLITE_BUSY|SQLITE_LOCKED/i.test(e.message)}function $b(e){if(e<=0)return;let t=Date.now()+e;try{let n=new SharedArrayBuffer(4),s=new Int32Array(n);Atomics.wait(s,0,0,e)}catch{for(;Date.now()<t;);}}function Jo(e,t={}){let n=Math.max(1,t.attempts??4),s=Math.max(0,t.baseDelayMs??8),r;for(let o=1;o<=n;o+=1)try{return e()}catch(a){if(!Is(a))throw a;r=a,o<n&&(t.onRetry?.(o,a),$b(Math.min(s*o,50)))}throw r}import{basename as or,join as dy}from"node:path";import{execFile as cS}from"node:child_process";import{promisify as lS}from"node:util";import{existsSync as uS}from"node:fs";import{basename as dS}from"node:path";Q();import{existsSync as Ub,readFileSync as Bb,writeFileSync as Hb}from"node:fs";import{join as Wb}from"node:path";import{z as fe}from"zod";var Go=Wb(H,"terminals.json"),eu=1440*60*1e3,qb=3e4,Xb=6e4,Jb=fe.object({shell_pid:fe.number(),tab_name:fe.string(),cwd:fe.string().nullable().optional(),opened_at:fe.string(),last_seen_at:fe.string()}),Gb=fe.object({schema:fe.string().optional(),saved_at:fe.string().optional(),terminals:fe.array(Jb).max(500).default([]),sessions_by_pid:fe.record(fe.string(),fe.array(fe.string()).max(50)).optional().default({})}),tu=/^[⠀-⣿✳\s]+/,nu=/^\d+(\.\d+){1,3}$/;function ue(e){let t=e.trim();return!!(!t||tu.test(t)||nu.test(t))}function It(e){let t=e.trim();if(!t||nu.test(t))return null;let n=t.replace(tu,"").trim();return n.length>0?n:null}function Yo(e,t){if(!ue(e))return e;let n=It(e);return n||(t&&!ue(t)?t:e)}function Yb(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var zo=class{entries=new Map;origins=new Map;sessionsByPid=new Map;pendingClaudeStarts=[];deferredLinks=new Map;pidOwnership=new Map;loaded=!1;ensureLoaded(){if(!this.loaded&&(this.loaded=!0,!!Ub(Go)))try{let t=Bb(Go,"utf8"),n=JSON.parse(t),s=Gb.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,a]of Object.entries(r.sessions_by_pid??{})){let c=Number(o);if(!Number.isFinite(c))continue;let u=new Set;for(let d of a)d.length>0&&u.add(d);u.size>0&&this.sessionsByPid.set(c,u)}this.gc()}catch{}}save(){try{z();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};Hb(Go,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=Yo(t.tab_name,s?.tab_name),o=s?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:n};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=Yo(n,s.tab_name),o={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>Xb?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=Yo(o.tab_name,a?.tab_name),u=a?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:c,opened_at:u,last_seen_at:n}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=Yb({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(n.kind==="pid-match"||n.kind==="singleton-cwd"){let s=this.pendingClaudeStarts.indexOf(n.entry);s>=0&&this.pendingClaudeStarts.splice(s,1)}return n}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,n,s,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:n,queued_at:Date.now(),cwd:s,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,n])=>({session_id:t,...n}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[n,s]of this.deferredLinks)s.queued_at<t&&this.deferredLinks.delete(n)}gcPending(){let t=Date.now()-qb;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(n=>{let s=Date.parse(n.started_at);return Number.isFinite(s)&&s>=t}))}outputTails=new Map;setOutputTail(t,n,s){this.ensureLoaded(),this.outputTails.set(t,{text:n,captured_at:s})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,n){this.ensureLoaded(),this.origins.set(t,n),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-eu;for(let[n,s]of this.entries){let r=Date.parse(s.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(n)}}gcOrigins(){let t=Date.now()-eu;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,o=0;for(let[a,c]of this.sessionsByPid){let u=this.entries.get(a);if(u){let d=Date.parse(u.last_seen_at);if(Number.isFinite(d)&&d>=s)continue}o+=c.size,this.sessionsByPid.delete(a),u&&(this.entries.delete(a),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let o=this.sessionsByPid.get(s);o&&(n+=o.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},M=new zo;U();Q();import{writeFileSync as zb}from"node:fs";import{join as Kb}from"node:path";var Vb=Kb(H,"aliases.json");function Ko(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Se(e){return h().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function Qb(){return h().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:Ko(t.previous_aliases)}))}function _e(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=h(),r=new Date().toISOString(),o=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=Ko(o.previous_aliases),o.alias!==n&&a.push({alias:o.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
1068
1068
  VALUES (?, ?, ?, ?)
1069
1069
  ON CONFLICT(session_id) DO UPDATE SET
1070
1070
  alias = excluded.alias,
1071
1071
  updated_at = excluded.updated_at,
1072
- previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(a)),Ql(),{session_id:e,alias:n,updated_at:r,previous_aliases:a}}function Is(e){let t=h(),n=new Date().toISOString(),s=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!s)return;let r=Go(s.previous_aliases);r.push({alias:s.alias,replaced_at:n}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
1073
- WHERE session_id = ?`).run(n,JSON.stringify(r),e),Ql()}function Ql(){try{z();let e=qb(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};Hb(Wb,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}U();import{execFile as Xb}from"node:child_process";import{readFile as Jb}from"node:fs/promises";import{promisify as Gb}from"node:util";var Yb=Gb(Xb),Zl=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function zb(e){let t={};for(let n of Zl){let s=e[n];typeof s=="string"&&s.length>0&&(t[n]=s)}return t}function Kb(e){let t=Date.now();if(e.CURSOR_TRACE_ID)return{editor:"cursor",label:"Cursor",detectedAt:t};if(e.VSCODE_PID||e.VSCODE_INJECTION)return{editor:"vscode",label:"VS Code",detectedAt:t};let n=e.TERM_PROGRAM;return n==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:n==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:n==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:n==="WezTerm"?{editor:"wezterm",label:"WezTerm",detectedAt:t}:e.WT_SESSION?{editor:"windows-terminal",label:"Windows Terminal",detectedAt:t}:e.KITTY_WINDOW_ID?{editor:"kitty",label:"Kitty",detectedAt:t}:e.TERM==="alacritty"||e.ALACRITTY_SOCKET?{editor:"alacritty",label:"Alacritty",detectedAt:t}:null}async function Vb(e){if(process.platform==="linux")try{let t=await Jb(`/proc/${e}/environ`,"utf8");return Qb(t)}catch{return{}}try{let{stdout:t}=await Yb("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return Zb(t)}catch{return{}}}function Qb(e){let t={};for(let n of e.split("\0")){if(!n)continue;let s=n.indexOf("=");if(s<=0)continue;let r=n.slice(0,s),o=n.slice(s+1);t[r]=o}return t}function Zb(e){let t={},n=new Set(Zl),s=e.replace(/\s+/g," ").trim().split(" ");for(let r of s){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);n.has(a)&&(t[a]=r.slice(o+1))}return t}async function eu(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await Vb(e),n=zb(t);return Kb(n)}catch{return null}}var Ds=tS(eS),Ms;function rS(){if(Ms!==void 0)return Ms;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(nS(t))return Ms=t,t;return Ms=null,null}var oS=3,iS=3600*1e3,yn=new Map;function ru(){let e=M.all(),t=e.map(n=>n.shell_pid).sort((n,s)=>n-s).join(",");return`${e.length}:${t}`}function aS(e){let t=yn.get(e);return t?Date.now()-t.lastAt>iS?(yn.delete(e),!1):t.refusals<oS?!1:t.fingerprint===ru():!1}function tu(e){let t=ru(),n=yn.get(e);n&&n.fingerprint===t?(n.refusals+=1,n.lastAt=Date.now()):yn.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function cS(e){yn.delete(e)}async function lS(e){let t=rS();if(!t)return null;try{let{stdout:n}=await Ds(t,["-Fpc",e],{timeout:2e3}),s=n.split(`
1074
- `),r=null,o=null,a=null;for(let c of s)c.startsWith("p")?(a=Number(c.slice(1)),Number.isFinite(a)&&a>0?r==null&&(r=a):a=null):c.startsWith("c")&&a!=null&&c.slice(1).trim()==="claude"&&o==null&&(o=a);return o??r}catch{return null}}async function nu(e){try{let{stdout:t}=await Ds("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),n=Number(t.trim());return Number.isFinite(n)&&n>0?n:null}catch{return null}}async function uS(e){try{let{stdout:t}=await Ds("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),n=Date.parse(t.trim());return Number.isFinite(n)?n:null}catch{return null}}var su=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu","node","deno","bun","python","python3","ruby","ts-node","tsx","go","cargo","java","php","irb","pry","iex","terminal","shell","console"]);function pe(e){let t=e.trim().toLowerCase();if(!t)return!0;let n=t.replace(/^[-/]+/,"").replace(/\s*\(\d+\)\s*$/,"").trim();if(su.has(n))return!0;if(/^[-/][\w./-]*$/.test(t)){let s=n.replace(/^.*\//,"");if(su.has(s))return!0}return!1}function Mt(e){let t=e.tabName?.trim();return t&&!pe(t)&&!ue(t)?t:null}function dS(e){try{return h().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function js(e){let t=sS(e,".jsonl");if(Se(t)||aS(t))return;let n=dS(t),s=await lS(e),r=s?await nu(s):null,o=s?await eu(s):null;o&&M.setOrigin(t,o);let a=null,c=null,u=null,d=M.takePendingMatched({shellPid:r,cwd:n?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${n?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),tu(t);return}if(d.kind==="pid-match"||d.kind==="singleton-cwd"){let S=d.entry;c=S.shell_pid,u=d.kind==="pid-match"?"pending-pid":"pending-cwd",a=M.get(S.shell_pid)??{shell_pid:S.shell_pid,tab_name:S.tab_name,cwd:S.cwd,opened_at:S.started_at,last_seen_at:S.started_at}}let m=null;if(!a&&s){let S=s;for(let w=0;w<4&&S!=null;w++){let A=await nu(S);if(!A)break;let v=M.get(A);if(v){a=v,c=A,u="ppid";break}m==null&&(m=A),S=A}}if(!a&&n?.cwd){let S=n.cwd.replace(/\/+$/,""),w=M.all().filter(A=>A.cwd&&A.cwd.replace(/\/+$/,"")===S);w.length===1?(a=w[0],c=a.shell_pid,u="cwd"):w.length>=2&&(console.log(`[correlator] ${w.length} registered terminals in ${S} for ${t.slice(0,8)} \u2014 refusing to guess; heuristic title will display`),tu(t))}let f=null;if(a?.tab_name&&!pe(a.tab_name)&&!ue(a.tab_name))f=a.tab_name;else if(a?.tab_name&&ue(a.tab_name)){let S=It(a.tab_name);S&&!pe(S)&&(f=S)}if(!f&&!(u==="pending-pid"||u==="pending-cwd")&&a&&n?.cwd){let S=n.cwd.replace(/\/+$/,""),w=M.all().filter(A=>A.shell_pid!==c&&A.cwd&&A.cwd.replace(/\/+$/,"")===S&&!pe(A.tab_name)&&!ue(A.tab_name)).sort((A,v)=>Date.parse(v.last_seen_at)-Date.parse(A.last_seen_at))[0];w&&(f=w.tab_name)}let T=Mt({tabName:f,origin:o,cwd:n?.cwd??null,gitBranch:n?.git_branch??null});if(m!=null&&!c&&M.deferSessionLink(t,m,n?.cwd??null,n?.git_branch??null),!!T)try{_e(t,T),cS(t);let S=!!f&&!pe(f)&&!ue(f)&&T===f.trim();c!=null&&M.linkSession(t,c);let w=u==="pending-pid"?"pending PID-exact match":u==="pending-cwd"?"pending cwd-singleton match":u??"unknown";console.log(`[correlator] auto-aliased ${t.slice(0,8)} \u2192 "${T}"`+(S?` (tab name via ${w}, shell pid ${c})`:a?` (generic shell name "${a.tab_name}" \u2192 ${o?.editor??"origin"} fallback, shell pid ${c})`:o?` (${o.editor} origin \u2014 no terminal match)`:""))}catch{}}async function pS(){try{let{stdout:e}=await Ds("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let n of e.split(`
1075
- `)){let s=n.trim();if(!s)continue;let r=s.match(/^(\d+)\s+(\d+)\s+(.+)$/);if(!r)continue;let o=Number(r[1]),a=Number(r[2]),c=r[3].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&(!Number.isFinite(o)||!Number.isFinite(a)||t.set(o,a))}return t}catch{return new Map}}var mS=9e4;function gS(e){let t=(Date.now()-mS)/1e3,n=e.replace(/\/+$/,"");return h().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1072
+ previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(a)),su(),{session_id:e,alias:n,updated_at:r,previous_aliases:a}}function Ms(e){let t=h(),n=new Date().toISOString(),s=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!s)return;let r=Ko(s.previous_aliases);r.push({alias:s.alias,replaced_at:n}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
1073
+ WHERE session_id = ?`).run(n,JSON.stringify(r),e),su()}function su(){try{z();let e=Qb(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};zb(Vb,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}U();import{execFile as Zb}from"node:child_process";import{readFile as eS}from"node:fs/promises";import{promisify as tS}from"node:util";var nS=tS(Zb),ru=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function sS(e){let t={};for(let n of ru){let s=e[n];typeof s=="string"&&s.length>0&&(t[n]=s)}return t}function rS(e){let t=Date.now();if(e.CURSOR_TRACE_ID)return{editor:"cursor",label:"Cursor",detectedAt:t};if(e.VSCODE_PID||e.VSCODE_INJECTION)return{editor:"vscode",label:"VS Code",detectedAt:t};let n=e.TERM_PROGRAM;return n==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:n==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:n==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:n==="WezTerm"?{editor:"wezterm",label:"WezTerm",detectedAt:t}:e.WT_SESSION?{editor:"windows-terminal",label:"Windows Terminal",detectedAt:t}:e.KITTY_WINDOW_ID?{editor:"kitty",label:"Kitty",detectedAt:t}:e.TERM==="alacritty"||e.ALACRITTY_SOCKET?{editor:"alacritty",label:"Alacritty",detectedAt:t}:null}async function oS(e){if(process.platform==="linux")try{let t=await eS(`/proc/${e}/environ`,"utf8");return iS(t)}catch{return{}}try{let{stdout:t}=await nS("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return aS(t)}catch{return{}}}function iS(e){let t={};for(let n of e.split("\0")){if(!n)continue;let s=n.indexOf("=");if(s<=0)continue;let r=n.slice(0,s),o=n.slice(s+1);t[r]=o}return t}function aS(e){let t={},n=new Set(ru),s=e.replace(/\s+/g," ").trim().split(" ");for(let r of s){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);n.has(a)&&(t[a]=r.slice(o+1))}return t}async function ou(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await oS(e),n=sS(t);return rS(n)}catch{return null}}var js=lS(cS),Ds;function pS(){if(Ds!==void 0)return Ds;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(uS(t))return Ds=t,t;return Ds=null,null}var mS=3,gS=3600*1e3,yn=new Map;function lu(){let e=M.all(),t=e.map(n=>n.shell_pid).sort((n,s)=>n-s).join(",");return`${e.length}:${t}`}function fS(e){let t=yn.get(e);return t?Date.now()-t.lastAt>gS?(yn.delete(e),!1):t.refusals<mS?!1:t.fingerprint===lu():!1}function iu(e){let t=lu(),n=yn.get(e);n&&n.fingerprint===t?(n.refusals+=1,n.lastAt=Date.now()):yn.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function _S(e){yn.delete(e)}async function hS(e){let t=pS();if(!t)return null;try{let{stdout:n}=await js(t,["-Fpc",e],{timeout:2e3}),s=n.split(`
1074
+ `),r=null,o=null,a=null;for(let c of s)c.startsWith("p")?(a=Number(c.slice(1)),Number.isFinite(a)&&a>0?r==null&&(r=a):a=null):c.startsWith("c")&&a!=null&&c.slice(1).trim()==="claude"&&o==null&&(o=a);return o??r}catch{return null}}async function au(e){try{let{stdout:t}=await js("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),n=Number(t.trim());return Number.isFinite(n)&&n>0?n:null}catch{return null}}async function ES(e){try{let{stdout:t}=await js("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),n=Date.parse(t.trim());return Number.isFinite(n)?n:null}catch{return null}}var cu=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu","node","deno","bun","python","python3","ruby","ts-node","tsx","go","cargo","java","php","irb","pry","iex","terminal","shell","console"]);function pe(e){let t=e.trim().toLowerCase();if(!t)return!0;let n=t.replace(/^[-/]+/,"").replace(/\s*\(\d+\)\s*$/,"").trim();if(cu.has(n))return!0;if(/^[-/][\w./-]*$/.test(t)){let s=n.replace(/^.*\//,"");if(cu.has(s))return!0}return!1}function Mt(e){let t=e.tabName?.trim();return t&&!pe(t)&&!ue(t)?t:null}function bS(e){try{return h().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function Ps(e){let t=dS(e,".jsonl");if(Se(t)||fS(t))return;let n=bS(t),s=await hS(e),r=s?await au(s):null,o=s?await ou(s):null;o&&M.setOrigin(t,o);let a=null,c=null,u=null,d=M.takePendingMatched({shellPid:r,cwd:n?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${n?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),iu(t);return}if(d.kind==="pid-match"||d.kind==="singleton-cwd"){let S=d.entry;c=S.shell_pid,u=d.kind==="pid-match"?"pending-pid":"pending-cwd",a=M.get(S.shell_pid)??{shell_pid:S.shell_pid,tab_name:S.tab_name,cwd:S.cwd,opened_at:S.started_at,last_seen_at:S.started_at}}let m=null;if(!a&&s){let S=s;for(let R=0;R<4&&S!=null;R++){let A=await au(S);if(!A)break;let v=M.get(A);if(v){a=v,c=A,u="ppid";break}m==null&&(m=A),S=A}}if(!a&&n?.cwd){let S=n.cwd.replace(/\/+$/,""),R=M.all().filter(A=>A.cwd&&A.cwd.replace(/\/+$/,"")===S);R.length===1?(a=R[0],c=a.shell_pid,u="cwd"):R.length>=2&&(console.log(`[correlator] ${R.length} registered terminals in ${S} for ${t.slice(0,8)} \u2014 refusing to guess; heuristic title will display`),iu(t))}let f=null;if(a?.tab_name&&!pe(a.tab_name)&&!ue(a.tab_name))f=a.tab_name;else if(a?.tab_name&&ue(a.tab_name)){let S=It(a.tab_name);S&&!pe(S)&&(f=S)}if(!f&&!(u==="pending-pid"||u==="pending-cwd")&&a&&n?.cwd){let S=n.cwd.replace(/\/+$/,""),R=M.all().filter(A=>A.shell_pid!==c&&A.cwd&&A.cwd.replace(/\/+$/,"")===S&&!pe(A.tab_name)&&!ue(A.tab_name)).sort((A,v)=>Date.parse(v.last_seen_at)-Date.parse(A.last_seen_at))[0];R&&(f=R.tab_name)}let T=Mt({tabName:f,origin:o,cwd:n?.cwd??null,gitBranch:n?.git_branch??null});if(m!=null&&!c&&M.deferSessionLink(t,m,n?.cwd??null,n?.git_branch??null),!!T)try{_e(t,T),_S(t);let S=!!f&&!pe(f)&&!ue(f)&&T===f.trim();c!=null&&M.linkSession(t,c);let R=u==="pending-pid"?"pending PID-exact match":u==="pending-cwd"?"pending cwd-singleton match":u??"unknown";console.log(`[correlator] auto-aliased ${t.slice(0,8)} \u2192 "${T}"`+(S?` (tab name via ${R}, shell pid ${c})`:a?` (generic shell name "${a.tab_name}" \u2192 ${o?.editor??"origin"} fallback, shell pid ${c})`:o?` (${o.editor} origin \u2014 no terminal match)`:""))}catch{}}async function SS(){try{let{stdout:e}=await js("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let n of e.split(`
1075
+ `)){let s=n.trim();if(!s)continue;let r=s.match(/^(\d+)\s+(\d+)\s+(.+)$/);if(!r)continue;let o=Number(r[1]),a=Number(r[2]),c=r[3].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&(!Number.isFinite(o)||!Number.isFinite(a)||t.set(o,a))}return t}catch{return new Map}}var TS=9e4;function yS(e){let t=(Date.now()-TS)/1e3,n=e.replace(/\/+$/,"");return h().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1076
1076
  FROM sessions s
1077
1077
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1078
- WHERE s.cwd = ? AND s.file_mtime > ?`).all(n,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function fS(e){let t=await import("node:fs/promises"),n="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);n=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let s=[];for(let r of n.split(`
1078
+ WHERE s.cwd = ? AND s.file_mtime > ?`).all(n,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function wS(e){let t=await import("node:fs/promises"),n="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);n=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let s=[];for(let r of n.split(`
1079
1079
  `)){if(!r.trim())continue;let o;try{o=JSON.parse(r)}catch{continue}let a=o;if(!a||a.type!=="user"&&a.type!=="assistant")continue;let c=a.message?.content,u="";if(typeof c=="string")u=c;else if(Array.isArray(c))for(let d of c)d&&typeof d=="object"&&"text"in d&&typeof d.text=="string"&&(u+=d.text+`
1080
- `);if(u){for(let d of u.split(/\r?\n/)){let m=d.trim();m.length>=60&&m.length<=400&&s.push(m)}if(s.length>=8)break}}return s}async function ou(e){if(Se(e))return null;let t=h().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let n=await fS(t.file_path);if(n.length===0)return null;let s=t.cwd.replace(/\/+$/,""),r=M.allOutputTails(),o=[];for(let[a,c]of r){let u=M.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==s||pe(u.tab_name)||ue(u.tab_name))continue;let d=0;for(let m of n)c.text.includes(m)&&d++;d>0&&o.push({shell_pid:a,tab_name:u.tab_name,matched_fingerprints:d})}return o.length===0||(o.sort((a,c)=>c.matched_fingerprints-a.matched_fingerprints),o.length>1&&o[0].matched_fingerprints===o[1].matched_fingerprints)?null:o[0]}var _S=3e4;function iu(){let e=Date.now(),t=M.all(),n=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>_S},s=new Map;for(let o of t){if(n(o)||!o.cwd||pe(o.tab_name)||ue(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=s.get(a);c||(c=[],s.set(a,c)),c.push({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd})}let r={rebound:0,ghosts:0,ambiguous:0};for(let o of t){if(!n(o))continue;let a=M.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=Se(c);if(!u)continue;let d=h().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let m=`${d.cwd.replace(/\/+$/,"")}::${u}`,f=s.get(m)??[];if(f.length===0)continue;if(f.length>1){r.ambiguous++;continue}let b=f[0];b.shell_pid!==o.shell_pid&&(M.unlinkSession(c),M.linkSession(c,b.shell_pid),r.rebound++)}}}return r}function au(){let e={resolved:0,expired:0},t=M.allDeferredLinks();for(let n of t){let s=M.get(n.parent_shell_pid);if(!s||pe(s.tab_name)||ue(s.tab_name))continue;let r=Se(n.session_id);if(r&&!M.isSessionAutoLinked(n.session_id)){M.resolveDeferredLink(n.session_id);continue}let o=M.getOrigin(n.session_id),a=Mt({tabName:s.tab_name,origin:o??null,cwd:n.cwd,gitBranch:n.git_branch});if(!a){M.resolveDeferredLink(n.session_id);continue}r!==a&&_e(n.session_id,a),M.linkSession(n.session_id,n.parent_shell_pid),M.resolveDeferredLink(n.session_id),e.resolved++}return e}var hS=6e4;async function Ps(){let e=await pS(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let n=[];for(let[a,c]of e){let u=M.get(c);if(!u||!u.cwd||pe(u.tab_name)||ue(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let m=await uS(a);n.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:m})}let s=new Map,r=a=>{let c=s.get(a);if(c)return c;let u=gS(a);return s.set(a,u),u},o=new Set;for(let a of n){let c=r(a.cwd).filter(d=>!o.has(d.id));if(c.length===0)continue;let u=null;if(a.startTimeMs!=null){let d=hS;for(let m of c){if(m.started_at_ms==null)continue;let f=Math.abs(m.started_at_ms-a.startTimeMs);f<d&&(d=f,u=m)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!M.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){M.linkSession(u.id,a.shellPid);continue}try{_e(u.id,a.target),M.linkSession(u.id,a.shellPid),u.alias?t.renamed++:t.linked++,console.log(`[correlator] linked ${u.id.slice(0,8)} \u2192 "${a.target}" (live sweep, claude pid ${a.claudePid}, shell pid ${a.shellPid})`)}catch{}}return t}function Yo(e,t,n,s={}){s.insertOnly||e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let r=e.prepare(`
1080
+ `);if(u){for(let d of u.split(/\r?\n/)){let m=d.trim();m.length>=60&&m.length<=400&&s.push(m)}if(s.length>=8)break}}return s}async function uu(e){if(Se(e))return null;let t=h().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let n=await wS(t.file_path);if(n.length===0)return null;let s=t.cwd.replace(/\/+$/,""),r=M.allOutputTails(),o=[];for(let[a,c]of r){let u=M.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==s||pe(u.tab_name)||ue(u.tab_name))continue;let d=0;for(let m of n)c.text.includes(m)&&d++;d>0&&o.push({shell_pid:a,tab_name:u.tab_name,matched_fingerprints:d})}return o.length===0||(o.sort((a,c)=>c.matched_fingerprints-a.matched_fingerprints),o.length>1&&o[0].matched_fingerprints===o[1].matched_fingerprints)?null:o[0]}var RS=3e4;function du(){let e=Date.now(),t=M.all(),n=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>RS},s=new Map;for(let o of t){if(n(o)||!o.cwd||pe(o.tab_name)||ue(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=s.get(a);c||(c=[],s.set(a,c)),c.push({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd})}let r={rebound:0,ghosts:0,ambiguous:0};for(let o of t){if(!n(o))continue;let a=M.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=Se(c);if(!u)continue;let d=h().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let m=`${d.cwd.replace(/\/+$/,"")}::${u}`,f=s.get(m)??[];if(f.length===0)continue;if(f.length>1){r.ambiguous++;continue}let b=f[0];b.shell_pid!==o.shell_pid&&(M.unlinkSession(c),M.linkSession(c,b.shell_pid),r.rebound++)}}}return r}function pu(){let e={resolved:0,expired:0},t=M.allDeferredLinks();for(let n of t){let s=M.get(n.parent_shell_pid);if(!s||pe(s.tab_name)||ue(s.tab_name))continue;let r=Se(n.session_id);if(r&&!M.isSessionAutoLinked(n.session_id)){M.resolveDeferredLink(n.session_id);continue}let o=M.getOrigin(n.session_id),a=Mt({tabName:s.tab_name,origin:o??null,cwd:n.cwd,gitBranch:n.git_branch});if(!a){M.resolveDeferredLink(n.session_id);continue}r!==a&&_e(n.session_id,a),M.linkSession(n.session_id,n.parent_shell_pid),M.resolveDeferredLink(n.session_id),e.resolved++}return e}var kS=6e4;async function Fs(){let e=await SS(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let n=[];for(let[a,c]of e){let u=M.get(c);if(!u||!u.cwd||pe(u.tab_name)||ue(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let m=await ES(a);n.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:m})}let s=new Map,r=a=>{let c=s.get(a);if(c)return c;let u=yS(a);return s.set(a,u),u},o=new Set;for(let a of n){let c=r(a.cwd).filter(d=>!o.has(d.id));if(c.length===0)continue;let u=null;if(a.startTimeMs!=null){let d=kS;for(let m of c){if(m.started_at_ms==null)continue;let f=Math.abs(m.started_at_ms-a.startTimeMs);f<d&&(d=f,u=m)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!M.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){M.linkSession(u.id,a.shellPid);continue}try{_e(u.id,a.target),M.linkSession(u.id,a.shellPid),u.alias?t.renamed++:t.linked++,console.log(`[correlator] linked ${u.id.slice(0,8)} \u2192 "${a.target}" (live sweep, claude pid ${a.claudePid}, shell pid ${a.shellPid})`)}catch{}}return t}function Vo(e,t,n,s={}){s.insertOnly||e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let r=e.prepare(`
1081
1081
  INSERT INTO message_usage (
1082
1082
  message_uuid, session_id, model,
1083
1083
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1109,11 +1109,11 @@ ${o}
1109
1109
  total_cache_create_tokens = @cc,
1110
1110
  total_cache_read_tokens = @cr,
1111
1111
  primary_model = @model
1112
- WHERE id = @id`).run({id:t,input:n.input_tokens,output:n.output_tokens,cc:n.cache_create_tokens,cr:n.cache_read_tokens,model:s?.model??null})}U();import{execFile as ES}from"node:child_process";import{promisify as bS}from"node:util";import{stat as SS}from"node:fs/promises";var cu=bS(ES),lu=1e4,TS="%H%x09%aI%x09%s";async function yS(e){try{let{stdout:t}=await cu("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:lu});return t.trim()==="true"}catch{return!1}}async function wS(e,t,n){let s=["--no-pager","log","--all","--no-color","--since",t,"--until",n,`--pretty=format:${TS}`],{stdout:r}=await cu("git",s,{cwd:e,timeout:lu,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1113
- `)){if(!c)continue;let[u,d,...m]=c.split(" ");!u||a.has(u)||(a.add(u),o.push({commit_sha:u,committed_at:d??null,subject:m.join(" ")||null}))}return o}function RS(e){return h().prepare(`SELECT id, cwd, started_at, ended_at
1114
- FROM sessions WHERE id = ?`).get(e)??null}function kS(e,t,n){if(n.length===0)return 0;let s=h(),r=new Date().toISOString(),o=s.prepare(`INSERT OR IGNORE INTO session_commits
1112
+ WHERE id = @id`).run({id:t,input:n.input_tokens,output:n.output_tokens,cc:n.cache_create_tokens,cr:n.cache_read_tokens,model:s?.model??null})}U();import{execFile as AS}from"node:child_process";import{promisify as xS}from"node:util";import{stat as NS}from"node:fs/promises";var mu=xS(AS),gu=1e4,OS="%H%x09%aI%x09%s";async function CS(e){try{let{stdout:t}=await mu("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:gu});return t.trim()==="true"}catch{return!1}}async function LS(e,t,n){let s=["--no-pager","log","--all","--no-color","--since",t,"--until",n,`--pretty=format:${OS}`],{stdout:r}=await mu("git",s,{cwd:e,timeout:gu,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1113
+ `)){if(!c)continue;let[u,d,...m]=c.split(" ");!u||a.has(u)||(a.add(u),o.push({commit_sha:u,committed_at:d??null,subject:m.join(" ")||null}))}return o}function vS(e){return h().prepare(`SELECT id, cwd, started_at, ended_at
1114
+ FROM sessions WHERE id = ?`).get(e)??null}function IS(e,t,n){if(n.length===0)return 0;let s=h(),r=new Date().toISOString(),o=s.prepare(`INSERT OR IGNORE INTO session_commits
1115
1115
  (session_id, commit_sha, committed_at, subject, cwd_snapshot, correlated_at)
1116
- VALUES (?, ?, ?, ?, ?, ?)`),a=0;return s.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(n),a}async function zo(e){let t=RS(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let n=t.started_at,s=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await SS(t.cwd)).isDirectory())return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}catch{return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}if(!await yS(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await wS(t.cwd,n,s),a=kS(e,t.cwd,o);return{sessionId:e,status:"ok",commitsFound:o.length,commitsInserted:a}}catch(o){return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:o.message}}}function Fs(e){let t=h(),n=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(n))return[];let s=`${n.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1116
+ VALUES (?, ?, ?, ?, ?, ?)`),a=0;return s.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(n),a}async function Qo(e){let t=vS(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let n=t.started_at,s=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await NS(t.cwd)).isDirectory())return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}catch{return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}if(!await CS(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await LS(t.cwd,n,s),a=IS(e,t.cwd,o);return{sessionId:e,status:"ok",commitsFound:o.length,commitsInserted:a}}catch(o){return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:o.message}}}function $s(e){let t=h(),n=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(n))return[];let s=`${n.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1117
1117
  NULLIF(sa.alias, '') AS alias,
1118
1118
  p.name AS project,
1119
1119
  s.started_at AS startedAt,
@@ -1127,18 +1127,18 @@ ${o}
1127
1127
  LEFT JOIN session_aliases sa ON sa.session_id = sc.session_id
1128
1128
  WHERE lower(sc.commit_sha) = lower(?)
1129
1129
  OR lower(sc.commit_sha) LIKE ?
1130
- ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(n,s)}function Ko(e){return h().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1130
+ ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(n,s)}function Zo(e){return h().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1131
1131
  FROM session_commits
1132
1132
  WHERE session_id = ?
1133
- ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var AS=3e4;function uu(e){try{let n=h().prepare(`SELECT MAX(correlated_at) AS last_at
1134
- FROM session_commits WHERE session_id = ?`).get(e),s=n?.last_at?Date.parse(n.last_at):0;if(s&&Date.now()-s<AS)return}catch{}zo(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}U();Q();import{writeFileSync as IS,mkdirSync as MS,existsSync as DS}from"node:fs";import{join as Su}from"node:path";U();var du=80;function pu(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let n=t.split(`
1135
- `)[0].trim();return n.length>du?n.slice(0,du)+"\u2026":n}function xS(e){return h().prepare(`SELECT s.id AS id,
1133
+ ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var MS=3e4;function fu(e){try{let n=h().prepare(`SELECT MAX(correlated_at) AS last_at
1134
+ FROM session_commits WHERE session_id = ?`).get(e),s=n?.last_at?Date.parse(n.last_at):0;if(s&&Date.now()-s<MS)return}catch{}Qo(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}U();Q();import{writeFileSync as BS,mkdirSync as HS,existsSync as WS}from"node:fs";import{join as ku}from"node:path";U();var _u=80;function hu(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let n=t.split(`
1135
+ `)[0].trim();return n.length>_u?n.slice(0,_u)+"\u2026":n}function DS(e){return h().prepare(`SELECT s.id AS id,
1136
1136
  sa.alias AS alias,
1137
1137
  s.auto_title AS auto_title,
1138
1138
  s.first_user_message AS first_user_message
1139
1139
  FROM sessions s
1140
1140
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1141
- WHERE s.id = ?`).get(e)??null}function NS(e){let t=xS(e);return t?pu(t):e.slice(0,8)}function mu(e){if(!e)return null;let t=h(),n=t.prepare(`SELECT e.thread_id AS thread_id,
1141
+ WHERE s.id = ?`).get(e)??null}function jS(e){let t=DS(e);return t?hu(t):e.slice(0,8)}function Eu(e){if(!e)return null;let t=h(),n=t.prepare(`SELECT e.thread_id AS thread_id,
1142
1142
  t.name AS thread_name,
1143
1143
  e.parent_session_id AS parent_session_id,
1144
1144
  e.added_at AS added_at
@@ -1147,7 +1147,7 @@ ${o}
1147
1147
  WHERE e.session_id = ?
1148
1148
  AND t.archived = 0
1149
1149
  ORDER BY e.added_at DESC
1150
- LIMIT 1`).get(e);if(!n)return null;let s=n.parent_session_id?{id:n.parent_session_id,title:NS(n.parent_session_id)}:null,r=n.parent_session_id?[e,n.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1150
+ LIMIT 1`).get(e);if(!n)return null;let s=n.parent_session_id?{id:n.parent_session_id,title:jS(n.parent_session_id)}:null,r=n.parent_session_id?[e,n.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1151
1151
  s.id AS id,
1152
1152
  sa.alias AS alias,
1153
1153
  s.auto_title AS auto_title,
@@ -1157,9 +1157,9 @@ ${o}
1157
1157
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1158
1158
  WHERE e.thread_id = ?
1159
1159
  AND e.session_id NOT IN (${o})
1160
- ORDER BY e.added_at ASC`).all(n.thread_id,...r).map(u=>({id:u.session_id,title:u.id?pu(u):u.session_id.slice(0,8)}));return{thread_id:n.thread_id,thread_name:n.thread_name,parent_session:s,siblings:c}}var gu=[/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Base directory for this skill:/i,/^You are Claude Code in a fresh terminal/i,/^You are extracting a structured Output Index from a Claude/i];function Vo(e){return gu.some(t=>t.test(e))}var OS=[/^You are summarizing a Claude Code session/i,/^You are extracting a structured Output Index from a Claude/i,/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Thread context:\n- This session is part of thread/i];function fu(e){return e?OS.some(t=>t.test(e)):!1}var LS=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i,/^You are summarizing a Claude Code session/i],CS=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],vS=20;function Rn(e){if(e.auto_title_source==="agent"&&e.auto_title)return"agent";if(e.has_alias)return"manual_alias";if(e.auto_title&&e.auto_title.includes(" \xB7 "))return"fixed_v0.16.1";if(!e.auto_title||e.auto_title.length<vS)return"low_signal";for(let t of gu)if(t.test(e.auto_title))return"recursive_meta";for(let t of LS)if(t.test(e.auto_title))return"programmatic";for(let t of CS)if(t.test(e.auto_title))return"template_pending";return"clean"}function $s(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function _u(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let n=e.slice(0,t),s=e.slice(t+3),r=$s(s);return!r||r===s?e:`${n} \xB7 ${r}`}var ei=Su(B,"titles"),jS=80,PS=60,FS=100,$S=50,kn=5,Us=15,US=500;function Tu(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>!!n&&typeof n=="object"&&typeof n.title=="string"&&typeof n.replaced_at=="string")}catch{}return[]}function jt(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let n=HS(t,e);if(n)return n;let s=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(s&&s.length<=jS?s:t.slice(0,PS)).trim()||null}function HS(e,t){let n=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(n){let s=`/${n[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=ti(r);return o?Dt(`${s} \xB7 ${o}`):s}for(let s of BS){if(!e.match(s.match))continue;let o=s.prefix,a=s.extract?s.extract(e,t):ti(t);return a?s.completeFromExtract?Dt(a):Dt(`${o} \xB7 ${a}`):o}for(let s of XS){if(!e.match(s.match))continue;let o=s.extract?s.extract(e,t):Hs(t);return o?s.completeFromExtract?Dt(o):Dt(`${s.prefix} \xB7 ${o}`):s.prefix}return null}var BS=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let s=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=ti(t);return s&&r?`${s} \xB7 ${r}`:s||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let n=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return n?n.split("/").filter(Boolean).slice(-2).join("/")||n:null}},{match:/^(?:read|inspect) this and (?:then )?begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>qS(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let n=t.match(/\.claude\/skills\/([^/\s]+)/);return n?.[1]?`[skill] ${n[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>WS(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>hu(t,"[meta] auto-title (full-arc)")},{match:/^You will receive the first \d+ user messages from a Claude Code session/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>hu(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let n=t[1],s=t[2]?.trim();return s?`Implementing ${n} \xB7 ${s}`:`Implementing ${n}`}}];function WS(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),n=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),s=t?.[1]?.trim(),r=n?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return s&&o?`[output-index] \xB7 ${s} \xB7 ${o}`:s?`[output-index] \xB7 ${s}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function hu(e,t){if(!e)return t;let n=e.indexOf("Messages:");if(n===-1)return t;let s=e.slice(n+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of s.matchAll(r)){let a=o[1]?.trim()??"";if(!a)continue;let c=a.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!c||/^<local-command-/.test(c))continue;let u=c.length>60?c.slice(0,57)+"\u2026":c;return`${t} \xB7 ${u}`}return t}function qS(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let n=t[1].split("/").filter(Boolean);return(n[n.length-1]??"").replace(/\.[^.]+$/,"")||null}var XS=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>Hs(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let s=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=s?`${s} co-pilot`:"co-pilot",o=Hs(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>Hs(t)}];function Hs(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,n;for(;(n=t.exec(e))!==null;){let s=n[2].trim();if(!s||/^(null|none|n\/a|—|-)$/i.test(s))continue;let r=s.replace(/\s+/g," ");return $s(r)||r}return null}function Dt(e){return e.slice(0,FS).trim()}var JS=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],GS=[/^(?:brand|brand brief|brand summary)$/i,/^(?:known facts?|facts)$/i,/^(?:client|prospect|customer|account)$/i,/^(?:topic|subject|brief|task)$/i,/^(?:about|for|re)$/i];function Qo(e){let t=e.trim();return t.length<3?!0:JS.some(n=>n.test(t))}function YS(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return GS.some(n=>n.test(t))}function ti(e){let t=zS(e);return t===null?null:$s(t)||t}function zS(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,n,s=[];for(;(n=t.exec(e))!==null;){let u=n[2].trim(),d=n[3].trim().replace(/\s+/g," ");if(!Qo(d)){if(YS(u))return d;s.push(d)}}if(s.length>0)return s[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!Qo(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(n=o.exec(e))!==null;){let u=n[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!Qo(d)&&!/product context|reference/i.test(u))return d}let a=e.replace(/^Context for this run[^:]*:\s*/i,"");if(a!==e){let u=a.split(`
1160
+ ORDER BY e.added_at ASC`).all(n.thread_id,...r).map(u=>({id:u.session_id,title:u.id?hu(u):u.session_id.slice(0,8)}));return{thread_id:n.thread_id,thread_name:n.thread_name,parent_session:s,siblings:c}}var bu=[/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Base directory for this skill:/i,/^You are Claude Code in a fresh terminal/i,/^You are extracting a structured Output Index from a Claude/i];function ei(e){return bu.some(t=>t.test(e))}var PS=[/^You are summarizing a Claude Code session/i,/^You are extracting a structured Output Index from a Claude/i,/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Thread context:\n- This session is part of thread/i];function Su(e){return e?PS.some(t=>t.test(e)):!1}var FS=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i,/^You are summarizing a Claude Code session/i],$S=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],US=20;function Rn(e){if(e.auto_title_source==="agent"&&e.auto_title)return"agent";if(e.has_alias)return"manual_alias";if(e.auto_title&&e.auto_title.includes(" \xB7 "))return"fixed_v0.16.1";if(!e.auto_title||e.auto_title.length<US)return"low_signal";for(let t of bu)if(t.test(e.auto_title))return"recursive_meta";for(let t of FS)if(t.test(e.auto_title))return"programmatic";for(let t of $S)if(t.test(e.auto_title))return"template_pending";return"clean"}function Us(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function Tu(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let n=e.slice(0,t),s=e.slice(t+3),r=Us(s);return!r||r===s?e:`${n} \xB7 ${r}`}var si=ku(H,"titles"),qS=80,XS=60,JS=100,GS=50,kn=5,Bs=15,YS=500;function Au(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>!!n&&typeof n=="object"&&typeof n.title=="string"&&typeof n.replaced_at=="string")}catch{}return[]}function jt(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let n=zS(t,e);if(n)return n;let s=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(s&&s.length<=qS?s:t.slice(0,XS)).trim()||null}function zS(e,t){let n=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(n){let s=`/${n[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=ri(r);return o?Dt(`${s} \xB7 ${o}`):s}for(let s of KS){if(!e.match(s.match))continue;let o=s.prefix,a=s.extract?s.extract(e,t):ri(t);return a?s.completeFromExtract?Dt(a):Dt(`${o} \xB7 ${a}`):o}for(let s of ZS){if(!e.match(s.match))continue;let o=s.extract?s.extract(e,t):Hs(t);return o?s.completeFromExtract?Dt(o):Dt(`${s.prefix} \xB7 ${o}`):s.prefix}return null}var KS=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let s=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=ri(t);return s&&r?`${s} \xB7 ${r}`:s||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let n=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return n?n.split("/").filter(Boolean).slice(-2).join("/")||n:null}},{match:/^(?:read|inspect) this and (?:then )?begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>QS(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let n=t.match(/\.claude\/skills\/([^/\s]+)/);return n?.[1]?`[skill] ${n[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>VS(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>yu(t,"[meta] auto-title (full-arc)")},{match:/^You will receive the first \d+ user messages from a Claude Code session/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>yu(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let n=t[1],s=t[2]?.trim();return s?`Implementing ${n} \xB7 ${s}`:`Implementing ${n}`}}];function VS(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),n=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),s=t?.[1]?.trim(),r=n?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return s&&o?`[output-index] \xB7 ${s} \xB7 ${o}`:s?`[output-index] \xB7 ${s}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function yu(e,t){if(!e)return t;let n=e.indexOf("Messages:");if(n===-1)return t;let s=e.slice(n+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of s.matchAll(r)){let a=o[1]?.trim()??"";if(!a)continue;let c=a.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!c||/^<local-command-/.test(c))continue;let u=c.length>60?c.slice(0,57)+"\u2026":c;return`${t} \xB7 ${u}`}return t}function QS(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let n=t[1].split("/").filter(Boolean);return(n[n.length-1]??"").replace(/\.[^.]+$/,"")||null}var ZS=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>Hs(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let s=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=s?`${s} co-pilot`:"co-pilot",o=Hs(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>Hs(t)}];function Hs(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,n;for(;(n=t.exec(e))!==null;){let s=n[2].trim();if(!s||/^(null|none|n\/a|—|-)$/i.test(s))continue;let r=s.replace(/\s+/g," ");return Us(r)||r}return null}function Dt(e){return e.slice(0,JS).trim()}var eT=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],tT=[/^(?:brand|brand brief|brand summary)$/i,/^(?:known facts?|facts)$/i,/^(?:client|prospect|customer|account)$/i,/^(?:topic|subject|brief|task)$/i,/^(?:about|for|re)$/i];function ti(e){let t=e.trim();return t.length<3?!0:eT.some(n=>n.test(t))}function nT(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return tT.some(n=>n.test(t))}function ri(e){let t=sT(e);return t===null?null:Us(t)||t}function sT(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,n,s=[];for(;(n=t.exec(e))!==null;){let u=n[2].trim(),d=n[3].trim().replace(/\s+/g," ");if(!ti(d)){if(nT(u))return d;s.push(d)}}if(s.length>0)return s[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!ti(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(n=o.exec(e))!==null;){let u=n[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!ti(d)&&!/product context|reference/i.test(u))return d}let a=e.replace(/^Context for this run[^:]*:\s*/i,"");if(a!==e){let u=a.split(`
1161
1161
  `).map(d=>d.trim()).find(d=>d.length>=4);if(u)return u.slice(0,60)}let c=e.split(`
1162
- `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function ni(e){let t=h(),n=t.prepare(`SELECT rowid AS rid, content_text
1162
+ `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function oi(e){let t=h(),n=t.prepare(`SELECT rowid AS rid, content_text
1163
1163
  FROM messages
1164
1164
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1165
1165
  AND content_text IS NOT NULL AND content_text != ''
@@ -1169,21 +1169,21 @@ ${o}
1169
1169
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1170
1170
  AND content_text IS NOT NULL AND content_text != ''
1171
1171
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1172
- LIMIT ?`).all(e,Us),r=new Map;for(let m of n)r.set(m.rid,m.content_text);for(let m of s)r.set(m.rid,m.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let o=Array.from(r.entries()).sort((m,f)=>m[0]-f[0]).map(([,m])=>({content_text:m})),a=n.length===kn&&s.length===Us&&r.size===kn+Us,c=o.map((m,f)=>{let b=(m.content_text??"").slice(0,US);return a&&f===kn?`--- (middle of session omitted) ---
1172
+ LIMIT ?`).all(e,Bs),r=new Map;for(let m of n)r.set(m.rid,m.content_text);for(let m of s)r.set(m.rid,m.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let o=Array.from(r.entries()).sort((m,f)=>m[0]-f[0]).map(([,m])=>({content_text:m})),a=n.length===kn&&s.length===Bs&&r.size===kn+Bs,c=o.map((m,f)=>{let b=(m.content_text??"").slice(0,YS);return a&&f===kn?`--- (middle of session omitted) ---
1173
1173
  ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1174
- `),u=null;try{u=mu(e)}catch(m){console.error("[autoTitle] thread context resolution failed:",m),u=null}let d=[];return u&&(d.push(KS(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${kn}`,`messages (initial intent) and the last ${Us} messages (current direction).`,"Write a single descriptive title, max 50 characters, focused on what the user is","currently trying to accomplish. If initial intent and current direction differ, prefer","the current direction. Output ONLY the title, with no quotes and no trailing punctuation.","","Messages:",c),d.join(`
1175
- `)}var Zo=5;function KS(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let n=e.siblings.length;if(n>0){let r=e.siblings.slice(0,Zo).map(a=>`"${a.title}"`).join(", "),o=n>Zo?`, and ${n-Zo} more`:"";t.push(`- Sibling sessions (${n}): ${r}${o}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
1176
- `)}async function yu(e){let t=ni(e),{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(we(),pt));if(!s())throw new Error("claude CLI not found on PATH");let r=await n(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=VS(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,$S)}function VS(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return Eu(s)}}catch{}return Eu(t)}function Eu(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function he(e,t,n){let s=t.trim();if(!s)return;let r=h(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1177
- FROM sessions WHERE id = ?`).get(e);if(!o||n==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===s&&o.auto_title_source===n)return;let a=Tu(o.auto_title_history),c=new Date().toISOString();o.auto_title&&o.auto_title_source&&a.push({title:o.auto_title,source:o.auto_title_source,replaced_at:c}),r.prepare(`UPDATE sessions
1174
+ `),u=null;try{u=Eu(e)}catch(m){console.error("[autoTitle] thread context resolution failed:",m),u=null}let d=[];return u&&(d.push(rT(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${kn}`,`messages (initial intent) and the last ${Bs} messages (current direction).`,"Write a single descriptive title, max 50 characters, focused on what the user is","currently trying to accomplish. If initial intent and current direction differ, prefer","the current direction. Output ONLY the title, with no quotes and no trailing punctuation.","","Messages:",c),d.join(`
1175
+ `)}var ni=5;function rT(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let n=e.siblings.length;if(n>0){let r=e.siblings.slice(0,ni).map(a=>`"${a.title}"`).join(", "),o=n>ni?`, and ${n-ni} more`:"";t.push(`- Sibling sessions (${n}): ${r}${o}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
1176
+ `)}async function xu(e){let t=oi(e),{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(we(),pt));if(!s())throw new Error("claude CLI not found on PATH");let r=await n(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=oT(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,GS)}function oT(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return wu(s)}}catch{}return wu(t)}function wu(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function he(e,t,n){let s=t.trim();if(!s)return;let r=h(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1177
+ FROM sessions WHERE id = ?`).get(e);if(!o||n==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===s&&o.auto_title_source===n)return;let a=Au(o.auto_title_history),c=new Date().toISOString();o.auto_title&&o.auto_title_source&&a.push({title:o.auto_title,source:o.auto_title_source,replaced_at:c}),r.prepare(`UPDATE sessions
1178
1178
  SET auto_title = ?,
1179
1179
  auto_title_source = ?,
1180
1180
  auto_title_generated_at = ?,
1181
1181
  auto_title_history = ?
1182
- WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(a),e),tT(e,s,n,c)}function ve(e){let t=h().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1183
- FROM sessions WHERE id = ?`).get(e);return t?{auto_title:t.auto_title,auto_title_source:t.auto_title_source??null,auto_title_generated_at:t.auto_title_generated_at,auto_title_history:Tu(t.auto_title_history)}:null}function wu(){let t=h().prepare(`SELECT id, first_user_message
1182
+ WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(a),e),lT(e,s,n,c)}function ve(e){let t=h().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1183
+ FROM sessions WHERE id = ?`).get(e);return t?{auto_title:t.auto_title,auto_title_source:t.auto_title_source??null,auto_title_generated_at:t.auto_title_generated_at,auto_title_history:Au(t.auto_title_history)}:null}function Nu(){let t=h().prepare(`SELECT id, first_user_message
1184
1184
  FROM sessions
1185
1185
  WHERE auto_title IS NULL
1186
- AND first_user_message IS NOT NULL`).all(),n=0;for(let s of t){let r=jt(s.first_user_message);r&&(he(s.id,r,"heuristic"),n+=1)}return{updated:n}}function Ru(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1186
+ AND first_user_message IS NOT NULL`).all(),n=0;for(let s of t){let r=jt(s.first_user_message);r&&(he(s.id,r,"heuristic"),n+=1)}return{updated:n}}function Ou(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1187
1187
  SELECT auto_title, project_id
1188
1188
  FROM sessions
1189
1189
  WHERE auto_title IS NOT NULL
@@ -1236,7 +1236,7 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1236
1236
  AND content_text IS NOT NULL
1237
1237
  AND content_text != ''
1238
1238
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1239
- LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=null;for(let f of d){let b=f.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!b||/^<local-command-caveat>/.test(b))continue;let T=jt(b);if(T&&!bu(T)){m=T;break}}if(!m){let f=d.map(T=>T.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(T=>T.length>0&&!/^<local-command-caveat>/.test(T)),b=f?jt(f):null;b&&bu(b)&&(m=`[vacuous] ${b}`)}m&&(he(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function bu(e){let t=e.trim();return!t||/^\*\*Tool result\*\*$/i.test(t)?!0:[/^all right\.?$/i,/^alright\.?$/i,/^inspect this\.?$/i,/^resolve this\.?$/i,/^do it\.?$/i,/^go\.?$/i,/^continue\.?$/i,/^proceed\.?$/i,/^keep going\.?$/i,/^ok\.?$/i,/^okay\.?$/i,/^yes\.?$/i,/^yep\.?$/i].some(s=>s.test(t))}function ku(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1239
+ LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=null;for(let f of d){let b=f.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!b||/^<local-command-caveat>/.test(b))continue;let T=jt(b);if(T&&!Ru(T)){m=T;break}}if(!m){let f=d.map(T=>T.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(T=>T.length>0&&!/^<local-command-caveat>/.test(T)),b=f?jt(f):null;b&&Ru(b)&&(m=`[vacuous] ${b}`)}m&&(he(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function Ru(e){let t=e.trim();return!t||/^\*\*Tool result\*\*$/i.test(t)?!0:[/^all right\.?$/i,/^alright\.?$/i,/^inspect this\.?$/i,/^resolve this\.?$/i,/^do it\.?$/i,/^go\.?$/i,/^continue\.?$/i,/^proceed\.?$/i,/^keep going\.?$/i,/^ok\.?$/i,/^okay\.?$/i,/^yes\.?$/i,/^yep\.?$/i].some(s=>s.test(t))}function Cu(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1240
1240
  FROM sessions s
1241
1241
  WHERE s.auto_title_source = 'heuristic'${n}
1242
1242
  AND (
@@ -1251,47 +1251,47 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1251
1251
  AND content_text IS NOT NULL
1252
1252
  AND content_text != ''
1253
1253
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1254
- LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=QS(d,u.auto_title);m&&(he(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function QS(e,t){for(let n of e){let s=ZS(n.content_text);if(!s||Vo(s))continue;let r=jt(s);if(r){if(r===t)return null;if(!Vo(r))return r}}return t.startsWith("[meta]")?null:Dt(`[meta] ${t}`)}function Au(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1254
+ LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=iT(d,u.auto_title);m&&(he(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function iT(e,t){for(let n of e){let s=aT(n.content_text);if(!s||ei(s))continue;let r=jt(s);if(r){if(r===t)return null;if(!ei(r))return r}}return t.startsWith("[meta]")?null:Dt(`[meta] ${t}`)}function Lu(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1255
1255
  FROM sessions s
1256
1256
  WHERE s.auto_title_source = 'heuristic'${n}
1257
1257
  AND s.auto_title IS NOT NULL
1258
1258
  AND s.auto_title LIKE '% \xB7 %'
1259
- AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...s),o=0,a=0;for(let c of r){o+=1;let u=_u(c.auto_title);u!==c.auto_title&&(he(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function ZS(e){return e.replace(/<command-(?:name|message|args|stdout|stderr)>[\s\S]*?<\/command-(?:name|message|args|stdout|stderr)>/g,"").replace(/<local-command-(?:stdout|stderr|caveat)>[\s\S]*?<\/local-command-(?:stdout|stderr|caveat)>/g,"").trim()}function eT(){z(),DS(ei)||MS(ei,{recursive:!0})}function tT(e,t,n,s){try{eT();let r=Su(ei,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
1260
- `;IS(r,o+t+`
1261
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}import{existsSync as xu,mkdirSync as nT,readFileSync as sT,writeFileSync as rT}from"node:fs";import{homedir as oT}from"node:os";import{join as Nu}from"node:path";import{z as si}from"zod";function Ou(){return process.env.RECALL_HOME??Nu(oT(),".recall")}function iT(){let e=Ou();xu(e)||nT(e,{recursive:!0})}function Lu(){return Nu(Ou(),"config.json")}var Ws=si.object({heuristicEnabled:si.boolean().default(!0),agentEnabled:si.boolean().default(!1)}),Bs={heuristicEnabled:!0,agentEnabled:!1};function Cu(){let e=Lu();if(!xu(e))return{};try{return JSON.parse(sT(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function _t(){let e=Cu().autoTitle;if(!e)return{...Bs};let t=Ws.safeParse({...Bs,...e});return t.success?t.data:{...Bs}}function vu(e){iT();let t=Cu(),n=Ws.parse({...Bs,...t.autoTitle??{},...e}),s={...t,autoTitle:n};return rT(Lu(),JSON.stringify(s,null,2)),n}U();Q();import{randomUUID as ai}from"node:crypto";import{existsSync as mT,mkdirSync as gT,writeFileSync as Hu}from"node:fs";import{homedir as fT}from"node:os";import{basename as _T,join as ci}from"node:path";U();Q();import{randomUUID as aT}from"node:crypto";import{writeFileSync as cT,readFileSync as kC,existsSync as AC}from"node:fs";import{join as lT}from"node:path";var uT=lT(B,"collections.json"),qs=8;function Xs(e){return{...e}}function ke(e,t,n,s=null,r=new Date().toISOString()){h().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1262
- VALUES (?, ?, ?, ?, ?)`).run(e,s,t,n?JSON.stringify(n):null,r)}function Js(e){let t=h().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function Iu(e){if(!e)return 0;let t=0,n=e,s=new Set,r=h();for(;n;){if(s.has(n))throw new Error("collection cycle detected");s.add(n);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)break;t+=1,n=o.parent_id}return t}function dT(e,t){let n=h(),s=e,r=new Set;for(;s;){if(r.has(s))return!1;if(r.add(s),s===t)return!0;let o=n.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)return!1;s=o.parent_id}return!1}function Mu(e=!1){return h().prepare(`SELECT c.*,
1259
+ AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...s),o=0,a=0;for(let c of r){o+=1;let u=Tu(c.auto_title);u!==c.auto_title&&(he(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function aT(e){return e.replace(/<command-(?:name|message|args|stdout|stderr)>[\s\S]*?<\/command-(?:name|message|args|stdout|stderr)>/g,"").replace(/<local-command-(?:stdout|stderr|caveat)>[\s\S]*?<\/local-command-(?:stdout|stderr|caveat)>/g,"").trim()}function cT(){z(),WS(si)||HS(si,{recursive:!0})}function lT(e,t,n,s){try{cT();let r=ku(si,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
1260
+ `;BS(r,o+t+`
1261
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}import{existsSync as vu,mkdirSync as uT,readFileSync as dT,writeFileSync as pT}from"node:fs";import{homedir as mT}from"node:os";import{join as Iu}from"node:path";import{z as ii}from"zod";function Mu(){return process.env.RECALL_HOME??Iu(mT(),".recall")}function gT(){let e=Mu();vu(e)||uT(e,{recursive:!0})}function Du(){return Iu(Mu(),"config.json")}var qs=ii.object({heuristicEnabled:ii.boolean().default(!0),agentEnabled:ii.boolean().default(!1)}),Ws={heuristicEnabled:!0,agentEnabled:!1};function ju(){let e=Du();if(!vu(e))return{};try{return JSON.parse(dT(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function _t(){let e=ju().autoTitle;if(!e)return{...Ws};let t=qs.safeParse({...Ws,...e});return t.success?t.data:{...Ws}}function Pu(e){gT();let t=ju(),n=qs.parse({...Ws,...t.autoTitle??{},...e}),s={...t,autoTitle:n};return pT(Du(),JSON.stringify(s,null,2)),n}U();Q();import{randomUUID as ui}from"node:crypto";import{existsSync as TT,mkdirSync as yT,writeFileSync as Ju}from"node:fs";import{homedir as wT}from"node:os";import{basename as RT,join as di}from"node:path";U();Q();import{randomUUID as fT}from"node:crypto";import{writeFileSync as _T,readFileSync as DL,existsSync as jL}from"node:fs";import{join as hT}from"node:path";var ET=hT(H,"collections.json"),Xs=8;function Js(e){return{...e}}function ke(e,t,n,s=null,r=new Date().toISOString()){h().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1262
+ VALUES (?, ?, ?, ?, ?)`).run(e,s,t,n?JSON.stringify(n):null,r)}function Gs(e){let t=h().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function Fu(e){if(!e)return 0;let t=0,n=e,s=new Set,r=h();for(;n;){if(s.has(n))throw new Error("collection cycle detected");s.add(n);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)break;t+=1,n=o.parent_id}return t}function bT(e,t){let n=h(),s=e,r=new Set;for(;s;){if(r.has(s))return!1;if(r.add(s),s===t)return!0;let o=n.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)return!1;s=o.parent_id}return!1}function $u(e=!1){return h().prepare(`SELECT c.*,
1263
1263
  (SELECT COUNT(*) FROM collection_sessions cs WHERE cs.collection_id = c.id) AS session_count
1264
1264
  FROM collections c
1265
1265
  ${e?"":"WHERE c.archived_at IS NULL"}
1266
- ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(s=>({...Xs(s),session_count:s.session_count}))}function Qe(e){let t=h().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Xs(t):null}function Du(e,t=!0){let n=h(),s=t?ri(e):[e];if(s.length===0)return[];let r=s.map(()=>"?").join(",");return n.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1266
+ ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(s=>({...Js(s),session_count:s.session_count}))}function Qe(e){let t=h().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Js(t):null}function Uu(e,t=!0){let n=h(),s=t?ai(e):[e];if(s.length===0)return[];let r=s.map(()=>"?").join(",");return n.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1267
1267
  FROM collection_sessions
1268
1268
  WHERE collection_id IN (${r})
1269
- ORDER BY added_at DESC`).all(...s)}function ri(e){let t=h(),n=[e],s=[e],r=new Set([e]);for(;s.length>0;){let o=s.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...s),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),n.push(u.id),c.push(u.id));s=c}return n}function ju(e){return h().prepare(`SELECT c.* FROM collections c
1269
+ ORDER BY added_at DESC`).all(...s)}function ai(e){let t=h(),n=[e],s=[e],r=new Set([e]);for(;s.length>0;){let o=s.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...s),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),n.push(u.id),c.push(u.id));s=c}return n}function Bu(e){return h().prepare(`SELECT c.* FROM collections c
1270
1270
  JOIN collection_sessions cs ON cs.collection_id = c.id
1271
1271
  WHERE cs.session_id = ? AND c.archived_at IS NULL
1272
- ORDER BY LOWER(c.name)`).all(e)}function An(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let n=h(),s=new Date().toISOString(),r=aT();if(e.parent_id){if(!Qe(e.parent_id))throw new Error("parent collection not found");if(Iu(e.parent_id)>=qs-1)throw new Error(`max collection depth is ${qs}`)}return n.transaction(()=>{n.prepare(`INSERT INTO collections
1272
+ ORDER BY LOWER(c.name)`).all(e)}function An(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let n=h(),s=new Date().toISOString(),r=fT();if(e.parent_id){if(!Qe(e.parent_id))throw new Error("parent collection not found");if(Fu(e.parent_id)>=Xs-1)throw new Error(`max collection depth is ${Xs}`)}return n.transaction(()=>{n.prepare(`INSERT INTO collections
1273
1273
  (id, name, description, icon, color, parent_id, sort_key, created_at, updated_at, archived_at)
1274
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",s,s),ke(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,s)})(),ht(),Qe(r)}function Pu(e,t){let n=h(),s=Js(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():s.name,description:t.description!==void 0?t.description:s.description,icon:t.icon!==void 0?t.icon:s.icon,color:t.color!==void 0?t.color:s.color,parent_id:t.parent_id!==void 0?t.parent_id:s.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:s.sort_key};if(!o.name)throw new Error("name required");if(o.name.length>120)throw new Error("name too long (max 120 chars)");if(t.parent_id!==void 0&&t.parent_id!==s.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Qe(t.parent_id))throw new Error("parent collection not found");if(dT(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(Iu(t.parent_id)>=qs-1)throw new Error(`max collection depth is ${qs}`)}return n.transaction(()=>{n.prepare(`UPDATE collections
1274
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",s,s),ke(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,s)})(),ht(),Qe(r)}function Hu(e,t){let n=h(),s=Gs(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():s.name,description:t.description!==void 0?t.description:s.description,icon:t.icon!==void 0?t.icon:s.icon,color:t.color!==void 0?t.color:s.color,parent_id:t.parent_id!==void 0?t.parent_id:s.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:s.sort_key};if(!o.name)throw new Error("name required");if(o.name.length>120)throw new Error("name too long (max 120 chars)");if(t.parent_id!==void 0&&t.parent_id!==s.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Qe(t.parent_id))throw new Error("parent collection not found");if(bT(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(Fu(t.parent_id)>=Xs-1)throw new Error(`max collection depth is ${Xs}`)}return n.transaction(()=>{n.prepare(`UPDATE collections
1275
1275
  SET name = ?, description = ?, icon = ?, color = ?,
1276
1276
  parent_id = ?, sort_key = ?, updated_at = ?
1277
- WHERE id = ?`).run(o.name,o.description,o.icon,o.color,o.parent_id,o.sort_key,r,e),t.name!==void 0&&t.name!==s.name&&ke(e,"rename",{from:s.name,to:o.name},null,r),t.description!==void 0&&t.description!==s.description&&ke(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==s.icon||t.color!==void 0&&t.color!==s.color)&&ke(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==s.parent_id&&ke(e,"move",{from:s.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==s.sort_key&&ke(e,"reorder",{from:s.sort_key,to:o.sort_key},null,r)})(),ht(),Qe(e)}function Fu(e){let t=h(),n=Js(e);if(n.archived_at)return Xs(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(s,s,e),ke(e,"archive",{name:n.name},null,s)})(),ht(),Qe(e)}function $u(e){let t=h(),n=Js(e);if(!n.archived_at)return Xs(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(s,e),ke(e,"restore",{name:n.name},null,s)})(),ht(),Qe(e)}function xn(e,t,n=null,s={}){let r=h();if(Js(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let c=s.source??"manual",u=s.rule_id??null;if(c==="auto"&&!u)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
1278
- VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,n,c,u),ke(e,"add",{note:n,source:c,rule_id:u},t,d)})(),ht(),{added:!0}}function Uu(e,t){let n=h();if(!n.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return n.transaction(()=>{n.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),ke(e,"remove",null,t,r)})(),ht(),{removed:!0}}function Gs(e){let t=h(),n=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1279
- WHERE rule_id = ?`).all(e);if(n.length===0)return{removed:0};let s=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of n)ke(o.collection_id,"remove",{rule_id:e},o.session_id,s)})(),ht(),{removed:n.length}}function pT(){return h().prepare(`SELECT id, collection_id, session_id, action, payload, at
1277
+ WHERE id = ?`).run(o.name,o.description,o.icon,o.color,o.parent_id,o.sort_key,r,e),t.name!==void 0&&t.name!==s.name&&ke(e,"rename",{from:s.name,to:o.name},null,r),t.description!==void 0&&t.description!==s.description&&ke(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==s.icon||t.color!==void 0&&t.color!==s.color)&&ke(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==s.parent_id&&ke(e,"move",{from:s.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==s.sort_key&&ke(e,"reorder",{from:s.sort_key,to:o.sort_key},null,r)})(),ht(),Qe(e)}function Wu(e){let t=h(),n=Gs(e);if(n.archived_at)return Js(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(s,s,e),ke(e,"archive",{name:n.name},null,s)})(),ht(),Qe(e)}function qu(e){let t=h(),n=Gs(e);if(!n.archived_at)return Js(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(s,e),ke(e,"restore",{name:n.name},null,s)})(),ht(),Qe(e)}function xn(e,t,n=null,s={}){let r=h();if(Gs(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let c=s.source??"manual",u=s.rule_id??null;if(c==="auto"&&!u)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
1278
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,n,c,u),ke(e,"add",{note:n,source:c,rule_id:u},t,d)})(),ht(),{added:!0}}function Xu(e,t){let n=h();if(!n.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return n.transaction(()=>{n.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),ke(e,"remove",null,t,r)})(),ht(),{removed:!0}}function Ys(e){let t=h(),n=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1279
+ WHERE rule_id = ?`).all(e);if(n.length===0)return{removed:0};let s=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of n)ke(o.collection_id,"remove",{rule_id:e},o.session_id,s)})(),ht(),{removed:n.length}}function ST(){return h().prepare(`SELECT id, collection_id, session_id, action, payload, at
1280
1280
  FROM collection_events
1281
1281
  ORDER BY at ASC, id ASC`).all()}function ht(){try{z();let e=h(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
1282
1282
  created_at, updated_at, archived_at
1283
1283
  FROM collections
1284
1284
  ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),n=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1285
1285
  FROM collection_sessions
1286
- ORDER BY collection_id, added_at`).all(),s=pT(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:n,events:s};cT(uT,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Ys=ci(B,"auto-rules"),hT=ci(Ys,"rules.json"),ET=ci(Ys,"suggestions.json"),oi="Repositories",bT="Topics",Bu=3;var ST=5,TT=2,yT=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function li(e){return{id:e.id,name:e.name,type:e.type,pattern:e.pattern,collection_id:e.collection_id,priority:e.priority,enabled:e.enabled!==0,created_at:e.created_at,created_by:e.created_by}}function wT(e){return{id:e.id,type:e.type,pattern:e.pattern,suggested_name:e.suggested_name,suggested_parent_collection_id:e.suggested_parent_collection_id,session_count:e.session_count,detected_at:e.detected_at,dismissed:e.dismissed!==0}}function RT(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return oi;case"tag":return bT;case"plan-file":return null}}function kT(e){let n=h().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(n)return n.id;let o=An({name:e,icon:e===oi?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===oi?"0000-repos":"0001-topics"});return h().prepare(`INSERT INTO auto_collection_rules
1286
+ ORDER BY collection_id, added_at`).all(),s=ST(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:n,events:s};_T(ET,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var zs=di(H,"auto-rules"),kT=di(zs,"rules.json"),AT=di(zs,"suggestions.json"),ci="Repositories",xT="Topics",Gu=3;var NT=5,OT=2,CT=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function pi(e){return{id:e.id,name:e.name,type:e.type,pattern:e.pattern,collection_id:e.collection_id,priority:e.priority,enabled:e.enabled!==0,created_at:e.created_at,created_by:e.created_by}}function LT(e){return{id:e.id,type:e.type,pattern:e.pattern,suggested_name:e.suggested_name,suggested_parent_collection_id:e.suggested_parent_collection_id,session_count:e.session_count,detected_at:e.detected_at,dismissed:e.dismissed!==0}}function vT(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return ci;case"tag":return xT;case"plan-file":return null}}function IT(e){let n=h().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(n)return n.id;let o=An({name:e,icon:e===ci?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===ci?"0000-repos":"0001-topics"});return h().prepare(`INSERT INTO auto_collection_rules
1287
1287
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
1288
- VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(ai(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function AT(e,t,n){let s;if(n!==void 0)s=n;else{let o=RT(t);s=o?kT(o):null}return An({name:e,parent_id:s}).id}function zs(e){let t=h().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?li(t):null}function xT(e){let t=h();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(Ks(e.pattern)+"%").map(s=>s.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(Ks(e.pattern)+"%").map(s=>s.id);case"project-id":{let n=Number(e.pattern);return Number.isFinite(n)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(n).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(s=>s.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1289
- WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(s=>s.id)}}function Wu(e,t,n=3){let s=h(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1288
+ VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(ui(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function MT(e,t,n){let s;if(n!==void 0)s=n;else{let o=vT(t);s=o?IT(o):null}return An({name:e,parent_id:s}).id}function Ks(e){let t=h().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?pi(t):null}function DT(e){let t=h();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(Vs(e.pattern)+"%").map(s=>s.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(Vs(e.pattern)+"%").map(s=>s.id);case"project-id":{let n=Number(e.pattern);return Number.isFinite(n)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(n).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(s=>s.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1289
+ WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(s=>s.id)}}function Yu(e,t,n=3){let s=h(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1290
1290
  sa.alias AS alias, s.auto_title AS auto_title, s.first_user_message AS first_user_message`,o="LEFT JOIN session_aliases sa ON sa.session_id = s.id",a=[];switch(e){case"cwd-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1291
1291
  WHERE s.cwd IS NOT NULL AND s.cwd LIKE ? ESCAPE '\\'
1292
- ORDER BY s.started_at DESC LIMIT ?`).all(Ks(t)+"%",n);break;case"git-branch-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1292
+ ORDER BY s.started_at DESC LIMIT ?`).all(Vs(t)+"%",n);break;case"git-branch-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1293
1293
  WHERE s.git_branch IS NOT NULL AND s.git_branch LIKE ? ESCAPE '\\'
1294
- ORDER BY s.started_at DESC LIMIT ?`).all(Ks(t)+"%",n);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1294
+ ORDER BY s.started_at DESC LIMIT ?`).all(Vs(t)+"%",n);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1295
1295
  WHERE s.project_id = ?
1296
1296
  ORDER BY s.started_at DESC LIMIT ?`).all(c,n));break}case"tag":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1297
1297
  JOIN session_tags st ON st.session_id = s.id
@@ -1299,21 +1299,21 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1299
1299
  ORDER BY s.started_at DESC LIMIT ?`).all(t,n);break;case"plan-file":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1300
1300
  WHERE s.first_user_message IS NOT NULL
1301
1301
  AND s.first_user_message LIKE ?
1302
- ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",n);break}return a.map(c=>({id:c.id,title:NT(c),cwd:c.cwd,started_at:c.started_at}))}function NT(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return`session ${e.id.slice(0,8)}`;let n=t.split(`
1303
- `)[0].trim();return n.length>80?n.slice(0,80)+"\u2026":n}function OT(e,t,n=h()){switch(e.type){case"cwd-prefix":return!!t.cwd&&t.cwd.startsWith(e.pattern);case"git-branch-prefix":return!!t.git_branch&&t.git_branch.startsWith(e.pattern);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)&&t.project_id===s}case"tag":return!!n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(t.id,e.pattern);case"plan-file":return!!t.first_user_message&&t.first_user_message.includes(e.pattern)}}function qu(e){let t=h(),n=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!n)return{added:0};let s=t.prepare(`SELECT * FROM auto_collection_rules
1302
+ ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",n);break}return a.map(c=>({id:c.id,title:jT(c),cwd:c.cwd,started_at:c.started_at}))}function jT(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return`session ${e.id.slice(0,8)}`;let n=t.split(`
1303
+ `)[0].trim();return n.length>80?n.slice(0,80)+"\u2026":n}function PT(e,t,n=h()){switch(e.type){case"cwd-prefix":return!!t.cwd&&t.cwd.startsWith(e.pattern);case"git-branch-prefix":return!!t.git_branch&&t.git_branch.startsWith(e.pattern);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)&&t.project_id===s}case"tag":return!!n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(t.id,e.pattern);case"plan-file":return!!t.first_user_message&&t.first_user_message.includes(e.pattern)}}function zu(e){let t=h(),n=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!n)return{added:0};let s=t.prepare(`SELECT * FROM auto_collection_rules
1304
1304
  WHERE enabled = 1 AND created_by != 'seed'
1305
- ORDER BY priority, created_at`).all(),r=0;for(let o of s){let a=li(o);if(OT(a,n,t))try{xn(a.collection_id,e,null,{source:"auto",rule_id:a.id}).added&&(r+=1)}catch(c){console.error(`[auto-collections] failed to apply rule ${a.id} to session ${e}:`,c)}}return{added:r}}function ii(e){if(!e.enabled)return{added:0};let t=0;for(let n of xT(e))try{xn(e.collection_id,n,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(s){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${n}:`,s)}return{added:t}}function ui(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let n=(e.pattern??"").trim();if(!n)throw new Error("pattern required");let s=h(),r=new Date().toISOString(),o=ai(),a=e.collection_id;a||(a=AT(t,e.type,e.parent_collection_id)),s.prepare(`INSERT INTO auto_collection_rules
1305
+ ORDER BY priority, created_at`).all(),r=0;for(let o of s){let a=pi(o);if(PT(a,n,t))try{xn(a.collection_id,e,null,{source:"auto",rule_id:a.id}).added&&(r+=1)}catch(c){console.error(`[auto-collections] failed to apply rule ${a.id} to session ${e}:`,c)}}return{added:r}}function li(e){if(!e.enabled)return{added:0};let t=0;for(let n of DT(e))try{xn(e.collection_id,n,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(s){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${n}:`,s)}return{added:t}}function mi(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let n=(e.pattern??"").trim();if(!n)throw new Error("pattern required");let s=h(),r=new Date().toISOString(),o=ui(),a=e.collection_id;a||(a=MT(t,e.type,e.parent_collection_id)),s.prepare(`INSERT INTO auto_collection_rules
1306
1306
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
1307
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,n,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),s.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,n);let c=zs(o);return ii(c),Pt(),c}function Xu(e={}){let t=e.includeSeed?"SELECT * FROM auto_collection_rules ORDER BY priority, created_at":"SELECT * FROM auto_collection_rules WHERE created_by != 'seed' ORDER BY priority, created_at";return h().prepare(t).all().map(li)}function Ju(e,t){let n=h(),s=zs(e);if(!s)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():s.name,pattern:t.pattern!==void 0?t.pattern.trim():s.pattern,enabled:t.enabled!==void 0?t.enabled:s.enabled,priority:t.priority!==void 0?t.priority:s.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");n.prepare(`UPDATE auto_collection_rules
1307
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,n,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),s.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,n);let c=Ks(o);return li(c),Pt(),c}function Ku(e={}){let t=e.includeSeed?"SELECT * FROM auto_collection_rules ORDER BY priority, created_at":"SELECT * FROM auto_collection_rules WHERE created_by != 'seed' ORDER BY priority, created_at";return h().prepare(t).all().map(pi)}function Vu(e,t){let n=h(),s=Ks(e);if(!s)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():s.name,pattern:t.pattern!==void 0?t.pattern.trim():s.pattern,enabled:t.enabled!==void 0?t.enabled:s.enabled,priority:t.priority!==void 0?t.priority:s.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");n.prepare(`UPDATE auto_collection_rules
1308
1308
  SET name = ?, pattern = ?, enabled = ?, priority = ?
1309
- WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=zs(e);return t.pattern!==void 0&&t.pattern!==s.pattern?(Gs(e),o.enabled&&ii(o)):t.enabled!==void 0&&t.enabled!==s.enabled&&(o.enabled?ii(o):Gs(e)),Pt(),o}function Gu(e){let t=h();if(!zs(e))return{removed:0};let s=Gs(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),Pt(),s}function Vs(e={}){let t=e.includeDismissed?"SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC":"SELECT * FROM auto_collection_suggestions WHERE dismissed = 0 ORDER BY detected_at DESC";return h().prepare(t).all().map(wT)}function Yu(e){h().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),Pt()}function zu(e){let t=h(),n=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!n)throw new Error(`suggestion not found: ${e}`);if(n.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let s=ui({name:n.suggested_name,type:n.type,pattern:n.pattern,parent_collection_id:n.suggested_parent_collection_id===null?void 0:n.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),Pt(),s}function Qs(){let e=h(),t=new Date().toISOString(),n=fT(),s=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...LT(n,t).filter(c=>!s.has(c.pattern)),...CT(t),...vT(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),s.size>0){let c=Array.from(s).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(s))}let o=e.prepare("SELECT type, pattern FROM auto_collection_rules").all(),a=new Set(o.map(c=>`${c.type}:${c.pattern}`));for(let c of r){let u=`${c.type}:${c.pattern}`;if(a.has(u))continue;let d=e.prepare("SELECT id FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").get(c.type,c.pattern);d?e.prepare(`UPDATE auto_collection_suggestions
1309
+ WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=Ks(e);return t.pattern!==void 0&&t.pattern!==s.pattern?(Ys(e),o.enabled&&li(o)):t.enabled!==void 0&&t.enabled!==s.enabled&&(o.enabled?li(o):Ys(e)),Pt(),o}function Qu(e){let t=h();if(!Ks(e))return{removed:0};let s=Ys(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),Pt(),s}function Qs(e={}){let t=e.includeDismissed?"SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC":"SELECT * FROM auto_collection_suggestions WHERE dismissed = 0 ORDER BY detected_at DESC";return h().prepare(t).all().map(LT)}function Zu(e){h().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),Pt()}function ed(e){let t=h(),n=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!n)throw new Error(`suggestion not found: ${e}`);if(n.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let s=mi({name:n.suggested_name,type:n.type,pattern:n.pattern,parent_collection_id:n.suggested_parent_collection_id===null?void 0:n.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),Pt(),s}function Zs(){let e=h(),t=new Date().toISOString(),n=wT(),s=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...FT(n,t).filter(c=>!s.has(c.pattern)),...$T(t),...UT(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),s.size>0){let c=Array.from(s).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(s))}let o=e.prepare("SELECT type, pattern FROM auto_collection_rules").all(),a=new Set(o.map(c=>`${c.type}:${c.pattern}`));for(let c of r){let u=`${c.type}:${c.pattern}`;if(a.has(u))continue;let d=e.prepare("SELECT id FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").get(c.type,c.pattern);d?e.prepare(`UPDATE auto_collection_suggestions
1310
1310
  SET session_count = ?, detected_at = ?, suggested_name = ?, suggested_parent_collection_id = ?
1311
1311
  WHERE id = ?`).run(c.session_count,t,c.suggested_name,c.suggested_parent_collection_id,d.id):e.prepare(`INSERT INTO auto_collection_suggestions
1312
1312
  (id, type, pattern, suggested_name, suggested_parent_collection_id, session_count, detected_at, dismissed)
1313
- VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(ai(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return Pt(),Vs()}function LT(e,t){let s=h().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of s){let c=a.cwd.split("/").filter(Boolean),u="";for(let d of c){if(u=`${u}/${d}`,u===e||u==="/")continue;let m=r.get(u);m||(m=new Set,r.set(u,m)),m.add(a.id)}}let o=[];for(let[a,c]of r.entries()){if(c.size<Bu)continue;let u=!1;for(let[d,m]of r.entries())if(d!==a&&d.startsWith(a+"/")&&m.size>=Bu){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:_T(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function CT(e){return h().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(ST).map(n=>({type:"tag",pattern:n.tag,suggested_name:n.tag,suggested_parent_collection_id:null,session_count:n.n,detected_at:e,dismissed:!1}))}function vT(e){let t=h().prepare(`SELECT id, first_user_message FROM sessions
1314
- WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),n=new Map;for(let r of t)for(let o of yT){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=n.get(c);u||(u=new Set,n.set(c,u)),u.add(r.id)}}let s=[];for(let[r,o]of n.entries())o.size<TT||s.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return s}function Ks(e){return e.replace(/[\\%_]/g,"\\$&")}function Pt(){try{z(),mT(Ys)||gT(Ys,{recursive:!0});let e=h(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),n=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();Hu(hT,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),Hu(ET,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:n},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function Ku(){let e=h().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}var IT=/^[ \t]*<!--\s*claude-recall-alias\s*:\s*([^\n]+?)\s*-->[ \t]*\n?/;function di(e){if(e==null)return{alias:null,stripped:""};let t=e.match(IT);if(!t)return{alias:null,stripped:e};let n=t[1].trim();return n?{alias:n.length>200?n.slice(0,200):n,stripped:e.slice(t[0].length)}:{alias:null,stripped:e.slice(t[0].length)}}var Zs=new Map;function Vu(e,t){let s=(Zs.get(e)??Promise.resolve()).catch(()=>{}).then(t);return Zs.set(e,s),s.catch(()=>{}).finally(()=>{Zs.get(e)===s&&Zs.delete(e)}),s}import{realpathSync as MT,statSync as DT,readFileSync as jT}from"node:fs";import{dirname as Qu,join as PT,parse as FT}from"node:path";var Ft="(temporary sessions)";function On(e){if(!e)return!1;if(e.startsWith("/private/var/folders/")||e.startsWith("/var/folders/")||e.startsWith("/tmp/")||e.startsWith("/private/tmp/")||e==="/tmp"||e==="/private/tmp")return!0;let t=e.replace(/\\/g,"/").toLowerCase();return/^[a-z]:\/users\/[^/]+\/appdata\/local\/temp(\/|$)/.test(t)||/^[a-z]:\/windows\/temp(\/|$)/.test(t)||t.includes("/appdata/local/temp/")}function Zu(e,t){let n=e;for(;n!=="/"&&n!=="."&&n!=="";){if(t.has(n))return n;let s=Qu(n);if(s===n)break;n=s}return null}var Nn=new Map;function er(e,t={}){let n=t.foldWorktrees===!0,s=`${n?"1":"0"}:${e}`,r=Nn.get(s);if(r)return r;let o={root:e,isRepo:!1,isWorktree:!1,mainRepo:null},a;try{a=MT(e)}catch{return Nn.set(s,o),o}let{root:c}=FT(a),u=a;for(;;){let d=PT(u,".git"),m=null;try{m=DT(d)}catch{m=null}if(m&&m.isDirectory()){let f={root:u,isRepo:!0,isWorktree:!1,mainRepo:null};return Nn.set(s,f),f}if(m&&m.isFile()){let f=$T(d),S={root:n&&f?f:u,isRepo:!0,isWorktree:f!==null,mainRepo:f};return Nn.set(s,S),S}if(u===c)break;u=Qu(u)}return Nn.set(s,o),o}function $T(e){let t;try{t=jT(e,"utf8")}catch{return null}let n=t.match(/^gitdir:\s*(.+?)\s*$/m);if(!n)return null;let s=n[1].trim(),o=s.indexOf("/.git/worktrees/");return o<0?null:s.slice(0,o)}U();We();U();function ed(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function UT(e){let t=[],o=0;for(;o<e.length;){let a=[],c=0;for(;a.length<5&&o<e.length;){let d=e[o],m=ed(d.content_text??"");if(c+m.length>2e3&&a.length>=3)break;a.push(d),c+=m.length,o++}if(a.length===0)break;let u=a.map(d=>{let m=d.role??"system",f=ed(d.content_text??"");return`[${m}] ${f}`}).join(`
1313
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(ui(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return Pt(),Qs()}function FT(e,t){let s=h().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of s){let c=a.cwd.split("/").filter(Boolean),u="";for(let d of c){if(u=`${u}/${d}`,u===e||u==="/")continue;let m=r.get(u);m||(m=new Set,r.set(u,m)),m.add(a.id)}}let o=[];for(let[a,c]of r.entries()){if(c.size<Gu)continue;let u=!1;for(let[d,m]of r.entries())if(d!==a&&d.startsWith(a+"/")&&m.size>=Gu){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:RT(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function $T(e){return h().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(NT).map(n=>({type:"tag",pattern:n.tag,suggested_name:n.tag,suggested_parent_collection_id:null,session_count:n.n,detected_at:e,dismissed:!1}))}function UT(e){let t=h().prepare(`SELECT id, first_user_message FROM sessions
1314
+ WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),n=new Map;for(let r of t)for(let o of CT){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=n.get(c);u||(u=new Set,n.set(c,u)),u.add(r.id)}}let s=[];for(let[r,o]of n.entries())o.size<OT||s.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return s}function Vs(e){return e.replace(/[\\%_]/g,"\\$&")}function Pt(){try{z(),TT(zs)||yT(zs,{recursive:!0});let e=h(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),n=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();Ju(kT,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),Ju(AT,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:n},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function td(){let e=h().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}var BT=/^[ \t]*<!--\s*claude-recall-alias\s*:\s*([^\n]+?)\s*-->[ \t]*\n?/;function gi(e){if(e==null)return{alias:null,stripped:""};let t=e.match(BT);if(!t)return{alias:null,stripped:e};let n=t[1].trim();return n?{alias:n.length>200?n.slice(0,200):n,stripped:e.slice(t[0].length)}:{alias:null,stripped:e.slice(t[0].length)}}var er=new Map;function nd(e,t){let s=(er.get(e)??Promise.resolve()).catch(()=>{}).then(t);return er.set(e,s),s.catch(()=>{}).finally(()=>{er.get(e)===s&&er.delete(e)}),s}import{realpathSync as HT,statSync as WT,readFileSync as qT}from"node:fs";import{dirname as sd,join as XT,parse as JT}from"node:path";var Ft="(temporary sessions)";function On(e){if(!e)return!1;if(e.startsWith("/private/var/folders/")||e.startsWith("/var/folders/")||e.startsWith("/tmp/")||e.startsWith("/private/tmp/")||e==="/tmp"||e==="/private/tmp")return!0;let t=e.replace(/\\/g,"/").toLowerCase();return/^[a-z]:\/users\/[^/]+\/appdata\/local\/temp(\/|$)/.test(t)||/^[a-z]:\/windows\/temp(\/|$)/.test(t)||t.includes("/appdata/local/temp/")}function rd(e,t){let n=e;for(;n!=="/"&&n!=="."&&n!=="";){if(t.has(n))return n;let s=sd(n);if(s===n)break;n=s}return null}var Nn=new Map;function tr(e,t={}){let n=t.foldWorktrees===!0,s=`${n?"1":"0"}:${e}`,r=Nn.get(s);if(r)return r;let o={root:e,isRepo:!1,isWorktree:!1,mainRepo:null},a;try{a=HT(e)}catch{return Nn.set(s,o),o}let{root:c}=JT(a),u=a;for(;;){let d=XT(u,".git"),m=null;try{m=WT(d)}catch{m=null}if(m&&m.isDirectory()){let f={root:u,isRepo:!0,isWorktree:!1,mainRepo:null};return Nn.set(s,f),f}if(m&&m.isFile()){let f=GT(d),S={root:n&&f?f:u,isRepo:!0,isWorktree:f!==null,mainRepo:f};return Nn.set(s,S),S}if(u===c)break;u=sd(u)}return Nn.set(s,o),o}function GT(e){let t;try{t=qT(e,"utf8")}catch{return null}let n=t.match(/^gitdir:\s*(.+?)\s*$/m);if(!n)return null;let s=n[1].trim(),o=s.indexOf("/.git/worktrees/");return o<0?null:s.slice(0,o)}U();We();U();function od(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function YT(e){let t=[],o=0;for(;o<e.length;){let a=[],c=0;for(;a.length<5&&o<e.length;){let d=e[o],m=od(d.content_text??"");if(c+m.length>2e3&&a.length>=3)break;a.push(d),c+=m.length,o++}if(a.length===0)break;let u=a.map(d=>{let m=d.role??"system",f=od(d.content_text??"");return`[${m}] ${f}`}).join(`
1315
1315
 
1316
- `);t.push({messageUuids:a.map(d=>d.uuid),text:u}),o<e.length&&a.length>=3&&(o=Math.max(o-1,o-1))}return t}function td(e){let n=h().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return UT(n)}U();function pi(e){let t=h();t.transaction(()=>{t.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()&&t.prepare("DELETE FROM vec_chunks_v1_backup WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(e)})()}function fi(){try{return!!h().prepare("SELECT 1 AS held FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: migration_state"))return!1;throw e}}function cd(){return h().prepare("UPDATE chunk_queue SET action = 'embed' WHERE action = 'pending_post_migration'").run().changes}function HT(){try{return!!h().prepare("SELECT 1 AS present FROM chunk_queue WHERE action = 'pending_post_migration' LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: chunk_queue"))return!1;throw e}}var ld=2e3,BT=1e4,Et=null,tr=!1,_i=null,mi=0;function ud(e){mi=Math.max(0,Math.floor(e))}var Ie=new Set,dd=-1;function pd(e){Ie.add(e)}function md(){Ie.add(dd)}function nd(e){if(Ie.has(dd))return!0;if(Ie.size===0)return!1;let t=h().prepare("SELECT project_id FROM sessions WHERE id = ?").get(e);return t?Ie.has(t.project_id):!1}var sd=10,WT=200,qT=2e3;function XT(){let e=Number(process.env.RECALL_VECTOR_HARD_CAP??"");return Number.isFinite(e)&&e>0?Math.min(qT,Math.floor(e)):WT}var JT=3,rd=1e3,Ln=new Map,gi=new Set;function od(e,t,n,s){if(e.size>=rd&&(e instanceof Map,!e.has(t))){let r=e.keys().next();r.done||(e.delete(r.value),console.warn(`[vector-worker] ${s??"tracking-map"} hit ${rd}-entry cap; evicted oldest entry`))}e instanceof Map?e.set(t,n):e.add(t)}var Cn=[],GT=50,id=.3,$t=[],YT=50,ad=!1,vn=null,zT=0;function KT(e){if(!ad){ad=!0;return}e<=0||(Cn.push({ts:Date.now(),durationMs:e}),Cn.length>GT&&Cn.shift(),vn=vn===null?e:id*e+(1-id)*vn,zT+=1)}function VT(e){e<=0||($t.push({ts:Date.now(),chunks:e}),$t.length>YT&&$t.shift())}function hi(){if(Cn.length<5||vn===null)return null;let e=1e3/vn,t=$t.length>0?$t.reduce((n,s)=>n+s.chunks,0)/$t.length:30;return{samples:Cn.length,chunksPerSec:e,sessionsPerSec:e/Math.max(1,t),avgChunksPerSession:t,lastProcessedAt:_i}}function QT(){return h().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function ZT(){if(fi())return!1;let e=h(),t=e.prepare("SELECT DISTINCT session_id FROM chunk_queue ORDER BY id LIMIT 1").get();if(!t)return Ie.size>0&&Ie.clear(),!1;if(nd(t.session_id)||gi.has(t.session_id))return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(t.session_id),!0;let n=t.session_id;try{pi(n);let s=td(n),r=mi>0?mi:1/0,o=Math.min(r,XT()),a=o<s.length?s.slice(0,o):s;if(a.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),!0;let c=le().modelId,u=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, ?, 768, 0, datetime('now'))"),d=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)"),m=0,f=!1;for(let b=0;b<a.length;b+=sd){if(nd(n)){f=!0;break}let T=Math.min(a.length,b+sd),S=a.slice(b,T),w=S.map(P=>P.text),A=Date.now(),v=await Ve(w),D=Date.now()-A;e.transaction(()=>{for(let P=0;P<S.length;P++){let te=u.run(n,JSON.stringify(S[P].messageUuids),S[P].text,c),$=Buffer.from(v[P].buffer,v[P].byteOffset,v[P].byteLength);d.run(BigInt(te.lastInsertRowid),$)}})();let I=S.length>0?D/S.length:0;for(let P=0;P<S.length;P++)KT(I);m+=S.length,await new Promise(P=>setImmediate(P))}f&&pi(n),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),_i=new Date().toISOString(),VT(m),Ln.has(n)&&Ln.delete(n)}catch(s){console.error("[vector-worker] failed for session",n,s);let r=(Ln.get(n)??0)+1;od(Ln,n,r,"failureCounts"),r>=JT&&(od(gi,n,void 0,"blacklist"),Ln.delete(n),console.error(`[vector-worker] blacklisted session ${n} after ${r} failures \u2014 will not retry until daemon restart`)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n)}finally{if(Ie.size>0)try{let s=e.prepare("SELECT project_id FROM sessions WHERE id = ?").get(n);s!==void 0&&Ie.has(s.project_id)&&(e.prepare("SELECT 1 FROM chunk_queue cq JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ? LIMIT 1").get(s.project_id)||Ie.delete(s.project_id))}catch(s){console.error("[vector-worker] cancelledProjects cleanup failed:",s)}}return!0}async function gd(){if(!le().loaded)return;HT()&&cd();let e=await ZT();Et!==null&&clearTimeout(Et),Et=setTimeout(()=>{gd()},e?ld:BT)}function In(){if(!tr){if(!le().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}tr=!0,Et=setTimeout(()=>{gd()},ld)}}function fd(){tr=!1,Et!==null&&(clearTimeout(Et),Et=null)}function Me(){return{running:tr,queueDepth:QT(),lastProcessedAt:_i,blacklistedCount:gi.size,pausedForMigration:fi()}}var De=new Map;function _d(e,t,n=Date.now()){let s=De.get(e);if(s?(s.count+=1,t>s.lastSize?s.noProgressCount=0:s.noProgressCount+=1,s.lastSize=t):De.set(e,{count:1,noProgressCount:0,firstSeenAt:n,lastSize:t}),De.size>1e4&&(hd(n),De.size>1e4)){let r=[...De.entries()].sort((a,c)=>a[1].firstSeenAt-c[1].firstSeenAt),o=De.size-1e4;for(let a=0;a<o;a+=1){let c=r[a];c&&De.delete(c[0])}}}function hd(e){let t=e-36e5;for(let[n,s]of De)s.firstSeenAt<t&&De.delete(n)}function nr(e=10,t=Date.now()){hd(t);let n=[];for(let[s,r]of De)n.push({path:s,count:r.count,noProgressCount:r.noProgressCount,firstSeenAt:r.firstSeenAt});return n.sort((s,r)=>r.noProgressCount-s.noProgressCount||r.count-s.count||s.path.localeCompare(r.path)),n.slice(0,e)}function Ed(e,t,n=1500,s=8e3){return t===void 0?n:Math.max(n,t+s-e)}function Ei(e){return e.replace(/\\/g,"/").includes("/subagents/")}function bi(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}function Si(e){let{filePath:t,existing:n,currentMtime:s}=e;return Ei(t)?{kind:"skip-durable",reason:"subagent_path"}:bi(t)?s===null?{kind:"skip-transient",reason:"file_vanished"}:n?.skipped_reason?{kind:"skip-already-marked"}:n&&n.file_mtime>=s?{kind:"skip-transient",reason:"unchanged_mtime"}:{kind:"process"}:{kind:"skip-durable",reason:"no_encoded_project"}}function sr(e,t,n,s={}){switch(n.kind){case"process":case"skip-transient":case"skip-already-marked":return;case"skip-durable":{ey(e,t,n.reason,n.detail,s);return}default:{let r=n;return}}}function ey(e,t,n,s,r){switch(n){case"subagent_path":return;case"no_encoded_project":{console.warn(`[watcher] cannot persist marker for ${t} \u2014 no encoded-project segment`);return}case"daemon_spawn_phantom":return;case"parse_failed":return;default:{let o=n;return}}}var Ti=new Map,Td=new Map;var yi=bi,or=Ei;function ry(e){let s=h().prepare("SELECT COUNT(*) AS n FROM sessions WHERE file_path = ?").get(e).n;console.log(`[watcher] indexed ${rr(e)} (${s} session${s===1?"":"s"})`)}function oy(e,t){let n=rr(e).replace(/\.jsonl$/,"");if(!n)return;let s=yi(e);if(!s)return;let o=`parse_failed:${t instanceof Error&&t.name||"Error"}`,a=h(),c=a.prepare("SELECT id, skipped_reason FROM sessions WHERE id = ? LIMIT 1").get(n);if(c?.skipped_reason)return;let u=0;try{u=Ut(e).mtimeMs}catch{}if(c){a.prepare("UPDATE sessions SET skipped_reason = ? WHERE id = ?").run(o,n);return}if(u===0)return;let d=s.replace(/^-/,"/").replace(/-/g,"/"),m=rr(d)||s,{id:f}=a.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1316
+ `);t.push({messageUuids:a.map(d=>d.uuid),text:u}),o<e.length&&a.length>=3&&(o=Math.max(o-1,o-1))}return t}function id(e){let n=h().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return YT(n)}U();function fi(e){let t=h();t.transaction(()=>{t.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()&&t.prepare("DELETE FROM vec_chunks_v1_backup WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(e)})()}function Ei(){try{return!!h().prepare("SELECT 1 AS held FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: migration_state"))return!1;throw e}}function md(){return h().prepare("UPDATE chunk_queue SET action = 'embed' WHERE action = 'pending_post_migration'").run().changes}function zT(){try{return!!h().prepare("SELECT 1 AS present FROM chunk_queue WHERE action = 'pending_post_migration' LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: chunk_queue"))return!1;throw e}}var gd=2e3,KT=1e4,Et=null,nr=!1,bi=null,_i=0;function fd(e){_i=Math.max(0,Math.floor(e))}var Ie=new Set,_d=-1;function hd(e){Ie.add(e)}function Ed(){Ie.add(_d)}function ad(e){if(Ie.has(_d))return!0;if(Ie.size===0)return!1;let t=h().prepare("SELECT project_id FROM sessions WHERE id = ?").get(e);return t?Ie.has(t.project_id):!1}var cd=10,VT=200,QT=2e3;function ZT(){let e=Number(process.env.RECALL_VECTOR_HARD_CAP??"");return Number.isFinite(e)&&e>0?Math.min(QT,Math.floor(e)):VT}var ey=3,ld=1e3,Cn=new Map,hi=new Set;function ud(e,t,n,s){if(e.size>=ld&&(e instanceof Map,!e.has(t))){let r=e.keys().next();r.done||(e.delete(r.value),console.warn(`[vector-worker] ${s??"tracking-map"} hit ${ld}-entry cap; evicted oldest entry`))}e instanceof Map?e.set(t,n):e.add(t)}var Ln=[],ty=50,dd=.3,$t=[],ny=50,pd=!1,vn=null,sy=0;function ry(e){if(!pd){pd=!0;return}e<=0||(Ln.push({ts:Date.now(),durationMs:e}),Ln.length>ty&&Ln.shift(),vn=vn===null?e:dd*e+(1-dd)*vn,sy+=1)}function oy(e){e<=0||($t.push({ts:Date.now(),chunks:e}),$t.length>ny&&$t.shift())}function Si(){if(Ln.length<5||vn===null)return null;let e=1e3/vn,t=$t.length>0?$t.reduce((n,s)=>n+s.chunks,0)/$t.length:30;return{samples:Ln.length,chunksPerSec:e,sessionsPerSec:e/Math.max(1,t),avgChunksPerSession:t,lastProcessedAt:bi}}function iy(){return h().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function ay(){if(Ei())return!1;let e=h(),t=e.prepare("SELECT DISTINCT session_id FROM chunk_queue ORDER BY id LIMIT 1").get();if(!t)return Ie.size>0&&Ie.clear(),!1;if(ad(t.session_id)||hi.has(t.session_id))return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(t.session_id),!0;let n=t.session_id;try{fi(n);let s=id(n),r=_i>0?_i:1/0,o=Math.min(r,ZT()),a=o<s.length?s.slice(0,o):s;if(a.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),!0;let c=le().modelId,u=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, ?, 768, 0, datetime('now'))"),d=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)"),m=0,f=!1;for(let b=0;b<a.length;b+=cd){if(ad(n)){f=!0;break}let T=Math.min(a.length,b+cd),S=a.slice(b,T),R=S.map(P=>P.text),A=Date.now(),v=await Ve(R),D=Date.now()-A;e.transaction(()=>{for(let P=0;P<S.length;P++){let te=u.run(n,JSON.stringify(S[P].messageUuids),S[P].text,c),$=Buffer.from(v[P].buffer,v[P].byteOffset,v[P].byteLength);d.run(BigInt(te.lastInsertRowid),$)}})();let I=S.length>0?D/S.length:0;for(let P=0;P<S.length;P++)ry(I);m+=S.length,await new Promise(P=>setImmediate(P))}f&&fi(n),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),bi=new Date().toISOString(),oy(m),Cn.has(n)&&Cn.delete(n)}catch(s){console.error("[vector-worker] failed for session",n,s);let r=(Cn.get(n)??0)+1;ud(Cn,n,r,"failureCounts"),r>=ey&&(ud(hi,n,void 0,"blacklist"),Cn.delete(n),console.error(`[vector-worker] blacklisted session ${n} after ${r} failures \u2014 will not retry until daemon restart`)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n)}finally{if(Ie.size>0)try{let s=e.prepare("SELECT project_id FROM sessions WHERE id = ?").get(n);s!==void 0&&Ie.has(s.project_id)&&(e.prepare("SELECT 1 FROM chunk_queue cq JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ? LIMIT 1").get(s.project_id)||Ie.delete(s.project_id))}catch(s){console.error("[vector-worker] cancelledProjects cleanup failed:",s)}}return!0}async function bd(){if(!le().loaded)return;zT()&&md();let e=await ay();Et!==null&&clearTimeout(Et),Et=setTimeout(()=>{bd()},e?gd:KT)}function In(){if(!nr){if(!le().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}nr=!0,Et=setTimeout(()=>{bd()},gd)}}function Sd(){nr=!1,Et!==null&&(clearTimeout(Et),Et=null)}function Me(){return{running:nr,queueDepth:iy(),lastProcessedAt:bi,blacklistedCount:hi.size,pausedForMigration:Ei()}}var De=new Map;function Td(e,t,n=Date.now()){let s=De.get(e);if(s?(s.count+=1,t>s.lastSize?s.noProgressCount=0:s.noProgressCount+=1,s.lastSize=t):De.set(e,{count:1,noProgressCount:0,firstSeenAt:n,lastSize:t}),De.size>1e4&&(yd(n),De.size>1e4)){let r=[...De.entries()].sort((a,c)=>a[1].firstSeenAt-c[1].firstSeenAt),o=De.size-1e4;for(let a=0;a<o;a+=1){let c=r[a];c&&De.delete(c[0])}}}function yd(e){let t=e-36e5;for(let[n,s]of De)s.firstSeenAt<t&&De.delete(n)}function sr(e=10,t=Date.now()){yd(t);let n=[];for(let[s,r]of De)n.push({path:s,count:r.count,noProgressCount:r.noProgressCount,firstSeenAt:r.firstSeenAt});return n.sort((s,r)=>r.noProgressCount-s.noProgressCount||r.count-s.count||s.path.localeCompare(r.path)),n.slice(0,e)}function wd(e,t,n=1500,s=8e3){return t===void 0?n:Math.max(n,t+s-e)}function Ti(e){return e.replace(/\\/g,"/").includes("/subagents/")}function yi(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}function wi(e){let{filePath:t,existing:n,currentMtime:s}=e;return Ti(t)?{kind:"skip-durable",reason:"subagent_path"}:yi(t)?s===null?{kind:"skip-transient",reason:"file_vanished"}:n?.skipped_reason?{kind:"skip-already-marked"}:n&&n.file_mtime>=s?{kind:"skip-transient",reason:"unchanged_mtime"}:{kind:"process"}:{kind:"skip-durable",reason:"no_encoded_project"}}function rr(e,t,n,s={}){switch(n.kind){case"process":case"skip-transient":case"skip-already-marked":return;case"skip-durable":{cy(e,t,n.reason,n.detail,s);return}default:{let r=n;return}}}function cy(e,t,n,s,r){switch(n){case"subagent_path":return;case"no_encoded_project":{console.warn(`[watcher] cannot persist marker for ${t} \u2014 no encoded-project segment`);return}case"daemon_spawn_phantom":return;case"parse_failed":return;default:{let o=n;return}}}var Ri=new Map,Nd=new Map,Rd=9e4,ir=new Map,kd=new Map,py=3e4;function ki(e,t){let n=kd.get(e);return n!==void 0&&t-n<py?!1:(kd.set(e,t),!0)}function Ad(e,t,n){let s=Date.now();ir.set(e,s+Rd),ki(e,s)&&console.error(`[watcher] ${t} write still busy after retries for ${e}; deferring reindex ~${Math.round(Rd/1e3)}s (file stays indexed-pending, not skipped): ${n instanceof Error?n.message:String(n)}`),Ai(e)}var xi=yi,ar=Ti;function my(e){let s=h().prepare("SELECT COUNT(*) AS n FROM sessions WHERE file_path = ?").get(e).n;console.log(`[watcher] indexed ${or(e)} (${s} session${s===1?"":"s"})`)}function gy(e,t){let n=or(e).replace(/\.jsonl$/,"");if(!n)return;let s=xi(e);if(!s)return;let o=`parse_failed:${t instanceof Error&&t.name||"Error"}`,a=h(),c=a.prepare("SELECT id, skipped_reason FROM sessions WHERE id = ? LIMIT 1").get(n);if(c?.skipped_reason)return;let u=0;try{u=Ut(e).mtimeMs}catch{}if(c){a.prepare("UPDATE sessions SET skipped_reason = ? WHERE id = ?").run(o,n);return}if(u===0)return;let d=s.replace(/^-/,"/").replace(/-/g,"/"),m=or(d)||s,{id:f}=a.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1317
1317
  VALUES (?, ?, ?, NULL, NULL, 0, 0)
1318
1318
  ON CONFLICT(encoded_path) DO UPDATE SET decoded_path = excluded.decoded_path
1319
1319
  RETURNING id`).get(s,d,m);a.prepare(`INSERT INTO sessions (
@@ -1322,7 +1322,7 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1322
1322
  user_message_count, assistant_message_count,
1323
1323
  first_user_message, cwd, git_branch, version, indexed_at,
1324
1324
  skipped_reason
1325
- ) VALUES (?, ?, ?, ?, NULL, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, ?, ?)`).run(n,f,e,u,new Date().toISOString(),o)}async function iy(e){let t=null;try{t=Ut(e).mtimeMs}catch{}let n=h(),s=n.prepare("SELECT id, file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1").get(e),r=Si({filePath:e,existing:s?{file_mtime:s.file_mtime,skipped_reason:s.skipped_reason}:null,currentMtime:t});if(r.kind!=="process"){sr(n,e,r);return}let o=yi(e);if(!o)return;let a=t,c=Ut(e,{bigint:!0}),u=Yl(n,e),d=u?vs(e,u.byteOffset)===u.prefixHash:!1,m=Xl(u,{size:Number(c.size),inode:Number(c.ino)},d),f=m.mode==="incremental"?m.start:0,{entries:b,endOffset:T}=await ql(e,f);if(m.mode==="incremental"&&b.length===0&&T===f){n.prepare("UPDATE sessions SET file_mtime = ? WHERE file_path = ?").run(a,e);let x=n.prepare("SELECT line_count FROM file_cursor WHERE file_path = ? LIMIT 1").get(e)?.line_count??0;Wo(n,e,{byteOffset:T,sizeBytes:Number(c.size),inode:Number(c.ino),prefixHash:u?.prefixHash??vs(e,T),mtime:a,lineCount:x});return}let S=new Map,w=null;for(let x of b){let k=S.get(x.sessionId);if(k||(k={sessionId:x.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},S.set(x.sessionId,k)),k.entries.push(x),x.timestamp&&((!k.earliest||x.timestamp<k.earliest)&&(k.earliest=x.timestamp),(!k.latest||x.timestamp>k.latest)&&(k.latest=x.timestamp)),x.role==="user"&&!x.isSidechain){if(k.users+=1,!k.firstUser&&x.contentText){let L=x.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();L&&!/^<local-command-caveat>/.test(L)&&(k.firstUser=be(L).redacted.slice(0,2e3))}}else x.role==="assistant"&&!x.isSidechain&&(k.assistants+=1);!k.cwd&&x.cwd&&(k.cwd=x.cwd),!k.branch&&x.gitBranch&&(k.branch=x.gitBranch),!k.version&&x.version&&(k.version=x.version),!w&&x.cwd&&(w=x.cwd)}let A=w!==null,v=w?rr(w)||w:o,D=w??o.replace(/^-/,"/").replace(/-/g,"/"),X=null,I=null,P=0,te=0;if(On(D))X=Ft;else{let x=er(D);x.isRepo&&(X=x.root,I=x.mainRepo,P=1,te=x.isWorktree?1:0)}let $=n.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1325
+ ) VALUES (?, ?, ?, ?, NULL, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, ?, ?)`).run(n,f,e,u,new Date().toISOString(),o)}async function fy(e){let t=null;try{t=Ut(e).mtimeMs}catch{}let n=h(),s=n.prepare("SELECT id, file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1").get(e),r=wi({filePath:e,existing:s?{file_mtime:s.file_mtime,skipped_reason:s.skipped_reason}:null,currentMtime:t});if(r.kind!=="process"){rr(n,e,r);return}let o=xi(e);if(!o)return;let a=t,c=Ut(e,{bigint:!0}),u=Zl(n,e),d=u?vs(e,u.byteOffset)===u.prefixHash:!1,m=Kl(u,{size:Number(c.size),inode:Number(c.ino)},d),f=m.mode==="incremental"?m.start:0,{entries:b,endOffset:T}=await zl(e,f);if(m.mode==="incremental"&&b.length===0&&T===f){let x=n.prepare("SELECT line_count FROM file_cursor WHERE file_path = ? LIMIT 1").get(e)?.line_count??0;try{Jo(()=>{n.prepare("UPDATE sessions SET file_mtime = ? WHERE file_path = ?").run(a,e),Xo(n,e,{byteOffset:T,sizeBytes:Number(c.size),inode:Number(c.ino),prefixHash:u?.prefixHash??vs(e,T),mtime:a,lineCount:x})},{onRetry:(w,C)=>{ki(e,Date.now())&&console.error(`[watcher] empty-tail mtime write busy (attempt ${w}) for ${e}, retrying: ${C instanceof Error?C.message:String(C)}`)}})}catch(w){if(!Is(w))throw w;Ad(e,"empty-tail",w);return}return}let S=new Map,R=null;for(let x of b){let w=S.get(x.sessionId);if(w||(w={sessionId:x.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},S.set(x.sessionId,w)),w.entries.push(x),x.timestamp&&((!w.earliest||x.timestamp<w.earliest)&&(w.earliest=x.timestamp),(!w.latest||x.timestamp>w.latest)&&(w.latest=x.timestamp)),x.role==="user"&&!x.isSidechain){if(w.users+=1,!w.firstUser&&x.contentText){let C=x.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();C&&!/^<local-command-caveat>/.test(C)&&(w.firstUser=be(C).redacted.slice(0,2e3))}}else x.role==="assistant"&&!x.isSidechain&&(w.assistants+=1);!w.cwd&&x.cwd&&(w.cwd=x.cwd),!w.branch&&x.gitBranch&&(w.branch=x.gitBranch),!w.version&&x.version&&(w.version=x.version),!R&&x.cwd&&(R=x.cwd)}let A=R!==null,v=R?or(R)||R:o,D=R??o.replace(/^-/,"/").replace(/-/g,"/"),X=null,I=null,P=0,te=0;if(On(D))X=Ft;else{let x=tr(D);x.isRepo&&(X=x.root,I=x.mainRepo,P=1,te=x.isWorktree?1:0)}let $=n.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1326
1326
  VALUES (@encoded_path, @decoded_path, @name, @repo_root, @main_repo, @is_repo, @is_worktree)
1327
1327
  ON CONFLICT(encoded_path) DO UPDATE SET
1328
1328
  decoded_path = CASE WHEN @have_cwd = 1 THEN excluded.decoded_path ELSE projects.decoded_path END,
@@ -1383,17 +1383,17 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1383
1383
  file_mtime = @file_mtime,
1384
1384
  indexed_at = @indexed_at
1385
1385
  WHERE id = @id
1386
- `),E=new Date().toISOString(),y=new Set,R=new Set;n.transaction(()=>{let{id:x}=$.get({encoded_path:o,decoded_path:D,name:v,repo_root:X,main_repo:I,is_repo:P,is_worktree:te,have_cwd:A?1:0});for(let k of S.values()){if(fu(k.firstUser)){console.log(`[watcher] skipping daemon-spawn phantom ${k.sessionId} (first message matches autonomous-spawn pattern)`),l.run(k.sessionId),p.run({id:k.sessionId,project_id:x,file_path:e,file_mtime:a,started_at:k.earliest,ended_at:k.latest,cwd:k.cwd,indexed_at:E}),sr(n,e,{kind:"skip-durable",reason:"daemon_spawn_phantom"}),y.add(k.sessionId);continue}if(m.mode==="incremental"&&g.get(k.sessionId)){R.add(k.sessionId);for(let W of k.entries)i.run(bd(W));_.run({id:k.sessionId,file_mtime:a,indexed_at:E}),Yo(n,k.sessionId,k.entries,{insertOnly:!0}),wn(n,k.sessionId);continue}let L=di(k.firstUser),F=L.alias?L.stripped:k.firstUser;if(ne.run({id:k.sessionId,project_id:x,file_path:e,file_mtime:a,started_at:k.earliest,ended_at:k.latest,message_count:k.entries.length,user_message_count:k.users,assistant_message_count:k.assistants,first_user_message:F,cwd:k.cwd,git_branch:k.branch,version:k.version,indexed_at:E}),L.alias&&!Se(k.sessionId))try{_e(k.sessionId,L.alias)}catch(W){console.error(`[watcher] header-alias setAlias failed for ${k.sessionId}:`,W)}l.run(k.sessionId);for(let W of k.entries)i.run(bd(W));Yo(n,k.sessionId,k.entries),wn(n,k.sessionId)}})();let N=vs(e,T),C=n.prepare("SELECT COUNT(*) AS n FROM messages m JOIN sessions s ON s.id = m.session_id WHERE s.file_path = ?").get(e);if(Wo(n,e,{byteOffset:T,sizeBytes:Number(c.size),inode:Number(c.ino),prefixHash:N,mtime:a,lineCount:C.n}),_t().heuristicEnabled)for(let x of S.values()){if(y.has(x.sessionId)||R.has(x.sessionId))continue;let k=di(x.firstUser).stripped,L=jt(k);L&&he(x.sessionId,L,"heuristic")}}function yd(e){return Td.set(e,Date.now()),Vu(e,()=>iy(e)).finally(()=>{let t=0;try{t=Ut(e).size}catch{}_d(e,t)})}function bd(e){let t=be(e.contentText).redacted,n=be(e.raw).redacted;return{uuid:e.uuid,session_id:e.sessionId,parent_uuid:e.parentUuid,type:e.type,role:e.role,timestamp:e.timestamp,is_sidechain:e.isSidechain?1:0,content_text:t,tool_names:e.toolNames.join(","),raw_json:n}}function Sd(e){let t=Ti.get(e);t?.timer&&clearTimeout(t.timer);let n=Ed(Date.now(),Td.get(e)),s={timer:null};s.timer=setTimeout(()=>{Ti.delete(e),yd(e).then(async()=>{ry(e),js(e);try{let o=h().prepare("SELECT id FROM sessions WHERE file_path = ? AND skipped_reason IS NULL").all(e);for(let a of o){pl(a.id),uu(a.id);try{qu(a.id)}catch(c){console.error("[watcher] auto-collections apply failed:",c)}}}catch(r){console.error("[watcher] semantic dispatch failed:",r)}}).catch(r=>{console.error(`[watcher] reindex failed for ${e}:`,r)})},n),Ti.set(e,s)}function ay(e){if(e.endsWith(".jsonl"))return or(e);try{if(Ut(e).isDirectory())return!1}catch{}return!0}function wd(){let e=ty(an,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:ay});return e.on("add",t=>t.endsWith(".jsonl")&&!or(t)&&Sd(t)),e.on("change",t=>t.endsWith(".jsonl")&&!or(t)&&Sd(t)),e.on("ready",()=>{console.log(`[watcher] ready, watching ${an}`)}),e.on("error",t=>{console.error("[watcher] chokidar error:",t)}),process.env.RECALL_WATCHER_DEBUG==="1"&&(e.on("add",t=>console.log(`[watcher.debug] add: ${t}`)),e.on("change",t=>console.log(`[watcher.debug] change: ${t}`)),e.on("unlink",t=>console.log(`[watcher.debug] unlink: ${t}`))),e}var cy=4;function*wi(e){let t;try{t=ny(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=sy(e,n.name);n.isDirectory()?yield*wi(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}async function Ri(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},n=h(),s=[],r=n.prepare("SELECT file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1");for(let d of wi(an)){t.scanned+=1;let m;try{m=Ut(d).mtimeMs}catch{m=null}let f=r.get(d),b=Si({filePath:d,existing:f??null,currentMtime:m});switch(b.kind){case"skip-durable":sr(n,d,b),t.skipped+=1;continue;case"skip-already-marked":t.skipped+=1;continue;case"skip-transient":b.reason==="unchanged_mtime"?t.upToDate+=1:t.errors+=1;continue;case"process":s.push(d);continue}}if(s.length===0)return t.durationMs=Date.now()-e,t;let o=s.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await yd(d),t.reindexed+=1}catch(m){t.errors+=1;let f=m instanceof Error?m.message:String(m);try{oy(d,m)}catch(b){console.error(`[ingestion-sweep] marker write failed for ${d}:`,b)}console.error(`[ingestion-sweep] failed for ${d}: ${f}`)}}},c=Math.min(cy,s.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}async function Rd(){try{let e=await Ri();e.reindexed>0&&console.log(`[safety-sweep] reindexed ${e.reindexed} file(s) the watcher missed (scanned=${e.scanned}, upToDate=${e.upToDate})`)}catch(e){console.error("[safety-sweep] failed:",e)}}import{execFileSync as Nd}from"node:child_process";var ly=["dist/mcp-server.js","dist/mcp/server.js"];function uy(e){for(let t of ly)if(e.includes(t))return!0;return!1}function Ht(e={}){let t=e.psOutput??dy(),n=e.isProcessAlive??py,s=e.getParentCommand??my,r=[];for(let o of t.split(`
1387
- `)){let a=o.trim();if(!a||!uy(a)||gy(a))continue;let c=a.split(/\s+/);if(c.length<5)continue;let u=Number(c[0]),d=Number(c[1]),m=c[2],f=c[3],b=c[4],T=0,S=0;if(/^\d+$/.test(f)&&(b.includes(".")||/^\d+$/.test(b))?(T=Number(f),S=Number(b)):S=Number(f),!Number.isFinite(u)||!Number.isFinite(d))continue;let A=d>1&&n(d);r.push({pid:u,ppid:d,parentAlive:A,etimeSeconds:fy(m),pcpu:Number.isFinite(S)?S:0,rssKb:Number.isFinite(T)?T:0,orphan:!A,parentCommand:A?s(d):null})}return r}function dy(){try{return Nd("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -axo failed: ${t}
1388
- `),""}}function py(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function my(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=Nd("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return n.length?n.slice(0,200):null}catch{return null}}function gy(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=[t[5]??"",t[4]??""];for(let s of n)if(s&&(s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"))return!0;return!1}function fy(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=kd(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(kd),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function kd(e){let t=Number(e);return Number.isFinite(t)?t:0}var _y=50,hy=60;function ki(e){return e.pcpu>=_y&&e.etimeSeconds>=hy}var Ad=4,Ey=1024*1024;function xd(e){return e.reduce((t,n)=>t+(Number.isFinite(n.rssKb)&&n.rssKb>0?n.rssKb:0),0)}function Ai(e){let t=e??Ht(),n=t.length,s=xd(t),r=t.filter(b=>b.orphan),o=r.length,a=xd(r),c=o>Ad,u=a>Ey;if(!(c||u))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,orphanCount:o,orphanRssKb:a,message:null};let m=[];c&&m.push(`${o} orphaned MCP children (threshold ${Ad})`),u&&m.push(`${by(a)} aggregate RSS across orphaned children (threshold 1 GiB)`);let f=`Zombie MCP threshold breached: ${m.join(" + ")}. Each orphaned MCP child (its parent claude/VS Code tab has exited) still holds a SQLite read connection and can pin WAL checkpoints. Run \`recall mcp-prune --all\` to reap (note: pre-2026-05-23 builds may miss processes from stale install paths -- \`pkill -f mcp-server.js\` is the nuclear option).`;return{flagged:!0,severity:"high",count:n,aggregateRssKb:s,orphanCount:o,orphanRssKb:a,message:f}}function by(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}import{execFileSync as Od}from"node:child_process";var Sy=["dist/daemon/entrypoint.js"];function Ty(e){let t=e.replace(/\\/g,"/");for(let n of Sy)if(t.includes(n))return!0;return!1}function yy(e){if(e.length<5)return!1;let t=[e[5]??"",e[4]??""];for(let n of t)if(n&&(n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"))return!0;return!1}function xi(e={}){let t=e.psOutput??wy(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
1389
- `)){let o=r.trim();if(!o||!Ty(o))continue;let a=o.split(/\s+/);if(yy(a)||a.length<5)continue;let c=Number(a[0]),u=Number(a[1]),d=a[2];!Number.isFinite(c)||!Number.isFinite(u)||n.has(c)||s.push({pid:c,ppid:u,etimeSeconds:Ry(d),etime:d,command:o})}return s.sort((r,o)=>r.etimeSeconds-o.etimeSeconds),s}function wy(){if(process.platform==="win32")try{return Od("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'node.exe' -and $_.CommandLine -like '*entrypoint.js*' } | ForEach-Object { "$($_.ProcessId) $($_.ParentProcessId) 00:00 $($_.CommandLine)" }`],{encoding:"utf8",timeout:5e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] Win32_Process survey failed: ${t}
1390
- `),""}try{return Od("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] ps -axo failed: ${t}
1391
- `),""}}function Ry(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Ld(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Ld),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Ld(e){let t=Number(e);return Number.isFinite(t)?t:0}Q();import{existsSync as ir,readFileSync as Xv,writeFileSync as Wt,unlinkSync as Cy}from"node:fs";import{basename as ar,join as Ni}from"node:path";import{randomBytes as Oy}from"node:crypto";Q();import{join as Ly}from"node:path";Q();import{appendFileSync as ky}from"node:fs";import{join as Ay}from"node:path";var xy=Ay(B,"daemon.log"),Ny="[state-files-audit]";function Bt(e,t){let s=(new Error().stack??"").split(`
1392
- `).slice(2,4).map(a=>a.trim().replace(/file:\/\/.*?(\bdist\b|\bsrc\b)/g,"$1")).join(" << "),o=`${new Date().toISOString()} ${Ny} ${e}: ${t} | caller: ${s}
1393
- `;try{ky(xy,o)}catch{}}var Cd=Ly(B,"daemon.token");function vd(){let e=Oy(32).toString("hex");return Bt("mint-token",`length=${e.length}`),e}var vy=Ni(B,"daemon.pid"),Iy=Ni(B,"daemon.port"),Vv=Ni(B,"daemon.log");function Mn(){return{pid:vy,port:Iy,token:Cd}}function Id(e,t={}){let n=t.paths??Mn();z(),Wt(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),Wt(n.port,String(e.port),{encoding:"utf8",mode:384}),Wt(n.token,e.token,{encoding:"utf8",mode:384}),Bt("write-info-atomic",`pid=${e.pid} port=${e.port}`)}function Oi(e={}){let t=e.paths??Mn(),n=[];for(let s of[t.pid,t.port,t.token])if(ir(s))try{Cy(s),n.push(ar(s))}catch{}return Bt("clear-force",`cleared: ${n.join(",")||"(none -- files already absent)"}`),{deleted:!0,reason:`cleared ${n.length} file(s)`,cleared:n}}function Md(e){return My(e)}function My(e){try{return process.kill(e,0),!0}catch{return!1}}function Dd(e,t={}){let n=t.paths??Mn(),s=[];return z(),ir(n.pid)||(Wt(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),s.push(ar(n.pid))),ir(n.port)||(Wt(n.port,String(e.port),{encoding:"utf8",mode:384}),s.push(ar(n.port))),ir(n.token)||(Wt(n.token,e.token,{encoding:"utf8",mode:384}),s.push(ar(n.token))),s.length>0&&Bt("heal",`restored: ${s.join(",")}`),{restored:s}}import{statSync as Dy}from"node:fs";function Li(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var jy=6e4,Py=5*6e4,jd=1073741824,Fy=5368709120,$y=6e4,Uy=100;function Ci(e,t="PASSIVE",n){let s=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=By(r),a={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-s};return n&&(t==="PASSIVE"&&a.log>=Uy&&a.moved===0?n(`[wal-maintenance] PASSIVE checkpoint blocked: log=${a.log} frames pending, moved=0 (readers holding snapshots)`):a.moved>0&&n(`[wal-maintenance] ${t} checkpoint: log=${a.log} moved=${a.moved} busy=${a.busy} (${a.durationMs}ms)`)),a}function Pd(e){let t=e.db,n=e.walPath,s=e.checkpointEveryMs??jy,r=e.sizeCheckEveryMs??Py,o=e.warnBytes??jd,a=e.errorBytes??Fy,c=e.forceRestartCooldownMs??$y,u=e.logger??(v=>{process.stderr.write(v+`
1394
- `)}),d=e.statFn??(v=>Dy(v)),m=e.describeSuspectsFn??Hy,f=0,b=null,T=()=>{try{Ci(t,"PASSIVE",u).moved>0&&(b=Date.now())}catch(v){u(`[wal-maintenance] PASSIVE checkpoint threw: ${Li(v)}`)}},S=()=>{let v;try{v=d(n).size}catch(D){let X=D.code;if(X!=="ENOENT"){let I=D instanceof Error?D.message:String(D);u(`[wal-maintenance] WAL stat failed (${X??"unknown"}): ${I}`)}return}if(v>=a){u(`[wal-maintenance] ERROR: WAL ${cr(v)} exceeds error threshold ${cr(a)}`);let D=m();D&&u(`[wal-maintenance] ${D}`);let X=Date.now();if(X-f>=c){f=X;try{let I=Ci(t,"RESTART",u);u(`[wal-maintenance] forced RESTART: moved=${I.moved} busy=${I.busy} log=${I.log} (${I.durationMs}ms)`),I.moved>0&&(b=Date.now())}catch(I){u(`[wal-maintenance] forced RESTART threw: ${Li(I)}`)}}}else v>=o&&u(`[wal-maintenance] WARN: WAL ${cr(v)} exceeds warn threshold ${cr(o)}`)},w=setInterval(T,s),A=setInterval(S,r);return typeof w.unref=="function"&&w.unref(),typeof A.unref=="function"&&A.unref(),{stop:()=>{clearInterval(w),clearInterval(A)},checkpointNow:(v="PASSIVE")=>{let D=Ci(t,v,u);return D.moved>0&&(b=Date.now()),D},walSizeBytes:()=>{try{return d(n).size}catch{return 0}},lastCheckpointAt:()=>b}}function Hy(e={}){let n=(e.list??Ht)();if(n.length===0)return null;let s=n.find(ki);if(s)return`pin suspect: pid ${s.pid} at ${s.pcpu.toFixed(0)}%cpu, elapsed ${s.etimeSeconds}s (parent ${s.parentCommand??s.ppid})`;let r=n.slice().sort((o,a)=>a.etimeSeconds-o.etimeSeconds)[0];return`pin suspect: pid ${r.pid}, elapsed ${r.etimeSeconds}s (parent ${r.parentCommand??r.ppid})`}function By(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:vi(t.busy),log:vi(t.log),checkpointed:vi(t.checkpointed)}}function vi(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}function cr(e){return e>=1e9?`${(e/1e9).toFixed(2)} GB`:e>=1e6?`${(e/1e6).toFixed(0)} MB`:e>=1e3?`${(e/1e3).toFixed(0)} KB`:`${e} B`}Q();U();import{existsSync as Wy,readFileSync as qy,renameSync as $d,writeFileSync as Xy}from"node:fs";import{join as Jy}from"node:path";var Gy=24,Yy=3600*1e3,zy=1e3,Ky=1e3,Vy=1e4,Qy=5e3,Zy=1440*60*1e3,Fd=1e4;function Ud(){return Jy(B,"doctor-state.json")}function Hd(){let e=Ud();if(!Wy(e))return{chunkQueue:{samples:[]}};let t;try{t=qy(e,"utf8")}catch{return{chunkQueue:{samples:[]}}}try{let n=JSON.parse(t),s=n.chunkQueue?.samples,r=Array.isArray(s)?s.filter(u=>typeof u=="object"&&u!==null&&typeof u.ts=="string"&&typeof u.size=="number"&&typeof u.semanticEnabled=="boolean"):[],o=n.autoPruneCounters?.events,a=Array.isArray(o)?o.filter(u=>{if(!u||typeof u!="object")return!1;let d=u;return!(typeof d.ts!="string"||typeof d.pid!="number"||!Number.isFinite(d.pid)||d.action!=="would_kill"&&d.action!=="killed"&&d.action!=="failed"||d.reason!=="orphan_10min"&&d.reason!=="runaway_cpu_5min")}):[],c={chunkQueue:{samples:r}};return(a.length>0||o!==void 0)&&(c.autoPruneCounters={events:a}),c}catch{try{$d(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function Bd(e){let t=Ud(),n=`${t}.tmp`;try{Xy(n,JSON.stringify(e,null,2),{mode:384}),$d(n,t)}catch{}}function Ii(){let e=h(),t=0;try{t=e.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let n=!1;try{n=e.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}let s=Hd(),r=Date.now(),o=s.chunkQueue.samples,a=o.map(S=>({s:S,ms:Date.parse(S.ts)})).filter(S=>Number.isFinite(S.ms)&&r-S.ms<=Yy).sort((S,w)=>S.ms-w.ms),c=null;a.length>0&&(c=t-a[0].s.size);let u=a.length,m={chunkQueue:{samples:[...o,{ts:new Date(r).toISOString(),size:t,semanticEnabled:n}].slice(-Gy)}};s.autoPruneCounters&&(m.autoPruneCounters=s.autoPruneCounters),Bd(m);let f="ok",b=`chunk_queue growth: ok (current ${t.toLocaleString()} row${t===1?"":"s"}, semantic ${n?"on":"off"}, ${u} prior sample${u===1?"":"s"} in last hour).`,T=null;return t>zy&&!n&&c!==null&&c>Ky?(f="critical",b=`chunk_queue growth: CRITICAL \u2014 ${t.toLocaleString()} rows with semantic disabled, grew by ${c.toLocaleString()} in the last hour. Schema-sync race shape.`,T="Schema-sync race likely \u2014 Phase 1.1 fix shipped on `feat/daemon-state-integrity`; if rows persist after a clean restart, re-investigate. Inspect with `sqlite3 ~/.recall/db.sqlite 'SELECT action, COUNT(*) FROM chunk_queue GROUP BY action;'`."):t>Vy?(f="high",b=`chunk_queue growth: HIGH \u2014 ${t.toLocaleString()} rows pending (semantic ${n?"on":"off"}`+(c!==null?`, last-hour \u0394 ${c>=0?"+":""}${c.toLocaleString()}`:"")+").",T="Queue is large but stable \u2014 operator-authorized `recall semantic backfill` (or the daemon's embed worker, if intentionally on) would drain."):c!==null&&c>Qy&&(f="medium",b=`chunk_queue growth: MEDIUM \u2014 grew by ${c.toLocaleString()} in the last hour (current ${t.toLocaleString()}, semantic ${n?"on":"off"}).`,T="Growth is fast even though the absolute size is small. Re-run `recall doctor` in 10\u201330 min; if growth continues without semantic on, investigate the schema-sync gate."),{status:f,currentSize:t,semanticEnabled:n,lastHourGrowth:c,priorSampleCount:u,message:b,remediation:T}}function Wd(e,t=Date.now()){if(e.length===0)return;let n=Hd(),r=[...n.autoPruneCounters?.events??[],...e],o=ew(r,t),a={chunkQueue:n.chunkQueue,autoPruneCounters:{events:o}};Bd(a)}function ew(e,t){let n=e.filter(s=>{let r=Date.parse(s.ts);return Number.isFinite(r)&&t-r<=Zy});return n.length>Fd?n.slice(-Fd):n}var qd="dry-run",tw=600,nw=95,sw=300,rw=5e3;function lr(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return qd;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:qd}async function Gd(e={}){let t=e.mode??lr(),n=e.log??cw,s=e.now??Date.now,r={mode:t,candidates:[],wouldHaveKilled:[],killed:[],failed:[]};if(t==="off")return r;let o=e.list??Ht,a;try{a=o()}catch(m){return n(`[auto-prune] inventory scan failed: ${Ze(m)}`),r}for(let m of a){let f=ow(m);f!==null&&r.candidates.push({pid:m.pid,reason:f,proc:m})}if(r.candidates.length===0)return r;if(t==="dry-run"){for(let m of r.candidates)n(Mi("WOULD kill",m.pid,m.reason,m.proc)),r.wouldHaveKilled.push({pid:m.pid,reason:m.reason});return Jd(r,s()),r}let c=e.kill??iw,u=e.sleep??aw,d=[];for(let m of r.candidates)try{c(m.pid,"SIGTERM"),d.push({pid:m.pid,reason:m.reason})}catch(f){f?.code==="ESRCH"?r.killed.push({pid:m.pid,reason:m.reason}):(n(`[auto-prune] kill failed pid=${m.pid} reason=${m.reason} error=${Ze(f)}`),r.failed.push({pid:m.pid,reason:m.reason,error:Ze(f)}))}await u(rw);for(let m of d){let f=!1;try{c(m.pid,0),f=!0}catch(b){b?.code==="ESRCH"?r.killed.push({pid:m.pid,reason:m.reason}):(n(`[auto-prune] liveness probe failed pid=${m.pid} reason=${m.reason} error=${Ze(b)}`),r.failed.push({pid:m.pid,reason:m.reason,error:Ze(b)}))}if(f)try{c(m.pid,"SIGKILL"),r.killed.push({pid:m.pid,reason:m.reason}),n(Mi("killed",m.pid,m.reason,Xd(r.candidates,m.pid)))}catch(b){n(`[auto-prune] SIGKILL failed pid=${m.pid} reason=${m.reason} error=${Ze(b)}`),r.failed.push({pid:m.pid,reason:m.reason,error:Ze(b)})}else n(Mi("killed",m.pid,m.reason,Xd(r.candidates,m.pid)))}return Jd(r,s()),r}function ow(e){return e.orphan&&e.etimeSeconds>tw?"orphan_10min":e.pcpu>nw&&e.etimeSeconds>sw?"runaway_cpu_5min":null}function Xd(e,t){return e.find(s=>s.pid===t).proc}function Mi(e,t,n,s){return`[auto-prune] ${e} pid=${t} reason=${n} etime=${s.etimeSeconds}s pcpu=${s.pcpu}% rssKb=${s.rssKb} ppid=${s.ppid} parentAlive=${s.parentAlive}`}function Jd(e,t){let n=new Date(t).toISOString(),s=[];for(let r of e.wouldHaveKilled)s.push({ts:n,pid:r.pid,action:"would_kill",reason:r.reason});for(let r of e.killed)s.push({ts:n,pid:r.pid,action:"killed",reason:r.reason});for(let r of e.failed)s.push({ts:n,pid:r.pid,action:"failed",reason:r.reason});if(s.length!==0)try{Wd(s,t)}catch(r){process.stderr.write(`[auto-prune] counter persist failed: ${Ze(r)}
1395
- `)}}function iw(e,t){process.kill(e,t)}function aw(e){return new Promise(t=>{setTimeout(t,e)})}function cw(e){process.stderr.write(e+`
1396
- `)}function Ze(e){if(e instanceof Error){let t=e.code;return t?`${t} ${e.message}`:e.message}return String(e)}Q();import{existsSync as lw,readFileSync as uw,renameSync as zd,writeFileSync as dw}from"node:fs";import{createHash as pw}from"node:crypto";import{join as mw}from"node:path";function Kd(){return mw(B,"doctor-alerts.json")}function gw(e,t){let n=Object.keys(t).sort(),s={};for(let o of n)s[o]=t[o];let r=pw("sha256");return r.update(e),r.update("\0"),r.update(JSON.stringify(s)),r.digest("hex")}function Di(){let e=Kd();if(!lw(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=uw(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return Yd(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return Yd(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter(fw):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function Yd(e){try{zd(e,`${e}.corrupt.${Date.now()}`)}catch{}}function fw(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&typeof t.check=="string"&&(t.severity==="critical"||t.severity==="high"||t.severity==="medium")&&typeof t.message=="string"&&typeof t.remediation=="string"&&typeof t.firstSeenAt=="string"&&typeof t.lastSeenAt=="string"&&typeof t.seenCount=="number"&&typeof t.acknowledged=="boolean"}function ji(e){let t=Kd(),n=`${t}.tmp`;try{dw(n,JSON.stringify(e,null,2),{mode:384}),zd(n,t)}catch{}}function Vd(e,t,n=new Date){let s=n.toISOString(),r=new Map;for(let o of e.alerts)r.set(o.id,o);for(let o of t){let a=gw(o.check,o.keyFacts),c=r.get(a);c?r.set(a,{...c,severity:o.severity,message:o.message,remediation:o.remediation,lastSeenAt:s,seenCount:c.seenCount+1}):r.set(a,{id:a,check:o.check,severity:o.severity,message:o.message,remediation:o.remediation,firstSeenAt:s,lastSeenAt:s,seenCount:1,acknowledged:!1})}return{version:1,lastTickAt:s,alerts:[...r.values()]}}var bw=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],UI=new RegExp(`^(${bw.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var tp=50;var HI=5*6e4;function Sw(e){return e==="db.sqlite"||e==="db.sqlite-wal"||e==="db.sqlite-shm"?!1:!!(e.startsWith("db.sqlite.")||e.endsWith(".bak")||e.includes(".bak."))}function np(e=B){let t=0,n=0;try{let T=Zd(e);t=Number(T.bavail)*Number(T.bsize),n=Number(T.blocks)*Number(T.bsize)}catch{}let s=n>0?t/n*100:100,r=[];try{r=_w(e)}catch{r=[]}let o=0,a=0,c=[],u=Date.now(),d=720*60*60*1e3;for(let T of r){if(!Sw(T))continue;let S;try{S=hw(ep(e,T))}catch{continue}if(!S.isFile())continue;a+=1,o+=S.size;let w=Math.max(0,u-S.mtimeMs),A=Math.floor(w/(1440*60*1e3));w>=d&&c.push({name:T,sizeBytes:S.size,ageDays:A})}c.sort((T,S)=>S.sizeBytes!==T.sizeBytes?S.sizeBytes-T.sizeBytes:S.ageDays-T.ageDays);let m=2*1024**3,f=5*1024**3,b="ok";return n>0&&s<10||o>f?b="high":n>0&&s<20||o>m?b="medium":a>0&&(b="low"),{freeBytes:t,totalBytes:n,freePercent:s,backupTotalBytes:o,backupFileCount:a,oldFiles:c,severity:b}}function sp(e={}){if((e.liveDaemons??xi({excludePids:[process.pid]})).length===0)return{flagged:!1,severity:"ok",daemonAlive:!1,missing:[],message:null,remediation:null};let n=e.paths??Mn(),s=e.existsSync??Pi,r=e.isProcessAlive??Md,o=!1;if(s(n.pid))try{let u=JSON.parse(Qd(n.pid,"utf8"));typeof u.pid=="number"&&r(u.pid)&&(o=!0)}catch{}if(!o)return{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null};let a=[];return s(n.port)||a.push("daemon.port"),s(n.token)||a.push("daemon.token"),a.length===0?{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null}:{flagged:!0,severity:"critical",daemonAlive:!0,missing:a,message:`Daemon process is alive (per pidfile) but companion state file${a.length===1?"":"s"} ${a.join(", ")} missing. Web UI and authenticated CLI calls will return 401 until the 30s heal tick restores them.`,remediation:"The 30-second heal tick re-creates these files automatically. To force immediate recovery: recall stop && recall start. Inspect ~/.recall/daemon.log for [state-files-audit] entries to identify the deletion source."}}function rp(e=ep(Ew(),".claude.json")){let t={status:"ok",configPath:e,configExists:Pi(e),findings:[]};if(!t.configExists)return t;let n;try{n=Qd(e,"utf8")}catch{return t}let s;try{s=JSON.parse(n)}catch{return t}if(!s||typeof s!="object"||Array.isArray(s))return t;let r=s.mcpServers;if(!r||typeof r!="object"||Array.isArray(r))return t;for(let[o,a]of Object.entries(r)){if(!a||typeof a!="object")continue;let c=a.args;if(!Array.isArray(c)||c.length===0)continue;let u=c[0];if(typeof u!="string"||u.length===0||!u.startsWith("/")&&!u.startsWith("~")||Pi(u))continue;let d=o==="recall";t.findings.push({name:o,stalePath:u,severity:d?"HIGH":"MEDIUM",remediation:d?"restart daemon (`recall stop && recall start`) \u2014 auto-repoint runs on boot now":"this is a third-party MCP \u2014 the vendor's package may have moved; reinstall their package"})}return t.findings.some(o=>o.severity==="HIGH")?t.status="fail":t.findings.length>0&&(t.status="warn"),t}function Fi(){try{let e=Zd(B);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function op(e){let{projects:t,sessions:n,messages:s,port:r,version:o}=e;return`<!DOCTYPE html>
1386
+ `),E=new Date().toISOString(),y=new Set,k=new Set,O=n.transaction(()=>{let{id:x}=$.get({encoded_path:o,decoded_path:D,name:v,repo_root:X,main_repo:I,is_repo:P,is_worktree:te,have_cwd:A?1:0});for(let w of S.values()){if(Su(w.firstUser)){console.log(`[watcher] skipping daemon-spawn phantom ${w.sessionId} (first message matches autonomous-spawn pattern)`),l.run(w.sessionId),p.run({id:w.sessionId,project_id:x,file_path:e,file_mtime:a,started_at:w.earliest,ended_at:w.latest,cwd:w.cwd,indexed_at:E}),rr(n,e,{kind:"skip-durable",reason:"daemon_spawn_phantom"}),y.add(w.sessionId);continue}if(m.mode==="incremental"&&g.get(w.sessionId)){k.add(w.sessionId);for(let W of w.entries)i.run(xd(W));_.run({id:w.sessionId,file_mtime:a,indexed_at:E}),Vo(n,w.sessionId,w.entries,{insertOnly:!0}),wn(n,w.sessionId);continue}let C=gi(w.firstUser),F=C.alias?C.stripped:w.firstUser;if(ne.run({id:w.sessionId,project_id:x,file_path:e,file_mtime:a,started_at:w.earliest,ended_at:w.latest,message_count:w.entries.length,user_message_count:w.users,assistant_message_count:w.assistants,first_user_message:F,cwd:w.cwd,git_branch:w.branch,version:w.version,indexed_at:E}),C.alias&&!Se(w.sessionId))try{_e(w.sessionId,C.alias)}catch(W){console.error(`[watcher] header-alias setAlias failed for ${w.sessionId}:`,W)}l.run(w.sessionId);for(let W of w.entries)i.run(xd(W));Vo(n,w.sessionId,w.entries),wn(n,w.sessionId)}});try{Jo(()=>O(),{onRetry:(x,w)=>{ki(e,Date.now())&&console.error(`[watcher] reindex write busy (attempt ${x}) for ${e}, retrying: ${w instanceof Error?w.message:String(w)}`)}})}catch(x){if(!Is(x))throw x;Ad(e,"reindex-txn",x);return}ir.delete(e);let N=vs(e,T),L=n.prepare("SELECT COUNT(*) AS n FROM messages m JOIN sessions s ON s.id = m.session_id WHERE s.file_path = ?").get(e);if(Xo(n,e,{byteOffset:T,sizeBytes:Number(c.size),inode:Number(c.ino),prefixHash:N,mtime:a,lineCount:L.n}),_t().heuristicEnabled)for(let x of S.values()){if(y.has(x.sessionId)||k.has(x.sessionId))continue;let w=gi(x.firstUser).stripped,C=jt(w);C&&he(x.sessionId,C,"heuristic")}}function Od(e){return Nd.set(e,Date.now()),nd(e,()=>fy(e)).finally(()=>{let t=0;try{t=Ut(e).size}catch{}Td(e,t)})}function xd(e){let t=be(e.contentText).redacted,n=be(e.raw).redacted;return{uuid:e.uuid,session_id:e.sessionId,parent_uuid:e.parentUuid,type:e.type,role:e.role,timestamp:e.timestamp,is_sidechain:e.isSidechain?1:0,content_text:t,tool_names:e.toolNames.join(","),raw_json:n}}function Ai(e){let t=Ri.get(e);t?.timer&&clearTimeout(t.timer);let n=Date.now(),s=wd(n,Nd.get(e)),r=ir.get(e);r!==void 0&&(r>n?s=Math.max(s,r-n):ir.delete(e));let o={timer:null};o.timer=setTimeout(()=>{Ri.delete(e),Od(e).then(async()=>{my(e),Ps(e);try{let c=h().prepare("SELECT id FROM sessions WHERE file_path = ? AND skipped_reason IS NULL").all(e);for(let u of c){hl(u.id),fu(u.id);try{zu(u.id)}catch(d){console.error("[watcher] auto-collections apply failed:",d)}}}catch(a){console.error("[watcher] semantic dispatch failed:",a)}}).catch(a=>{console.error(`[watcher] reindex failed for ${e}:`,a)})},s),Ri.set(e,o)}function _y(e){if(e.endsWith(".jsonl"))return ar(e);try{if(Ut(e).isDirectory())return!1}catch{}return!0}function Cd(){let e=ly(an,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:_y});return e.on("add",t=>t.endsWith(".jsonl")&&!ar(t)&&Ai(t)),e.on("change",t=>t.endsWith(".jsonl")&&!ar(t)&&Ai(t)),e.on("ready",()=>{console.log(`[watcher] ready, watching ${an}`)}),e.on("error",t=>{console.error("[watcher] chokidar error:",t)}),process.env.RECALL_WATCHER_DEBUG==="1"&&(e.on("add",t=>console.log(`[watcher.debug] add: ${t}`)),e.on("change",t=>console.log(`[watcher.debug] change: ${t}`)),e.on("unlink",t=>console.log(`[watcher.debug] unlink: ${t}`))),e}var hy=4;function*Ni(e){let t;try{t=uy(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=dy(e,n.name);n.isDirectory()?yield*Ni(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}async function Oi(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},n=h(),s=[],r=n.prepare("SELECT file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1");for(let d of Ni(an)){t.scanned+=1;let m;try{m=Ut(d).mtimeMs}catch{m=null}let f=r.get(d),b=wi({filePath:d,existing:f??null,currentMtime:m});switch(b.kind){case"skip-durable":rr(n,d,b),t.skipped+=1;continue;case"skip-already-marked":t.skipped+=1;continue;case"skip-transient":b.reason==="unchanged_mtime"?t.upToDate+=1:t.errors+=1;continue;case"process":s.push(d);continue}}if(s.length===0)return t.durationMs=Date.now()-e,t;let o=s.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await Od(d),t.reindexed+=1}catch(m){t.errors+=1;let f=m instanceof Error?m.message:String(m);try{gy(d,m)}catch(b){console.error(`[ingestion-sweep] marker write failed for ${d}:`,b)}console.error(`[ingestion-sweep] failed for ${d}: ${f}`)}}},c=Math.min(hy,s.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}async function Ld(){try{let e=await Oi();e.reindexed>0&&console.log(`[safety-sweep] reindexed ${e.reindexed} file(s) the watcher missed (scanned=${e.scanned}, upToDate=${e.upToDate})`)}catch(e){console.error("[safety-sweep] failed:",e)}}import{execFileSync as Dd}from"node:child_process";var Ey=["dist/mcp-server.js","dist/mcp/server.js"];function by(e){for(let t of Ey)if(e.includes(t))return!0;return!1}function Bt(e={}){let t=e.psOutput??Sy(),n=e.isProcessAlive??Ty,s=e.getParentCommand??yy,r=[];for(let o of t.split(`
1387
+ `)){let a=o.trim();if(!a||!by(a)||wy(a))continue;let c=a.split(/\s+/);if(c.length<5)continue;let u=Number(c[0]),d=Number(c[1]),m=c[2],f=c[3],b=c[4],T=0,S=0;if(/^\d+$/.test(f)&&(b.includes(".")||/^\d+$/.test(b))?(T=Number(f),S=Number(b)):S=Number(f),!Number.isFinite(u)||!Number.isFinite(d))continue;let A=d>1&&n(d);r.push({pid:u,ppid:d,parentAlive:A,etimeSeconds:Ry(m),pcpu:Number.isFinite(S)?S:0,rssKb:Number.isFinite(T)?T:0,orphan:!A,parentCommand:A?s(d):null})}return r}function Sy(){try{return Dd("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -axo failed: ${t}
1388
+ `),""}}function Ty(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function yy(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=Dd("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return n.length?n.slice(0,200):null}catch{return null}}function wy(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=[t[5]??"",t[4]??""];for(let s of n)if(s&&(s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"))return!0;return!1}function Ry(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=vd(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(vd),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function vd(e){let t=Number(e);return Number.isFinite(t)?t:0}var ky=50,Ay=60;function Ci(e){return e.pcpu>=ky&&e.etimeSeconds>=Ay}var Id=4,xy=1024*1024;function Md(e){return e.reduce((t,n)=>t+(Number.isFinite(n.rssKb)&&n.rssKb>0?n.rssKb:0),0)}function Li(e){let t=e??Bt(),n=t.length,s=Md(t),r=t.filter(b=>b.orphan),o=r.length,a=Md(r),c=o>Id,u=a>xy;if(!(c||u))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,orphanCount:o,orphanRssKb:a,message:null};let m=[];c&&m.push(`${o} orphaned MCP children (threshold ${Id})`),u&&m.push(`${Ny(a)} aggregate RSS across orphaned children (threshold 1 GiB)`);let f=`Zombie MCP threshold breached: ${m.join(" + ")}. Each orphaned MCP child (its parent claude/VS Code tab has exited) still holds a SQLite read connection and can pin WAL checkpoints. Run \`recall mcp-prune --all\` to reap (note: pre-2026-05-23 builds may miss processes from stale install paths -- \`pkill -f mcp-server.js\` is the nuclear option).`;return{flagged:!0,severity:"high",count:n,aggregateRssKb:s,orphanCount:o,orphanRssKb:a,message:f}}function Ny(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}import{execFileSync as jd}from"node:child_process";var Oy=["dist/daemon/entrypoint.js"];function Cy(e){let t=e.replace(/\\/g,"/");for(let n of Oy)if(t.includes(n))return!0;return!1}function Ly(e){if(e.length<5)return!1;let t=[e[5]??"",e[4]??""];for(let n of t)if(n&&(n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"))return!0;return!1}function vi(e={}){let t=e.psOutput??vy(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
1389
+ `)){let o=r.trim();if(!o||!Cy(o))continue;let a=o.split(/\s+/);if(Ly(a)||a.length<5)continue;let c=Number(a[0]),u=Number(a[1]),d=a[2];!Number.isFinite(c)||!Number.isFinite(u)||n.has(c)||s.push({pid:c,ppid:u,etimeSeconds:Iy(d),etime:d,command:o})}return s.sort((r,o)=>r.etimeSeconds-o.etimeSeconds),s}function vy(){if(process.platform==="win32")try{return jd("powershell.exe",["-NoProfile","-NonInteractive","-Command",`Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'node.exe' -and $_.CommandLine -like '*entrypoint.js*' } | ForEach-Object { "$($_.ProcessId) $($_.ParentProcessId) 00:00 $($_.CommandLine)" }`],{encoding:"utf8",timeout:5e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] Win32_Process survey failed: ${t}
1390
+ `),""}try{return jd("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] ps -axo failed: ${t}
1391
+ `),""}}function Iy(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Pd(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Pd),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Pd(e){let t=Number(e);return Number.isFinite(t)?t:0}Q();import{existsSync as cr,readFileSync as nI,writeFileSync as Wt,unlinkSync as Uy}from"node:fs";import{basename as lr,join as Ii}from"node:path";import{randomBytes as Fy}from"node:crypto";Q();import{join as $y}from"node:path";Q();import{appendFileSync as My}from"node:fs";import{join as Dy}from"node:path";var jy=Dy(H,"daemon.log"),Py="[state-files-audit]";function Ht(e,t){let s=(new Error().stack??"").split(`
1392
+ `).slice(2,4).map(a=>a.trim().replace(/file:\/\/.*?(\bdist\b|\bsrc\b)/g,"$1")).join(" << "),o=`${new Date().toISOString()} ${Py} ${e}: ${t} | caller: ${s}
1393
+ `;try{My(jy,o)}catch{}}var Fd=$y(H,"daemon.token");function $d(){let e=Fy(32).toString("hex");return Ht("mint-token",`length=${e.length}`),e}var By=Ii(H,"daemon.pid"),Hy=Ii(H,"daemon.port"),cI=Ii(H,"daemon.log");function Mn(){return{pid:By,port:Hy,token:Fd}}function Ud(e,t={}){let n=t.paths??Mn();z(),Wt(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),Wt(n.port,String(e.port),{encoding:"utf8",mode:384}),Wt(n.token,e.token,{encoding:"utf8",mode:384}),Ht("write-info-atomic",`pid=${e.pid} port=${e.port}`)}function Mi(e={}){let t=e.paths??Mn(),n=[];for(let s of[t.pid,t.port,t.token])if(cr(s))try{Uy(s),n.push(lr(s))}catch{}return Ht("clear-force",`cleared: ${n.join(",")||"(none -- files already absent)"}`),{deleted:!0,reason:`cleared ${n.length} file(s)`,cleared:n}}function Bd(e){return Wy(e)}function Wy(e){try{return process.kill(e,0),!0}catch{return!1}}function Hd(e,t={}){let n=t.paths??Mn(),s=[];return z(),cr(n.pid)||(Wt(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),s.push(lr(n.pid))),cr(n.port)||(Wt(n.port,String(e.port),{encoding:"utf8",mode:384}),s.push(lr(n.port))),cr(n.token)||(Wt(n.token,e.token,{encoding:"utf8",mode:384}),s.push(lr(n.token))),s.length>0&&Ht("heal",`restored: ${s.join(",")}`),{restored:s}}import{statSync as qy}from"node:fs";function Di(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var Xy=6e4,Jy=5*6e4,Wd=1073741824,Gy=5368709120,Yy=6e4,zy=100;function ji(e,t="PASSIVE",n){let s=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=Vy(r),a={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-s};return n&&(t==="PASSIVE"&&a.log>=zy&&a.moved===0?n(`[wal-maintenance] PASSIVE checkpoint blocked: log=${a.log} frames pending, moved=0 (readers holding snapshots)`):a.moved>0&&n(`[wal-maintenance] ${t} checkpoint: log=${a.log} moved=${a.moved} busy=${a.busy} (${a.durationMs}ms)`)),a}function qd(e){let t=e.db,n=e.walPath,s=e.checkpointEveryMs??Xy,r=e.sizeCheckEveryMs??Jy,o=e.warnBytes??Wd,a=e.errorBytes??Gy,c=e.forceRestartCooldownMs??Yy,u=e.logger??(v=>{process.stderr.write(v+`
1394
+ `)}),d=e.statFn??(v=>qy(v)),m=e.describeSuspectsFn??Ky,f=0,b=null,T=()=>{try{ji(t,"PASSIVE",u).moved>0&&(b=Date.now())}catch(v){u(`[wal-maintenance] PASSIVE checkpoint threw: ${Di(v)}`)}},S=()=>{let v;try{v=d(n).size}catch(D){let X=D.code;if(X!=="ENOENT"){let I=D instanceof Error?D.message:String(D);u(`[wal-maintenance] WAL stat failed (${X??"unknown"}): ${I}`)}return}if(v>=a){u(`[wal-maintenance] ERROR: WAL ${ur(v)} exceeds error threshold ${ur(a)}`);let D=m();D&&u(`[wal-maintenance] ${D}`);let X=Date.now();if(X-f>=c){f=X;try{let I=ji(t,"RESTART",u);u(`[wal-maintenance] forced RESTART: moved=${I.moved} busy=${I.busy} log=${I.log} (${I.durationMs}ms)`),I.moved>0&&(b=Date.now())}catch(I){u(`[wal-maintenance] forced RESTART threw: ${Di(I)}`)}}}else v>=o&&u(`[wal-maintenance] WARN: WAL ${ur(v)} exceeds warn threshold ${ur(o)}`)},R=setInterval(T,s),A=setInterval(S,r);return typeof R.unref=="function"&&R.unref(),typeof A.unref=="function"&&A.unref(),{stop:()=>{clearInterval(R),clearInterval(A)},checkpointNow:(v="PASSIVE")=>{let D=ji(t,v,u);return D.moved>0&&(b=Date.now()),D},walSizeBytes:()=>{try{return d(n).size}catch{return 0}},lastCheckpointAt:()=>b}}function Ky(e={}){let n=(e.list??Bt)();if(n.length===0)return null;let s=n.find(Ci);if(s)return`pin suspect: pid ${s.pid} at ${s.pcpu.toFixed(0)}%cpu, elapsed ${s.etimeSeconds}s (parent ${s.parentCommand??s.ppid})`;let r=n.slice().sort((o,a)=>a.etimeSeconds-o.etimeSeconds)[0];return`pin suspect: pid ${r.pid}, elapsed ${r.etimeSeconds}s (parent ${r.parentCommand??r.ppid})`}function Vy(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:Pi(t.busy),log:Pi(t.log),checkpointed:Pi(t.checkpointed)}}function Pi(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}function ur(e){return e>=1e9?`${(e/1e9).toFixed(2)} GB`:e>=1e6?`${(e/1e6).toFixed(0)} MB`:e>=1e3?`${(e/1e3).toFixed(0)} KB`:`${e} B`}Q();U();import{existsSync as Qy,readFileSync as Zy,renameSync as Jd,writeFileSync as ew}from"node:fs";import{join as tw}from"node:path";var nw=24,sw=3600*1e3,rw=1e3,ow=1e3,iw=1e4,aw=5e3,cw=1440*60*1e3,Xd=1e4;function Gd(){return tw(H,"doctor-state.json")}function Yd(){let e=Gd();if(!Qy(e))return{chunkQueue:{samples:[]}};let t;try{t=Zy(e,"utf8")}catch{return{chunkQueue:{samples:[]}}}try{let n=JSON.parse(t),s=n.chunkQueue?.samples,r=Array.isArray(s)?s.filter(u=>typeof u=="object"&&u!==null&&typeof u.ts=="string"&&typeof u.size=="number"&&typeof u.semanticEnabled=="boolean"):[],o=n.autoPruneCounters?.events,a=Array.isArray(o)?o.filter(u=>{if(!u||typeof u!="object")return!1;let d=u;return!(typeof d.ts!="string"||typeof d.pid!="number"||!Number.isFinite(d.pid)||d.action!=="would_kill"&&d.action!=="killed"&&d.action!=="failed"||d.reason!=="orphan_10min"&&d.reason!=="runaway_cpu_5min")}):[],c={chunkQueue:{samples:r}};return(a.length>0||o!==void 0)&&(c.autoPruneCounters={events:a}),c}catch{try{Jd(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function zd(e){let t=Gd(),n=`${t}.tmp`;try{ew(n,JSON.stringify(e,null,2),{mode:384}),Jd(n,t)}catch{}}function Fi(){let e=h(),t=0;try{t=e.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let n=!1;try{n=e.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}let s=Yd(),r=Date.now(),o=s.chunkQueue.samples,a=o.map(S=>({s:S,ms:Date.parse(S.ts)})).filter(S=>Number.isFinite(S.ms)&&r-S.ms<=sw).sort((S,R)=>S.ms-R.ms),c=null;a.length>0&&(c=t-a[0].s.size);let u=a.length,m={chunkQueue:{samples:[...o,{ts:new Date(r).toISOString(),size:t,semanticEnabled:n}].slice(-nw)}};s.autoPruneCounters&&(m.autoPruneCounters=s.autoPruneCounters),zd(m);let f="ok",b=`chunk_queue growth: ok (current ${t.toLocaleString()} row${t===1?"":"s"}, semantic ${n?"on":"off"}, ${u} prior sample${u===1?"":"s"} in last hour).`,T=null;return t>rw&&!n&&c!==null&&c>ow?(f="critical",b=`chunk_queue growth: CRITICAL \u2014 ${t.toLocaleString()} rows with semantic disabled, grew by ${c.toLocaleString()} in the last hour. Schema-sync race shape.`,T="Schema-sync race likely \u2014 Phase 1.1 fix shipped on `feat/daemon-state-integrity`; if rows persist after a clean restart, re-investigate. Inspect with `sqlite3 ~/.recall/db.sqlite 'SELECT action, COUNT(*) FROM chunk_queue GROUP BY action;'`."):t>iw?(f="high",b=`chunk_queue growth: HIGH \u2014 ${t.toLocaleString()} rows pending (semantic ${n?"on":"off"}`+(c!==null?`, last-hour \u0394 ${c>=0?"+":""}${c.toLocaleString()}`:"")+").",T="Queue is large but stable \u2014 operator-authorized `recall semantic backfill` (or the daemon's embed worker, if intentionally on) would drain."):c!==null&&c>aw&&(f="medium",b=`chunk_queue growth: MEDIUM \u2014 grew by ${c.toLocaleString()} in the last hour (current ${t.toLocaleString()}, semantic ${n?"on":"off"}).`,T="Growth is fast even though the absolute size is small. Re-run `recall doctor` in 10\u201330 min; if growth continues without semantic on, investigate the schema-sync gate."),{status:f,currentSize:t,semanticEnabled:n,lastHourGrowth:c,priorSampleCount:u,message:b,remediation:T}}function Kd(e,t=Date.now()){if(e.length===0)return;let n=Yd(),r=[...n.autoPruneCounters?.events??[],...e],o=lw(r,t),a={chunkQueue:n.chunkQueue,autoPruneCounters:{events:o}};zd(a)}function lw(e,t){let n=e.filter(s=>{let r=Date.parse(s.ts);return Number.isFinite(r)&&t-r<=cw});return n.length>Xd?n.slice(-Xd):n}var Vd="dry-run",uw=600,dw=95,pw=300,mw=5e3;function dr(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return Vd;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:Vd}async function ep(e={}){let t=e.mode??dr(),n=e.log??hw,s=e.now??Date.now,r={mode:t,candidates:[],wouldHaveKilled:[],killed:[],failed:[]};if(t==="off")return r;let o=e.list??Bt,a;try{a=o()}catch(m){return n(`[auto-prune] inventory scan failed: ${Ze(m)}`),r}for(let m of a){let f=gw(m);f!==null&&r.candidates.push({pid:m.pid,reason:f,proc:m})}if(r.candidates.length===0)return r;if(t==="dry-run"){for(let m of r.candidates)n($i("WOULD kill",m.pid,m.reason,m.proc)),r.wouldHaveKilled.push({pid:m.pid,reason:m.reason});return Zd(r,s()),r}let c=e.kill??fw,u=e.sleep??_w,d=[];for(let m of r.candidates)try{c(m.pid,"SIGTERM"),d.push({pid:m.pid,reason:m.reason})}catch(f){f?.code==="ESRCH"?r.killed.push({pid:m.pid,reason:m.reason}):(n(`[auto-prune] kill failed pid=${m.pid} reason=${m.reason} error=${Ze(f)}`),r.failed.push({pid:m.pid,reason:m.reason,error:Ze(f)}))}await u(mw);for(let m of d){let f=!1;try{c(m.pid,0),f=!0}catch(b){b?.code==="ESRCH"?r.killed.push({pid:m.pid,reason:m.reason}):(n(`[auto-prune] liveness probe failed pid=${m.pid} reason=${m.reason} error=${Ze(b)}`),r.failed.push({pid:m.pid,reason:m.reason,error:Ze(b)}))}if(f)try{c(m.pid,"SIGKILL"),r.killed.push({pid:m.pid,reason:m.reason}),n($i("killed",m.pid,m.reason,Qd(r.candidates,m.pid)))}catch(b){n(`[auto-prune] SIGKILL failed pid=${m.pid} reason=${m.reason} error=${Ze(b)}`),r.failed.push({pid:m.pid,reason:m.reason,error:Ze(b)})}else n($i("killed",m.pid,m.reason,Qd(r.candidates,m.pid)))}return Zd(r,s()),r}function gw(e){return e.orphan&&e.etimeSeconds>uw?"orphan_10min":e.pcpu>dw&&e.etimeSeconds>pw?"runaway_cpu_5min":null}function Qd(e,t){return e.find(s=>s.pid===t).proc}function $i(e,t,n,s){return`[auto-prune] ${e} pid=${t} reason=${n} etime=${s.etimeSeconds}s pcpu=${s.pcpu}% rssKb=${s.rssKb} ppid=${s.ppid} parentAlive=${s.parentAlive}`}function Zd(e,t){let n=new Date(t).toISOString(),s=[];for(let r of e.wouldHaveKilled)s.push({ts:n,pid:r.pid,action:"would_kill",reason:r.reason});for(let r of e.killed)s.push({ts:n,pid:r.pid,action:"killed",reason:r.reason});for(let r of e.failed)s.push({ts:n,pid:r.pid,action:"failed",reason:r.reason});if(s.length!==0)try{Kd(s,t)}catch(r){process.stderr.write(`[auto-prune] counter persist failed: ${Ze(r)}
1395
+ `)}}function fw(e,t){process.kill(e,t)}function _w(e){return new Promise(t=>{setTimeout(t,e)})}function hw(e){process.stderr.write(e+`
1396
+ `)}function Ze(e){if(e instanceof Error){let t=e.code;return t?`${t} ${e.message}`:e.message}return String(e)}Q();import{existsSync as Ew,readFileSync as bw,renameSync as np,writeFileSync as Sw}from"node:fs";import{createHash as Tw}from"node:crypto";import{join as yw}from"node:path";function sp(){return yw(H,"doctor-alerts.json")}function ww(e,t){let n=Object.keys(t).sort(),s={};for(let o of n)s[o]=t[o];let r=Tw("sha256");return r.update(e),r.update("\0"),r.update(JSON.stringify(s)),r.digest("hex")}function Ui(){let e=sp();if(!Ew(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=bw(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return tp(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return tp(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter(Rw):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function tp(e){try{np(e,`${e}.corrupt.${Date.now()}`)}catch{}}function Rw(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&typeof t.check=="string"&&(t.severity==="critical"||t.severity==="high"||t.severity==="medium")&&typeof t.message=="string"&&typeof t.remediation=="string"&&typeof t.firstSeenAt=="string"&&typeof t.lastSeenAt=="string"&&typeof t.seenCount=="number"&&typeof t.acknowledged=="boolean"}function Bi(e){let t=sp(),n=`${t}.tmp`;try{Sw(n,JSON.stringify(e,null,2),{mode:384}),np(n,t)}catch{}}function rp(e,t,n=new Date){let s=n.toISOString(),r=new Map;for(let o of e.alerts)r.set(o.id,o);for(let o of t){let a=ww(o.check,o.keyFacts),c=r.get(a);c?r.set(a,{...c,severity:o.severity,message:o.message,remediation:o.remediation,lastSeenAt:s,seenCount:c.seenCount+1}):r.set(a,{id:a,check:o.check,severity:o.severity,message:o.message,remediation:o.remediation,firstSeenAt:s,lastSeenAt:s,seenCount:1,acknowledged:!1})}return{version:1,lastTickAt:s,alerts:[...r.values()]}}var Nw=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],tM=new RegExp(`^(${Nw.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var cp=50;var nM=5*6e4;function Ow(e){return e==="db.sqlite"||e==="db.sqlite-wal"||e==="db.sqlite-shm"?!1:!!(e.startsWith("db.sqlite.")||e.endsWith(".bak")||e.includes(".bak."))}function lp(e=H){let t=0,n=0;try{let T=ip(e);t=Number(T.bavail)*Number(T.bsize),n=Number(T.blocks)*Number(T.bsize)}catch{}let s=n>0?t/n*100:100,r=[];try{r=kw(e)}catch{r=[]}let o=0,a=0,c=[],u=Date.now(),d=720*60*60*1e3;for(let T of r){if(!Ow(T))continue;let S;try{S=Aw(ap(e,T))}catch{continue}if(!S.isFile())continue;a+=1,o+=S.size;let R=Math.max(0,u-S.mtimeMs),A=Math.floor(R/(1440*60*1e3));R>=d&&c.push({name:T,sizeBytes:S.size,ageDays:A})}c.sort((T,S)=>S.sizeBytes!==T.sizeBytes?S.sizeBytes-T.sizeBytes:S.ageDays-T.ageDays);let m=2*1024**3,f=5*1024**3,b="ok";return n>0&&s<10||o>f?b="high":n>0&&s<20||o>m?b="medium":a>0&&(b="low"),{freeBytes:t,totalBytes:n,freePercent:s,backupTotalBytes:o,backupFileCount:a,oldFiles:c,severity:b}}function up(e={}){if((e.liveDaemons??vi({excludePids:[process.pid]})).length===0)return{flagged:!1,severity:"ok",daemonAlive:!1,missing:[],message:null,remediation:null};let n=e.paths??Mn(),s=e.existsSync??Hi,r=e.isProcessAlive??Bd,o=!1;if(s(n.pid))try{let u=JSON.parse(op(n.pid,"utf8"));typeof u.pid=="number"&&r(u.pid)&&(o=!0)}catch{}if(!o)return{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null};let a=[];return s(n.port)||a.push("daemon.port"),s(n.token)||a.push("daemon.token"),a.length===0?{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null}:{flagged:!0,severity:"critical",daemonAlive:!0,missing:a,message:`Daemon process is alive (per pidfile) but companion state file${a.length===1?"":"s"} ${a.join(", ")} missing. Web UI and authenticated CLI calls will return 401 until the 30s heal tick restores them.`,remediation:"The 30-second heal tick re-creates these files automatically. To force immediate recovery: recall stop && recall start. Inspect ~/.recall/daemon.log for [state-files-audit] entries to identify the deletion source."}}function dp(e=ap(xw(),".claude.json")){let t={status:"ok",configPath:e,configExists:Hi(e),findings:[]};if(!t.configExists)return t;let n;try{n=op(e,"utf8")}catch{return t}let s;try{s=JSON.parse(n)}catch{return t}if(!s||typeof s!="object"||Array.isArray(s))return t;let r=s.mcpServers;if(!r||typeof r!="object"||Array.isArray(r))return t;for(let[o,a]of Object.entries(r)){if(!a||typeof a!="object")continue;let c=a.args;if(!Array.isArray(c)||c.length===0)continue;let u=c[0];if(typeof u!="string"||u.length===0||!u.startsWith("/")&&!u.startsWith("~")||Hi(u))continue;let d=o==="recall";t.findings.push({name:o,stalePath:u,severity:d?"HIGH":"MEDIUM",remediation:d?"restart daemon (`recall stop && recall start`) \u2014 auto-repoint runs on boot now":"this is a third-party MCP \u2014 the vendor's package may have moved; reinstall their package"})}return t.findings.some(o=>o.severity==="HIGH")?t.status="fail":t.findings.length>0&&(t.status="warn"),t}function Wi(){try{let e=ip(H);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function pp(e){let{projects:t,sessions:n,messages:s,port:r,version:o}=e;return`<!DOCTYPE html>
1397
1397
  <html lang="en">
1398
1398
  <head>
1399
1399
  <meta charset="utf-8" />
@@ -1448,32 +1448,32 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1448
1448
  </footer>
1449
1449
  </main>
1450
1450
  </body>
1451
- </html>`}var Tw=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,yw=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,ww=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function Rw(e){return e.replace(Tw,"").trim()}function kw(e){let t=e.replace(yw,"[tool call]");return t=t.replace(ww,"[tool result]"),t=t.replace(/_\(unknown block: thinking\)_/g,""),t=t.replace(/(?:\[tool call\]|\[tool result\])(?:\s*(?:\[tool call\]|\[tool result\]))+/g,"[tool activity]"),t=t.replace(/\n{3,}/g,`
1451
+ </html>`}var Cw=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,Lw=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,vw=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function Iw(e){return e.replace(Cw,"").trim()}function Mw(e){let t=e.replace(Lw,"[tool call]");return t=t.replace(vw,"[tool result]"),t=t.replace(/_\(unknown block: thinking\)_/g,""),t=t.replace(/(?:\[tool call\]|\[tool result\])(?:\s*(?:\[tool call\]|\[tool result\]))+/g,"[tool activity]"),t=t.replace(/\n{3,}/g,`
1452
1452
 
1453
- `),t.trim()}function Aw(e){return e.role??e.type??"message"}function ip(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,o=n.since?Date.parse(n.since):0,a=t.filter(m=>!(!r&&m.is_sidechain===1||o&&m.timestamp&&Date.parse(m.timestamp)<o)),c=[];n.prelude&&(c.push(n.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${s})`),c.push(""),c.push(`- **Project**: ${e.project_name}`),e.decoded_path&&c.push(`- **Path**: \`${e.decoded_path}\``),c.push(`- **Session ID**: \`${e.id}\``),e.started_at&&c.push(`- **Started**: ${e.started_at}`),e.ended_at&&c.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&c.push(`- **Branch**: \`${e.git_branch}\``),c.push(`- **Messages**: ${a.length}`),c.push(""),c.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),c.push(""),c.push("---"),c.push("");let u=0,d=0;for(let m of a){let f=m.content_text??"",b=Rw(f);s==="condensed"&&(b=kw(b));let T=b.length>0,S=!!m.tool_names&&m.tool_names.length>0;if(!T&&!S){d+=1;continue}let w=Aw(m),A=m.timestamp?` \`${m.timestamp}\``:"";c.push(`## ${w}${A}`),c.push(""),S&&s==="condensed"&&(c.push(`_tools used: ${m.tool_names}_`),c.push("")),T&&(c.push(b),c.push("")),u+=1}return c.push("---"),c.push(""),c.push(`_${u} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),c.join(`
1454
- `)}function Dn(e){if(!e.sessionStartedAt)return{allowed:!1,reason:"missing-session-started-at"};if(!e.terminalOpenedAt)return{allowed:!1,reason:"missing-terminal-opened-at"};let t=Date.parse(e.sessionStartedAt),n=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(n)?n-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}U();Q();import{writeFileSync as xw,mkdirSync as Nw,existsSync as Ow}from"node:fs";import{join as cp}from"node:path";var $i=cp(B,"notes"),ap=200,Lw=12e3,Cw=800,vw=8e3;function lp(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function up(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&typeof n.synopsis=="string"&&typeof n.replaced_at=="string"):[]}catch{return[]}}function Iw(){z(),Ow($i)||Nw($i,{recursive:!0})}function Mw(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:lp(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:up(e.auto_synopsis_history)}}var Dw="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function ur(e){let t=h().prepare(`SELECT ${Dw} FROM session_notes WHERE session_id = ?`).get(e);return t?Mw(t):null}function dp(e,t){let n=h(),s=new Date().toISOString(),r=n.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=lp(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:s})),n.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1453
+ `),t.trim()}function Dw(e){return e.role??e.type??"message"}function mp(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,o=n.since?Date.parse(n.since):0,a=t.filter(m=>!(!r&&m.is_sidechain===1||o&&m.timestamp&&Date.parse(m.timestamp)<o)),c=[];n.prelude&&(c.push(n.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${s})`),c.push(""),c.push(`- **Project**: ${e.project_name}`),e.decoded_path&&c.push(`- **Path**: \`${e.decoded_path}\``),c.push(`- **Session ID**: \`${e.id}\``),e.started_at&&c.push(`- **Started**: ${e.started_at}`),e.ended_at&&c.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&c.push(`- **Branch**: \`${e.git_branch}\``),c.push(`- **Messages**: ${a.length}`),c.push(""),c.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),c.push(""),c.push("---"),c.push("");let u=0,d=0;for(let m of a){let f=m.content_text??"",b=Iw(f);s==="condensed"&&(b=Mw(b));let T=b.length>0,S=!!m.tool_names&&m.tool_names.length>0;if(!T&&!S){d+=1;continue}let R=Dw(m),A=m.timestamp?` \`${m.timestamp}\``:"";c.push(`## ${R}${A}`),c.push(""),S&&s==="condensed"&&(c.push(`_tools used: ${m.tool_names}_`),c.push("")),T&&(c.push(b),c.push("")),u+=1}return c.push("---"),c.push(""),c.push(`_${u} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),c.join(`
1454
+ `)}function Dn(e){if(!e.sessionStartedAt)return{allowed:!1,reason:"missing-session-started-at"};if(!e.terminalOpenedAt)return{allowed:!1,reason:"missing-terminal-opened-at"};let t=Date.parse(e.sessionStartedAt),n=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(n)?n-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}U();Q();import{writeFileSync as jw,mkdirSync as Pw,existsSync as Fw}from"node:fs";import{join as fp}from"node:path";var qi=fp(H,"notes"),gp=200,$w=12e3,Uw=800,Bw=8e3;function _p(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function hp(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&typeof n.synopsis=="string"&&typeof n.replaced_at=="string"):[]}catch{return[]}}function Hw(){z(),Fw(qi)||Pw(qi,{recursive:!0})}function Ww(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:_p(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:hp(e.auto_synopsis_history)}}var qw="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function pr(e){let t=h().prepare(`SELECT ${qw} FROM session_notes WHERE session_id = ?`).get(e);return t?Ww(t):null}function Ep(e,t){let n=h(),s=new Date().toISOString(),r=n.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=_p(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:s})),n.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1455
1455
  VALUES (?, ?, ?, ?)
1456
1456
  ON CONFLICT(session_id) DO UPDATE SET
1457
1457
  content = excluded.content,
1458
1458
  updated_at = excluded.updated_at,
1459
- previous_versions = excluded.previous_versions`).run(e,t,s,JSON.stringify(o)),Pw(e,t,s),ur(e)??{session_id:e,content:t,updated_at:s,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function pp(e){let n=h().prepare(`SELECT rowid AS rid, role, content_text
1459
+ previous_versions = excluded.previous_versions`).run(e,t,s,JSON.stringify(o)),Jw(e,t,s),pr(e)??{session_id:e,content:t,updated_at:s,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function bp(e){let n=h().prepare(`SELECT rowid AS rid, role, content_text
1460
1460
  FROM messages
1461
1461
  WHERE session_id = ? AND is_sidechain = 0
1462
1462
  AND content_text IS NOT NULL AND content_text != ''
1463
1463
  AND role IN ('user', 'assistant')
1464
1464
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1465
- LIMIT ?`).all(e,ap);if(n.length===0)throw new Error("no messages available to summarise");let s=Lw,r=[];for(let b of n){if(s<=0)break;let T=(b.content_text??"").slice(0,Cw);r.push({rid:b.rid,role:b.role,content:T}),s-=T.length}r.reverse();let o=n.length===ap||s<=0,a=r.map(b=>`**${b.role}**: ${b.content}`).join(`
1465
+ LIMIT ?`).all(e,gp);if(n.length===0)throw new Error("no messages available to summarise");let s=$w,r=[];for(let b of n){if(s<=0)break;let T=(b.content_text??"").slice(0,Uw);r.push({rid:b.rid,role:b.role,content:T}),s-=T.length}r.reverse();let o=n.length===gp||s<=0,a=r.map(b=>`**${b.role}**: ${b.content}`).join(`
1466
1466
 
1467
1467
  `),c=["You will receive a sampled chronological transcript from a Claude Code session.",o?"The sample is the most recent slice that fits in the context budget; older messages were dropped.":"The full session is included.","","Write a clean markdown synopsis of the session. Use these sections \u2014 omit any that genuinely have nothing to say:","","## Goal","One sentence \u2014 what the user was trying to accomplish.","","## What was done","Bullet list \u2014 concrete actions, code changes, decisions. Skip pleasantries.","","## Key decisions","Bullet list \u2014 non-obvious choices and the reason behind them.","","## Files touched","Bullet list \u2014 file paths mentioned in the conversation. Omit the section if none.","","## Open follow-ups","Bullet list \u2014 anything left undone or flagged for later. Omit the section if none.","","Output ONLY the markdown \u2014 no surrounding prose, no code fences around the whole thing, no closing summary.","","---","",a].join(`
1468
- `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(we(),pt));if(!d())throw new Error("claude CLI not found on PATH");let m=await u(c,[],{});if(!m.success)throw new Error(`claude CLI exited ${m.exitCode}: ${m.stderr.slice(-500)}`);let f=jw(m.stdout);if(!f)throw new Error("claude CLI returned an empty synopsis");return f.slice(0,vw)}function jw(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return s.trim()}}catch{}return t}function mp(e,t){let n=h(),s=new Date().toISOString(),r=Date.now(),o=n.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=up(o?.auto_synopsis_history??null);return o?.auto_synopsis&&o.auto_synopsis!==t&&o.auto_synopsis.length>0&&a.push({synopsis:o.auto_synopsis,replaced_at:s}),o?n.prepare(`UPDATE session_notes
1468
+ `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(we(),pt));if(!d())throw new Error("claude CLI not found on PATH");let m=await u(c,[],{});if(!m.success)throw new Error(`claude CLI exited ${m.exitCode}: ${m.stderr.slice(-500)}`);let f=Xw(m.stdout);if(!f)throw new Error("claude CLI returned an empty synopsis");return f.slice(0,Bw)}function Xw(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return s.trim()}}catch{}return t}function Sp(e,t){let n=h(),s=new Date().toISOString(),r=Date.now(),o=n.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=hp(o?.auto_synopsis_history??null);return o?.auto_synopsis&&o.auto_synopsis!==t&&o.auto_synopsis.length>0&&a.push({synopsis:o.auto_synopsis,replaced_at:s}),o?n.prepare(`UPDATE session_notes
1469
1469
  SET auto_synopsis = ?,
1470
1470
  auto_synopsis_generated_at = ?,
1471
1471
  auto_synopsis_history = ?
1472
1472
  WHERE session_id = ?`).run(t,r,JSON.stringify(a),e):n.prepare(`INSERT INTO session_notes
1473
1473
  (session_id, content, updated_at, previous_versions, auto_synopsis,
1474
1474
  auto_synopsis_generated_at, auto_synopsis_history)
1475
- VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,s,t,r,JSON.stringify(a)),ur(e)}function Pw(e,t,n){try{Iw();let s=cp($i,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${n} -->
1476
- `;xw(s,r+t)}catch(s){console.error("[notes] mirror write failed:",s)}}At();U();Q();import{randomUUID as gp}from"node:crypto";import{writeFileSync as fp,readFileSync as ZI,existsSync as Fw,mkdirSync as $w}from"node:fs";import{join as Ui}from"node:path";var dr=Ui(B,"threads"),Uw=Ui(dr,"index.json");function _p(){z(),Fw(dr)||$w(dr,{recursive:!0})}function Hi(e,t,n){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:n?.project??null,project_count:n?.project_count??0,folder_id:e.folder_id??null}}function hp(e){let t=new Map;if(e.length===0)return t;let n=h(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
1475
+ VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,s,t,r,JSON.stringify(a)),pr(e)}function Jw(e,t,n){try{Hw();let s=fp(qi,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${n} -->
1476
+ `;jw(s,r+t)}catch(s){console.error("[notes] mirror write failed:",s)}}At();U();Q();import{randomUUID as Tp}from"node:crypto";import{writeFileSync as yp,readFileSync as gM,existsSync as Gw,mkdirSync as Yw}from"node:fs";import{join as Xi}from"node:path";var mr=Xi(H,"threads"),zw=Xi(mr,"index.json");function wp(){z(),Gw(mr)||Yw(mr,{recursive:!0})}function Ji(e,t,n){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:n?.project??null,project_count:n?.project_count??0,folder_id:e.folder_id??null}}function Rp(e){let t=new Map;if(e.length===0)return t;let n=h(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
1477
1477
  p.name AS project,
1478
1478
  COUNT(*) AS n,
1479
1479
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
@@ -1481,7 +1481,7 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1481
1481
  LEFT JOIN sessions s ON s.id = te.session_id
1482
1482
  LEFT JOIN projects p ON p.id = s.project_id
1483
1483
  WHERE te.thread_id IN (${s})
1484
- GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let a of r){let c=o.get(a.thread_id);c||(c=[],o.set(a.thread_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(f=>f.project!==null),d=u.length,m=null;u.length>0&&(m=[...u].sort((b,T)=>T.n-b.n||T.origin_n-b.origin_n||(b.project??"").localeCompare(T.project??""))[0].project),t.set(a,{project:m,project_count:d})}return t}function Ep(e){let t=e.auto_title_source;return{thread_id:e.thread_id,session_id:e.session_id,parent_session_id:e.parent_session_id,role:e.role,confidence:e.confidence,source:e.source,added_at:e.added_at,alias:e.alias,auto_title:e.auto_title,auto_title_source:t==="agent"||t==="heuristic"?t:null,alias_source:e.alias?"manual":null,first_user_message:e.first_user_message,project:e.project}}function bp(e){let n=h().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1484
+ GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let a of r){let c=o.get(a.thread_id);c||(c=[],o.set(a.thread_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(f=>f.project!==null),d=u.length,m=null;u.length>0&&(m=[...u].sort((b,T)=>T.n-b.n||T.origin_n-b.origin_n||(b.project??"").localeCompare(T.project??""))[0].project),t.set(a,{project:m,project_count:d})}return t}function kp(e){let t=e.auto_title_source;return{thread_id:e.thread_id,session_id:e.session_id,parent_session_id:e.parent_session_id,role:e.role,confidence:e.confidence,source:e.source,added_at:e.added_at,alias:e.alias,auto_title:e.auto_title,auto_title_source:t==="agent"||t==="heuristic"?t:null,alias_source:e.alias?"manual":null,first_user_message:e.first_user_message,project:e.project}}function Ap(e){let n=h().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1485
1485
  s.auto_title AS auto_title,
1486
1486
  s.auto_title_source AS auto_title_source,
1487
1487
  s.first_user_message AS first_user_message,
@@ -1489,11 +1489,11 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1489
1489
  FROM (SELECT ? AS sid) q
1490
1490
  LEFT JOIN sessions s ON s.id = q.sid
1491
1491
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
1492
- LEFT JOIN projects p ON p.id = s.project_id`).get(e),s=n?.auto_title_source??null;return{alias:n?.alias??null,auto_title:n?.auto_title??null,auto_title_source:s==="agent"||s==="heuristic"?s:null,first_user_message:n?.first_user_message??null,project:n?.project??null}}function Bi(e){let n=h().prepare(`SELECT
1492
+ LEFT JOIN projects p ON p.id = s.project_id`).get(e),s=n?.auto_title_source??null;return{alias:n?.alias??null,auto_title:n?.auto_title??null,auto_title_source:s==="agent"||s==="heuristic"?s:null,first_user_message:n?.first_user_message??null,project:n?.project??null}}function Gi(e){let n=h().prepare(`SELECT
1493
1493
  COUNT(*) AS session_count,
1494
1494
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
1495
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function Ae(e){let t=ce(e);t&&(_p(),fp(Ui(dr,`${e}.json`),JSON.stringify(t,null,2)),Sp())}function Sp(){_p();let e=Wi({includeArchived:!0});fp(Uw,JSON.stringify({threads:e},null,2))}function pr(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=h(),s=gp(),r=new Date().toISOString();n.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(s,t,e.summary?.trim()||null,r),e.originSessionId&&n.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1496
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),Ae(s);let o=ce(s);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Wi(e={}){let t=h(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=hp(s.map(o=>o.id));return s.map(o=>Hi(o,Bi(o.id),r.get(o.id)))}function ce(e){let t=h(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
1495
+ FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function Ae(e){let t=ce(e);t&&(wp(),yp(Xi(mr,`${e}.json`),JSON.stringify(t,null,2)),xp())}function xp(){wp();let e=Yi({includeArchived:!0});yp(zw,JSON.stringify({threads:e},null,2))}function gr(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=h(),s=Tp(),r=new Date().toISOString();n.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(s,t,e.summary?.trim()||null,r),e.originSessionId&&n.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1496
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),Ae(s);let o=ce(s);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Yi(e={}){let t=h(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=Rp(s.map(o=>o.id));return s.map(o=>Ji(o,Gi(o.id),r.get(o.id)))}function ce(e){let t=h(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
1497
1497
  NULLIF(sa.alias, '') AS alias,
1498
1498
  s.auto_title AS auto_title,
1499
1499
  s.auto_title_source AS auto_title_source,
@@ -1504,10 +1504,10 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1504
1504
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1505
1505
  LEFT JOIN projects p ON p.id = s.project_id
1506
1506
  WHERE e.thread_id = ?
1507
- ORDER BY e.added_at ASC`).all(e).map(Ep),r=hp([e]).get(e);return{...Hi(n,Bi(n.id),r),edges:s}}function Tp(e){return h().prepare(`SELECT t.* FROM threads t
1507
+ ORDER BY e.added_at ASC`).all(e).map(kp),r=Rp([e]).get(e);return{...Ji(n,Gi(n.id),r),edges:s}}function Np(e){return h().prepare(`SELECT t.* FROM threads t
1508
1508
  JOIN thread_edges e ON e.thread_id = t.id
1509
1509
  WHERE e.session_id = ? AND t.archived = 0
1510
- ORDER BY t.created_at DESC`).all(e).map(s=>Hi(s,Bi(s.id)))}function mr(e){let t=h();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),a=e.confidence??1,c=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
1510
+ ORDER BY t.created_at DESC`).all(e).map(s=>Ji(s,Gi(s.id)))}function fr(e){let t=h();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),a=e.confidence??1,c=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
1511
1511
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1512
1512
  VALUES (?, ?, ?, ?, ?, ?, ?)
1513
1513
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -1515,22 +1515,22 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1515
1515
  role = excluded.role,
1516
1516
  confidence = excluded.confidence,
1517
1517
  source = excluded.source,
1518
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,s),Ae(e.threadId);let u=bp(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:s,alias:u.alias,auto_title:u.auto_title,auto_title_source:u.auto_title_source,alias_source:u.alias?"manual":null,first_user_message:u.first_user_message,project:u.project}}function yp(e,t){let s=h().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&Ae(e),{removed:s.changes}}function jn(e,t,n){let s=h(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let c=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=n,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=n?"child":"origin";s.prepare(`UPDATE thread_edges
1518
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,s),Ae(e.threadId);let u=Ap(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:s,alias:u.alias,auto_title:u.auto_title,auto_title_source:u.auto_title_source,alias_source:u.alias?"manual":null,first_user_message:u.first_user_message,project:u.project}}function Op(e,t){let s=h().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&Ae(e),{removed:s.changes}}function jn(e,t,n){let s=h(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let c=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=n,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=n?"child":"origin";s.prepare(`UPDATE thread_edges
1519
1519
  SET parent_session_id = ?, role = ?, added_at = ?
1520
- WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),Ae(e);let a=bp(t);return Ep({...r,parent_session_id:n,role:o,added_at:new Date().toISOString(),alias:a.alias,auto_title:a.auto_title,auto_title_source:a.auto_title_source,first_user_message:a.first_user_message,project:a.project})}function wp(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");h().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),Ae(e);let r=ce(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Rp(e){h().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function kp(e){h().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Ap(e){h().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function xp(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=h(),s=new Date().toISOString();n.transaction(()=>{let o=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)n.prepare(`INSERT INTO thread_edges
1520
+ WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),Ae(e);let a=Ap(t);return kp({...r,parent_session_id:n,role:o,added_at:new Date().toISOString(),alias:a.alias,auto_title:a.auto_title,auto_title_source:a.auto_title_source,first_user_message:a.first_user_message,project:a.project})}function Cp(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");h().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),Ae(e);let r=ce(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Lp(e){h().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function vp(e){h().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Ip(e){h().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Mp(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=h(),s=new Date().toISOString();n.transaction(()=>{let o=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)n.prepare(`INSERT INTO thread_edges
1521
1521
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1522
1522
  VALUES (?, ?, ?, ?, ?, ?, ?)
1523
1523
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
1524
1524
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
1525
1525
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
1526
1526
  confidence = MAX(thread_edges.confidence, excluded.confidence),
1527
- source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Ae(t),Sp();let r=ce(t);if(!r)throw new Error("merge destination disappeared");return r}function Np(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=h(),n=new Date().toISOString(),s=gp();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let o of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);a&&(t.prepare(`INSERT INTO thread_edges
1527
+ source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Ae(t),xp();let r=ce(t);if(!r)throw new Error("merge destination disappeared");return r}function Dp(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=h(),n=new Date().toISOString(),s=Tp();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let o of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);a&&(t.prepare(`INSERT INTO thread_edges
1528
1528
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1529
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,o,a.parent_session_id,a.role,a.confidence,a.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),Ae(e.threadId),Ae(s);let r=ce(s);if(!r)throw new Error("split destination disappeared");return r}U();import{execFile as tR}from"node:child_process";import{promisify as nR}from"node:util";import{readlink as sR,readFile as Ip}from"node:fs/promises";import{platform as Er}from"node:os";import{readFileSync as Hw,statSync as Bw}from"node:fs";var Ww=200*1024*1024,fr=.7,_r=.5,Cp=_r,qw=[{maxGapMs:3600*1e3,weight:.7,label:"<1h gap"},{maxGapMs:14400*1e3,weight:.4,label:"<4h gap"},{maxGapMs:1440*60*1e3,weight:.2,label:"<24h gap"}],Xw=["let's commit","lets commit","now commit","inspect the diff","inspect this diff","review the diff","review this diff","diff of all changes","diff of changes","based on what we just did","based on the things done","based on all the things","continue from","continuing from","pick up where","next step","now fix","now lets","now let's","from the previous session","from our last session","from the last session"];function Op(e){if(!e)return null;if(e.startsWith("/")){let n=e.split(" \xB7 ");if(n.length>1)return n[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function Jw(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of qw)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function Gw(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let o=r.toLowerCase();for(let a of Xw)if(o.includes(a))return{weight:t[s],matched:a,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function qi(e,t){if(!e||!t||e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function Yw(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let o=qi(s,r);o>n&&(n=o)}return n}function zw(e,t){let n=qi(e.mean_embedding,t.mean_embedding),s=qi(e.tail_pool,t.head_pool),r=Yw(e.sample_chunks,t.sample_chunks),o=0,a=null;if(n>o&&(o=n,a="mean"),s>o&&(o=s,a="asymmetric"),r>o&&(o=r,a="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:a};let c=(o-.65)/.2*.3;return{weight:Math.round(c*100)/100,cosine:o,mode:a}}function Kw(e,t){return e.cluster_id===null||t.cluster_id===null?{weight:0,same:!1}:e.cluster_id!==t.cluster_id?{weight:0,same:!1}:{weight:.05,same:!0}}function Vw(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let n=0;for(let r of t)e.has(r)&&n++;return n===0?{weight:0,count:0}:{weight:Math.min(.4,n*.1),count:n}}function Qw(e,t){let n=Op(e),s=Op(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function Lp(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Zw(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1530
- `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){n+=.5,s=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Lp(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Lp(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function eR(e,t,n=Cp){if(t.started_at_ms<=e.started_at_ms)return null;let s=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<s)return null;let r=Jw(s,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=Gw(o),c=Vw(e.touched_files,t.touched_files),u=Qw(e.auto_title,t.auto_title),d=zw(e,t),m=Kw(e,t),f=Zw(e,t),b=r.weight+a.weight+c.weight+u.weight+d.weight+m.weight+f.weight;if(b<n)return null;let T=[];if(r.label&&T.push(`temporal ${r.label} (+${r.weight})`),a.matched){let S=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;T.push(`continuation phrase "${a.matched}" in ${S} (+${a.weight})`)}if(c.count>0&&T.push(`${c.count} file${c.count===1?"":"s"} overlap (+${c.weight.toFixed(1)})`),u.brand&&T.push(`shared brand "${u.brand}" (+${u.weight})`),d.weight>0&&d.mode&&T.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),m.same&&T.push(`same cluster (+${m.weight})`),f.weight>0){let S=[];f.pathMatch&&S.push(`opened authored path "${f.pathMatch.split("/").pop()}"`),f.contentMatch&&S.push("verbatim-paste of authored content"),T.push(`doc-authorship: ${S.join(" + ")} (+${f.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,b),signals:{temporal:r.weight,continuation:a.weight,file_overlap:c.weight,same_brand:u.weight,semantic:d.weight,cluster:m.weight,doc_authorship:f.weight},reasons:T}}function qt(e,t=Cp){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=null;for(let a=0;a<s;a++){let c=e[a],u=eR(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&n.push(o)}return n}function vp(e,t){let n=new Map,s=c=>{let u=c;for(;n.get(u)!==u;)u=n.get(u);let d=c;for(;n.get(d)!==u;){let m=n.get(d);n.set(d,u),d=m}return u},r=(c,u)=>{let d=s(c),m=s(u);d!==m&&n.set(d,m)};for(let c of e)n.has(c.parent_id)||n.set(c.parent_id,c.parent_id),n.has(c.child_id)||n.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of n.keys()){let u=s(c),d=o.get(u);d||(d=[],o.set(u,d)),d.push(c)}let a=new Map;for(let c of t)a.set(c.id,c.started_at_ms);return Array.from(o.values()).map(c=>(c.sort((u,d)=>(a.get(u)??0)-(a.get(d)??0)),{rootId:c[0],sessionIds:c}))}function hr(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if(Bw(e).size>Ww)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=Hw(e,"utf8")}catch{return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}let d=0;for(;d<u.length;){let m=u.indexOf(`
1531
- `,d),f=m===-1?u.length:m,b=u.slice(d,f);if(d=m===-1?u.length:m+1,!b.trim())continue;let T;try{T=JSON.parse(b)}catch{continue}let S=T;if(S.type==="user"&&S.message?.role==="user"&&typeof S.message.content=="string"&&o.length<n){let A=S.message.content.trim();A&&o.push(A.length>s?A.slice(0,s):A)}let w=S.message?.content;if(Array.isArray(w))for(let A of w){if(!A||typeof A!="object")continue;let v=A;if(v.type!=="tool_use")continue;let D=v.input??{},X=typeof D.file_path=="string"?D.file_path:null;if(X){let I=gr(X);I&&r.add(I)}if((v.name==="Write"||v.name==="Edit"||v.name==="MultiEdit")&&X){let I=gr(X);I&&a.add(I);let P=typeof D.content=="string"?D.content:typeof D.new_string=="string"?D.new_string:null;P&&P.length>=200&&c.push(P.length>4096?P.slice(0,4096):P)}if(v.name==="Bash"&&typeof D.command=="string")for(let I of D.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let P=gr(I[1]);P&&r.add(P)}if((v.name==="Glob"||v.name==="Grep")&&typeof D.pattern=="string"){let I=gr(D.pattern);I&&!I.includes("*")&&r.add(I)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function gr(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Ji=nR(tR),rR=6,Mp="Active ",Dp=" sessions \u2014 ",oR=6e4;async function iR(){if(Er()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Ji(t,["-eo","pid=,comm="],{timeout:2e3}),s=[];for(let r of n.split(`
1532
- `)){let o=r.trim().match(/^(\d+)\s+(.+)$/);if(!o)continue;let a=Number(o[1]),c=o[2].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&Number.isFinite(a)&&s.push(a)}return s}catch{continue}return[]}async function aR(e){let t=Er();if(t==="linux")try{return(await sR(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:s}=await Ji(n,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of s.split(`
1533
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function cR(e){let t=Er();if(t==="linux")try{let n=await Ip(`/proc/${e}/stat`,"utf8"),s=n.lastIndexOf(")");if(s===-1)return null;let r=n.slice(s+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await Ip("/proc/uptime","utf8"),c=Number(a.split(/\s+/)[0]);return Number.isFinite(c)?Date.now()-c*1e3+o/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Ji(n,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(s.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function lR(e,t){let n=await iR();if(n.length===0)return null;let s=e.replace(/\/+$/,""),r=[];for(let a of n){let c=await aR(a);if(c&&(c===s||c.startsWith(s+"/"))){let u=await cR(a);r.push({pid:a,startMs:u})}}if(r.length===0)return new Set;let o=new Set;for(let{startMs:a}of r){if(a==null)continue;let c=null,u=oR;for(let d of t){if(o.has(d.session_id)||!d.started_at)continue;let m=Date.parse(d.started_at);if(!Number.isFinite(m))continue;let f=Math.abs(m-a);f<u&&(u=f,c=d)}c&&o.add(c.session_id)}return o}function uR(e){let n=h().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!n)throw new Error(`project ${e} not found`);return n}function dR(e,t){let n=h(),s=`${Mp}${t}${Dp}%`,r=n.prepare(`SELECT t.id
1529
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,o,a.parent_session_id,a.role,a.confidence,a.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),Ae(e.threadId),Ae(s);let r=ce(s);if(!r)throw new Error("split destination disappeared");return r}U();import{execFile as uR}from"node:child_process";import{promisify as dR}from"node:util";import{readlink as pR,readFile as Up}from"node:fs/promises";import{platform as Sr}from"node:os";import{readFileSync as Kw,statSync as Vw}from"node:fs";var Qw=200*1024*1024,hr=.7,Er=.5,Fp=Er,Zw=[{maxGapMs:3600*1e3,weight:.7,label:"<1h gap"},{maxGapMs:14400*1e3,weight:.4,label:"<4h gap"},{maxGapMs:1440*60*1e3,weight:.2,label:"<24h gap"}],eR=["let's commit","lets commit","now commit","inspect the diff","inspect this diff","review the diff","review this diff","diff of all changes","diff of changes","based on what we just did","based on the things done","based on all the things","continue from","continuing from","pick up where","next step","now fix","now lets","now let's","from the previous session","from our last session","from the last session"];function jp(e){if(!e)return null;if(e.startsWith("/")){let n=e.split(" \xB7 ");if(n.length>1)return n[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function tR(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of Zw)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function nR(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let o=r.toLowerCase();for(let a of eR)if(o.includes(a))return{weight:t[s],matched:a,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function zi(e,t){if(!e||!t||e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function sR(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let o=zi(s,r);o>n&&(n=o)}return n}function rR(e,t){let n=zi(e.mean_embedding,t.mean_embedding),s=zi(e.tail_pool,t.head_pool),r=sR(e.sample_chunks,t.sample_chunks),o=0,a=null;if(n>o&&(o=n,a="mean"),s>o&&(o=s,a="asymmetric"),r>o&&(o=r,a="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:a};let c=(o-.65)/.2*.3;return{weight:Math.round(c*100)/100,cosine:o,mode:a}}function oR(e,t){return e.cluster_id===null||t.cluster_id===null?{weight:0,same:!1}:e.cluster_id!==t.cluster_id?{weight:0,same:!1}:{weight:.05,same:!0}}function iR(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let n=0;for(let r of t)e.has(r)&&n++;return n===0?{weight:0,count:0}:{weight:Math.min(.4,n*.1),count:n}}function aR(e,t){let n=jp(e),s=jp(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function Pp(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function cR(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1530
+ `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){n+=.5,s=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Pp(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Pp(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function lR(e,t,n=Fp){if(t.started_at_ms<=e.started_at_ms)return null;let s=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<s)return null;let r=tR(s,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=nR(o),c=iR(e.touched_files,t.touched_files),u=aR(e.auto_title,t.auto_title),d=rR(e,t),m=oR(e,t),f=cR(e,t),b=r.weight+a.weight+c.weight+u.weight+d.weight+m.weight+f.weight;if(b<n)return null;let T=[];if(r.label&&T.push(`temporal ${r.label} (+${r.weight})`),a.matched){let S=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;T.push(`continuation phrase "${a.matched}" in ${S} (+${a.weight})`)}if(c.count>0&&T.push(`${c.count} file${c.count===1?"":"s"} overlap (+${c.weight.toFixed(1)})`),u.brand&&T.push(`shared brand "${u.brand}" (+${u.weight})`),d.weight>0&&d.mode&&T.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),m.same&&T.push(`same cluster (+${m.weight})`),f.weight>0){let S=[];f.pathMatch&&S.push(`opened authored path "${f.pathMatch.split("/").pop()}"`),f.contentMatch&&S.push("verbatim-paste of authored content"),T.push(`doc-authorship: ${S.join(" + ")} (+${f.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,b),signals:{temporal:r.weight,continuation:a.weight,file_overlap:c.weight,same_brand:u.weight,semantic:d.weight,cluster:m.weight,doc_authorship:f.weight},reasons:T}}function qt(e,t=Fp){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=null;for(let a=0;a<s;a++){let c=e[a],u=lR(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&n.push(o)}return n}function $p(e,t){let n=new Map,s=c=>{let u=c;for(;n.get(u)!==u;)u=n.get(u);let d=c;for(;n.get(d)!==u;){let m=n.get(d);n.set(d,u),d=m}return u},r=(c,u)=>{let d=s(c),m=s(u);d!==m&&n.set(d,m)};for(let c of e)n.has(c.parent_id)||n.set(c.parent_id,c.parent_id),n.has(c.child_id)||n.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of n.keys()){let u=s(c),d=o.get(u);d||(d=[],o.set(u,d)),d.push(c)}let a=new Map;for(let c of t)a.set(c.id,c.started_at_ms);return Array.from(o.values()).map(c=>(c.sort((u,d)=>(a.get(u)??0)-(a.get(d)??0)),{rootId:c[0],sessionIds:c}))}function br(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if(Vw(e).size>Qw)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=Kw(e,"utf8")}catch{return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}let d=0;for(;d<u.length;){let m=u.indexOf(`
1531
+ `,d),f=m===-1?u.length:m,b=u.slice(d,f);if(d=m===-1?u.length:m+1,!b.trim())continue;let T;try{T=JSON.parse(b)}catch{continue}let S=T;if(S.type==="user"&&S.message?.role==="user"&&typeof S.message.content=="string"&&o.length<n){let A=S.message.content.trim();A&&o.push(A.length>s?A.slice(0,s):A)}let R=S.message?.content;if(Array.isArray(R))for(let A of R){if(!A||typeof A!="object")continue;let v=A;if(v.type!=="tool_use")continue;let D=v.input??{},X=typeof D.file_path=="string"?D.file_path:null;if(X){let I=_r(X);I&&r.add(I)}if((v.name==="Write"||v.name==="Edit"||v.name==="MultiEdit")&&X){let I=_r(X);I&&a.add(I);let P=typeof D.content=="string"?D.content:typeof D.new_string=="string"?D.new_string:null;P&&P.length>=200&&c.push(P.length>4096?P.slice(0,4096):P)}if(v.name==="Bash"&&typeof D.command=="string")for(let I of D.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let P=_r(I[1]);P&&r.add(P)}if((v.name==="Glob"||v.name==="Grep")&&typeof D.pattern=="string"){let I=_r(D.pattern);I&&!I.includes("*")&&r.add(I)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function _r(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Vi=dR(uR),mR=6,Bp="Active ",Hp=" sessions \u2014 ",gR=6e4;async function fR(){if(Sr()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Vi(t,["-eo","pid=,comm="],{timeout:2e3}),s=[];for(let r of n.split(`
1532
+ `)){let o=r.trim().match(/^(\d+)\s+(.+)$/);if(!o)continue;let a=Number(o[1]),c=o[2].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&Number.isFinite(a)&&s.push(a)}return s}catch{continue}return[]}async function _R(e){let t=Sr();if(t==="linux")try{return(await pR(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:s}=await Vi(n,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of s.split(`
1533
+ `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function hR(e){let t=Sr();if(t==="linux")try{let n=await Up(`/proc/${e}/stat`,"utf8"),s=n.lastIndexOf(")");if(s===-1)return null;let r=n.slice(s+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await Up("/proc/uptime","utf8"),c=Number(a.split(/\s+/)[0]);return Number.isFinite(c)?Date.now()-c*1e3+o/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Vi(n,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(s.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function ER(e,t){let n=await fR();if(n.length===0)return null;let s=e.replace(/\/+$/,""),r=[];for(let a of n){let c=await _R(a);if(c&&(c===s||c.startsWith(s+"/"))){let u=await hR(a);r.push({pid:a,startMs:u})}}if(r.length===0)return new Set;let o=new Set;for(let{startMs:a}of r){if(a==null)continue;let c=null,u=gR;for(let d of t){if(o.has(d.session_id)||!d.started_at)continue;let m=Date.parse(d.started_at);if(!Number.isFinite(m))continue;let f=Math.abs(m-a);f<u&&(u=f,c=d)}c&&o.add(c.session_id)}return o}function bR(e){let n=h().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!n)throw new Error(`project ${e} not found`);return n}function SR(e,t){let n=h(),s=`${Bp}${t}${Hp}%`,r=n.prepare(`SELECT t.id
1534
1534
  FROM threads t
1535
1535
  WHERE t.archived = 0
1536
1536
  AND t.name LIKE ?
@@ -1541,7 +1541,7 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1541
1541
  WHERE s.project_id = ?
1542
1542
  AND t.archived = 0
1543
1543
  AND t.name LIKE ?
1544
- LIMIT 1`).get(e,s);return o?ce(o.id):null}function jp(e){let t=e?new Date(e):new Date,n=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${s}-${r}`}function Xi(e,t){let n=h(),s=t>0,r=s?Date.now()-t*60*60*1e3:0;return s?n.prepare(`SELECT s.id AS session_id,
1544
+ LIMIT 1`).get(e,s);return o?ce(o.id):null}function Wp(e){let t=e?new Date(e):new Date,n=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${s}-${r}`}function Ki(e,t){let n=h(),s=t>0,r=s?Date.now()-t*60*60*1e3:0;return s?n.prepare(`SELECT s.id AS session_id,
1545
1545
  sa.alias AS alias,
1546
1546
  s.auto_title AS auto_title,
1547
1547
  s.first_user_message AS first_user_message,
@@ -1561,19 +1561,19 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1561
1561
  FROM sessions s
1562
1562
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1563
1563
  WHERE s.project_id = ?
1564
- ORDER BY s.started_at ASC`).all(e)}function pR(e){let t=h(),n=[];for(let s of e){if(!s.started_at)continue;let r=Date.parse(s.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(s.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=hr(o.file_path,{maxUserMessages:7});n.push({id:s.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:s.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:s.auto_title,touched_files:c.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:c.authored_paths,authored_content:c.authored_content})}return n}function mR(e){let n=h().prepare(`SELECT session_id, parent_session_id, source
1564
+ ORDER BY s.started_at ASC`).all(e)}function TR(e){let t=h(),n=[];for(let s of e){if(!s.started_at)continue;let r=Date.parse(s.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(s.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=br(o.file_path,{maxUserMessages:7});n.push({id:s.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:s.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:s.auto_title,touched_files:c.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:c.authored_paths,authored_content:c.authored_content})}return n}function yR(e){let n=h().prepare(`SELECT session_id, parent_session_id, source
1565
1565
  FROM thread_edges
1566
- WHERE thread_id = ?`).all(e),s=new Map;for(let r of n)s.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return s}async function Pp(e,t={}){let n=uR(e),s=t.windowHours??rR,r=t.scoreThreshold??_r,o=t.useLivePids??!0,a=[],c=[];if(o&&n.decoded_path){let w=Xi(e,0),A=await lR(n.decoded_path,w);if(A===null){let D=Er()==="win32"?"Windows live-PID detection is not yet supported \u2014 falling back to the rolling mtime window.":"No live `claude` processes detected \u2014 falling back to the rolling mtime window. Output may include sessions that are no longer open.";a.push(D),c=Xi(e,s)}else A.size===0?(a.push(`No active terminals open in ${n.name} (cwd=${n.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=w.filter(v=>A.has(v.session_id))}else c=Xi(e,s);c.length===0&&!a.length&&a.push(`No active sessions in ${n.name} within the last ${s}h.`);let u=dR(e,n.name),d=new Set(u?u.edges.map(w=>w.session_id):[]),m=c.filter(w=>!d.has(w.session_id)),f=pR(c);f.sort((w,A)=>w.started_at_ms-A.started_at_ms);let b=qt(f,r),T=u?u.edges.filter(w=>w.source!=="auto-active"&&(w.parent_session_id||w.role==="origin")).map(w=>({session_id:w.session_id,parent_session_id:w.parent_session_id})):[],S=u?u.name:`${Mp}${n.name}${Dp}${jp(t.todayIso)}`;return{project:n,thread:{id:u?.id??null,name:S,exists:!!u,existing_session_count:u?.edges.length??0},candidates:c,proposed_additions:m,proposed_edges:b,preserved_manual_edges:T,warnings:a}}function Fp(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},n;e.thread.exists&&e.thread.id?n=e.thread.id:n=pr({name:e.thread.name,summary:`Auto-captured by sync-active on ${jp()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=n;let s=mR(n);for(let a of e.candidates)s.has(a.session_id)||(mr({threadId:n,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,s.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=s.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&s.has(a.parent_id))try{jn(n,a.child_id,a.parent_id),t.edges_set++,s.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=h().prepare(`SELECT session_id FROM thread_edges
1566
+ WHERE thread_id = ?`).all(e),s=new Map;for(let r of n)s.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return s}async function qp(e,t={}){let n=bR(e),s=t.windowHours??mR,r=t.scoreThreshold??Er,o=t.useLivePids??!0,a=[],c=[];if(o&&n.decoded_path){let R=Ki(e,0),A=await ER(n.decoded_path,R);if(A===null){let D=Sr()==="win32"?"Windows live-PID detection is not yet supported \u2014 falling back to the rolling mtime window.":"No live `claude` processes detected \u2014 falling back to the rolling mtime window. Output may include sessions that are no longer open.";a.push(D),c=Ki(e,s)}else A.size===0?(a.push(`No active terminals open in ${n.name} (cwd=${n.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=R.filter(v=>A.has(v.session_id))}else c=Ki(e,s);c.length===0&&!a.length&&a.push(`No active sessions in ${n.name} within the last ${s}h.`);let u=SR(e,n.name),d=new Set(u?u.edges.map(R=>R.session_id):[]),m=c.filter(R=>!d.has(R.session_id)),f=TR(c);f.sort((R,A)=>R.started_at_ms-A.started_at_ms);let b=qt(f,r),T=u?u.edges.filter(R=>R.source!=="auto-active"&&(R.parent_session_id||R.role==="origin")).map(R=>({session_id:R.session_id,parent_session_id:R.parent_session_id})):[],S=u?u.name:`${Bp}${n.name}${Hp}${Wp(t.todayIso)}`;return{project:n,thread:{id:u?.id??null,name:S,exists:!!u,existing_session_count:u?.edges.length??0},candidates:c,proposed_additions:m,proposed_edges:b,preserved_manual_edges:T,warnings:a}}function Xp(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},n;e.thread.exists&&e.thread.id?n=e.thread.id:n=gr({name:e.thread.name,summary:`Auto-captured by sync-active on ${Wp()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=n;let s=yR(n);for(let a of e.candidates)s.has(a.session_id)||(fr({threadId:n,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,s.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=s.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&s.has(a.parent_id))try{jn(n,a.child_id,a.parent_id),t.edges_set++,s.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=h().prepare(`SELECT session_id FROM thread_edges
1567
1567
  WHERE thread_id = ?
1568
1568
  AND source = 'auto-active'
1569
1569
  AND parent_session_id IS NULL
1570
- AND role = 'child'`).all(n);for(let a of o)try{jn(n,a.session_id,null)}catch{}return t}U();Q();import{randomUUID as gR}from"node:crypto";import{writeFileSync as fR}from"node:fs";import{join as _R}from"node:path";var hR=_R(B,"thread-folders.json");function $p(e){return{id:e.id,name:e.name,parent_folder_id:e.parent_folder_id,project_scope:e.project_scope,created_at:e.created_at,archived:e.archived===1,sort_order:e.sort_order}}function Pn(){try{z();let e=Gi({includeArchived:!0});fR(hR,JSON.stringify({folders:e},null,2))}catch{}}function Gi(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return h().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map($p)}function bt(e){let t=h().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?$p(t):null}function Up(e){let t=e.name.trim();if(!t)throw new Error("folder name cannot be empty");if(t.length>200)throw new Error("folder name too long (200 char max)");let n=e.parentFolderId??null,s=e.projectScope??null;if(n){let c=bt(n);if(!c)throw new Error(`parent folder ${n} not found`);s=c.project_scope}let r=gR(),o=new Date().toISOString(),a=Hp(n,s);return h().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1571
- VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,n,s,o,a),Pn(),{id:r,name:t,parent_folder_id:n,project_scope:s,created_at:o,archived:!1,sort_order:a}}function Hp(e,t){return h().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1570
+ AND role = 'child'`).all(n);for(let a of o)try{jn(n,a.session_id,null)}catch{}return t}U();Q();import{randomUUID as wR}from"node:crypto";import{writeFileSync as RR}from"node:fs";import{join as kR}from"node:path";var AR=kR(H,"thread-folders.json");function Jp(e){return{id:e.id,name:e.name,parent_folder_id:e.parent_folder_id,project_scope:e.project_scope,created_at:e.created_at,archived:e.archived===1,sort_order:e.sort_order}}function Pn(){try{z();let e=Qi({includeArchived:!0});RR(AR,JSON.stringify({folders:e},null,2))}catch{}}function Qi(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return h().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(Jp)}function bt(e){let t=h().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?Jp(t):null}function Gp(e){let t=e.name.trim();if(!t)throw new Error("folder name cannot be empty");if(t.length>200)throw new Error("folder name too long (200 char max)");let n=e.parentFolderId??null,s=e.projectScope??null;if(n){let c=bt(n);if(!c)throw new Error(`parent folder ${n} not found`);s=c.project_scope}let r=wR(),o=new Date().toISOString(),a=Yp(n,s);return h().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1571
+ VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,n,s,o,a),Pn(),{id:r,name:t,parent_folder_id:n,project_scope:s,created_at:o,archived:!1,sort_order:a}}function Yp(e,t){return h().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1572
1572
  FROM thread_folders
1573
1573
  WHERE parent_folder_id IS ?
1574
- AND project_scope IS ?`).get(e,t)?.next??0}function Bp(e,t){let n=t.trim();if(!n)throw new Error("folder name cannot be empty");if(n.length>200)throw new Error("folder name too long (200 char max)");let s=bt(e);if(!s)throw new Error(`folder ${e} not found`);return h().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(n,e),Pn(),{...s,name:n}}function Wp(e,t){let n=bt(e);if(!n)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let s=n.project_scope;if(t!==null){let o=bt(t);if(!o)throw new Error(`parent folder ${t} not found`);let a=o.parent_folder_id,c=0;for(;a!==null&&c<1024;){if(a===e)throw new Error("cannot move a folder into one of its own descendants (cycle)");let u=bt(a);if(!u)break;a=u.parent_folder_id,c++}s=o.project_scope}let r=Hp(t,s);return h().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,s,r,e),Pn(),{...n,parent_folder_id:t,project_scope:s,sort_order:r}}function qp(e,t,n){if(!Array.isArray(n)||n.length===0)throw new Error("ordered_ids must be a non-empty array");let s=new Set;for(let d of n){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(s.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);s.add(d)}let r=h(),o=r.prepare(`SELECT id FROM thread_folders
1574
+ AND project_scope IS ?`).get(e,t)?.next??0}function zp(e,t){let n=t.trim();if(!n)throw new Error("folder name cannot be empty");if(n.length>200)throw new Error("folder name too long (200 char max)");let s=bt(e);if(!s)throw new Error(`folder ${e} not found`);return h().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(n,e),Pn(),{...s,name:n}}function Kp(e,t){let n=bt(e);if(!n)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let s=n.project_scope;if(t!==null){let o=bt(t);if(!o)throw new Error(`parent folder ${t} not found`);let a=o.parent_folder_id,c=0;for(;a!==null&&c<1024;){if(a===e)throw new Error("cannot move a folder into one of its own descendants (cycle)");let u=bt(a);if(!u)break;a=u.parent_folder_id,c++}s=o.project_scope}let r=Yp(t,s);return h().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,s,r,e),Pn(),{...n,parent_folder_id:t,project_scope:s,sort_order:r}}function Vp(e,t,n){if(!Array.isArray(n)||n.length===0)throw new Error("ordered_ids must be a non-empty array");let s=new Set;for(let d of n){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(s.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);s.add(d)}let r=h(),o=r.prepare(`SELECT id FROM thread_folders
1575
1575
  WHERE parent_folder_id IS ?
1576
- AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==n.length)throw new Error(`ordered_ids length ${n.length} does not match sibling count ${a.size}`);for(let d of n)if(!a.has(d))throw new Error(`folder ${d} is not in the named sibling bucket`);let c=r.prepare("UPDATE thread_folders SET sort_order = ? WHERE id = ?");r.transaction(d=>{d.forEach((m,f)=>c.run(f*100,m))})(n),Pn()}function Xp(e){if(!bt(e))throw new Error(`folder ${e} not found`);h().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),Pn()}function Jp(e,t){if(t!==null&&!bt(t))throw new Error(`folder ${t} not found`);if(h().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function Gp(e,t){let n=new Set,s=[];for(let r of t){if(n.has(r.id))continue;let o=null,a="";if(r.source_session_id===e)o="outbound",a=r.target_session_id;else if(r.target_session_id===e)o="inbound",a=r.source_session_id;else continue;n.add(r.id),s.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return s.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),s}U();var ER=4e3,bR=2,SR=30,TR=.2,yR={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function br(e){return e?Math.ceil(e.length/4):0}function Yp(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/SR);return Math.max(TR,t)}function zp(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function Kp(e){return h().prepare(`SELECT s.id,
1576
+ AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==n.length)throw new Error(`ordered_ids length ${n.length} does not match sibling count ${a.size}`);for(let d of n)if(!a.has(d))throw new Error(`folder ${d} is not in the named sibling bucket`);let c=r.prepare("UPDATE thread_folders SET sort_order = ? WHERE id = ?");r.transaction(d=>{d.forEach((m,f)=>c.run(f*100,m))})(n),Pn()}function Qp(e){if(!bt(e))throw new Error(`folder ${e} not found`);h().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),Pn()}function Zp(e,t){if(t!==null&&!bt(t))throw new Error(`folder ${t} not found`);if(h().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function em(e,t){let n=new Set,s=[];for(let r of t){if(n.has(r.id))continue;let o=null,a="";if(r.source_session_id===e)o="outbound",a=r.target_session_id;else if(r.target_session_id===e)o="inbound",a=r.source_session_id;else continue;n.add(r.id),s.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return s.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),s}U();var xR=4e3,NR=2,OR=30,CR=.2,LR={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function Tr(e){return e?Math.ceil(e.length/4):0}function tm(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/OR);return Math.max(CR,t)}function nm(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function sm(e){return h().prepare(`SELECT s.id,
1577
1577
  NULLIF(sa.alias, '') AS alias,
1578
1578
  s.auto_title,
1579
1579
  s.auto_title_source,
@@ -1584,72 +1584,72 @@ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1584
1584
  FROM sessions s
1585
1585
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1586
1586
  LEFT JOIN projects p ON p.id = s.project_id
1587
- WHERE s.id = ?`).get(e)??null}function Vp(e){let n=h().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!n||!n.summary)return null;let s=n.summary.trim();return s.length>0?s:null}function Qp(e){let t=e.alias?.trim(),n=e.auto_title?.trim(),s=e.first_user_message?.trim();return n&&e.auto_title_source==="agent"?n:t||n||(s?s.slice(0,80):e.id.slice(0,8))}function wR(e){let n=h().prepare(`SELECT id, auto_title, started_at
1587
+ WHERE s.id = ?`).get(e)??null}function rm(e){let n=h().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!n||!n.summary)return null;let s=n.summary.trim();return s.length>0?s:null}function om(e){let t=e.alias?.trim(),n=e.auto_title?.trim(),s=e.first_user_message?.trim();return n&&e.auto_title_source==="agent"?n:t||n||(s?s.slice(0,80):e.id.slice(0,8))}function vR(e){let n=h().prepare(`SELECT id, auto_title, started_at
1588
1588
  FROM sessions
1589
1589
  WHERE project_id = ?
1590
- ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,o=[];for(let b of n){if(!b.auto_title||!b.auto_title.startsWith("/")){o.push({id:b.id,brand:null,skill:null});continue}let T=b.auto_title.split(" \xB7 "),S=T[0].trim(),w=T.length>1?T.slice(1).join(" \xB7 ").trim():null;o.push({id:b.id,brand:w||null,skill:S||null}),w&&s.add(w),S&&r.add(S)}let a=[...s].sort(),c=new Map;a.forEach((b,T)=>c.set(b,T));let u=[...r].sort(),d=new Map;u.forEach((b,T)=>d.set(b,T));let m=new Map,f=new Map;for(let b of o){if(!b.brand||!b.skill)continue;let T=c.get(b.brand),S=d.get(b.skill);if(T===void 0||S===void 0)continue;let w=`${T}.${S}`,A=(m.get(w)??0)+1;m.set(w,A),f.set(b.id,`${T}.${S}.${A}`)}return{byId:f}}function RR(e){return{table:e!==null?wR(e):null,originProjectId:e,cache:new Map}}function Sr(e,t){let n=e.cache.get(t);if(n)return n;let s=Kp(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:s.id,title:Qp(s),decimal:r,summary:Vp(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,o),o}function kR(e,t){let s=h().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1590
+ ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,o=[];for(let b of n){if(!b.auto_title||!b.auto_title.startsWith("/")){o.push({id:b.id,brand:null,skill:null});continue}let T=b.auto_title.split(" \xB7 "),S=T[0].trim(),R=T.length>1?T.slice(1).join(" \xB7 ").trim():null;o.push({id:b.id,brand:R||null,skill:S||null}),R&&s.add(R),S&&r.add(S)}let a=[...s].sort(),c=new Map;a.forEach((b,T)=>c.set(b,T));let u=[...r].sort(),d=new Map;u.forEach((b,T)=>d.set(b,T));let m=new Map,f=new Map;for(let b of o){if(!b.brand||!b.skill)continue;let T=c.get(b.brand),S=d.get(b.skill);if(T===void 0||S===void 0)continue;let R=`${T}.${S}`,A=(m.get(R)??0)+1;m.set(R,A),f.set(b.id,`${T}.${S}.${A}`)}return{byId:f}}function IR(e){return{table:e!==null?vR(e):null,originProjectId:e,cache:new Map}}function yr(e,t){let n=e.cache.get(t);if(n)return n;let s=sm(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:s.id,title:om(s),decimal:r,summary:rm(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,o),o}function MR(e,t){let s=h().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1591
1591
  FROM thread_edges te
1592
1592
  WHERE te.session_id = ?
1593
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let a=Sr(e,o.pid);a&&r.push(a)}return r}function AR(e,t){let s=h().prepare(`SELECT DISTINCT te.session_id AS sid
1593
+ AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let a=yr(e,o.pid);a&&r.push(a)}return r}function DR(e,t){let s=h().prepare(`SELECT DISTINCT te.session_id AS sid
1594
1594
  FROM thread_edges te
1595
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let a=Sr(e,o.sid);a&&r.push(a)}return r}function Zp(e){let t=yR[e.linkType]??.5,n=Xt(e.confidence),s=t*n,r=Yp(e.daysApart),o=e.embeddingCosine??.5,a=Xt(e.pagerank);if(e.scoring==="pagerank")return Xt(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Xt(s):Xt(o);let c=.35*s+.2*r+.2*o+.25*a;return Xt(c)}function Xt(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function xR(e,t,n,s,r){let o=new Map;function a(c,u){if(c===u)return;let d=o.get(c);d||(d=new Set,o.set(c,d)),d.add(u)}for(let c of t)a(c.source_session_id,c.target_session_id),a(c.target_session_id,c.source_session_id);for(let c of n)a(e,c.session_id);for(let c of n)a(c.session_id,e);for(let c of s)a(e,c.session_id);for(let c of s)a(c.session_id,e);if(r>1){let c=new Set([e]),u=new Set([e]);for(let d=1;d<r;d++){let m=new Set;for(let f of c){let b=o.get(f);if(b)for(let T of b){if(u.has(T))continue;let S=gn(T).filter(w=>w.approved);for(let w of S)a(w.source_session_id,w.target_session_id),a(w.target_session_id,w.source_session_id);u.add(T),m.add(T)}}if(m.size===0)break;for(let f of m)c.add(f)}}return{edges:o}}function NR(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,a=new Map(r.map(d=>[d,o]));for(let d=0;d<n;d++){let m=new Map(r.map(f=>[f,(1-s)/r.length]));for(let f of r){let b=e.edges.get(f);if(!b||b.size===0)continue;let T=(a.get(f)??0)/b.size;for(let S of b)m.set(S,(m.get(S)??0)+s*T)}a=m}let c=0;for(let d of a.values())d>c&&(c=d);if(c<=0)return a;let u=new Map;for(let[d,m]of a)u.set(d,m/c);return u}var em=240;function tm(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function OR(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let o=tm(e.summary,em);return`${r}
1596
- ${o}`}return r}function LR(e,t,n){let s=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(s.push(c),o+=br(c),e.summary){let u=tm(e.summary,em*4);s.push(u),o+=br(u)}s.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,m=br(d),f=[],b=0;for(let T of u.refs){let S=OR(T),w=br(S);if(o+m+b+w>n){r.push({session_id:T.session_id,title:T.title,decimal:T.decimal,summary:T.summary,project:T.project,started_at:T.started_at});continue}f.push(S),b+=w}if(f.length>0){s.push(d);for(let T of f)s.push(T);s.push(""),o+=m+b}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
1595
+ WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let a=yr(e,o.sid);a&&r.push(a)}return r}function im(e){let t=LR[e.linkType]??.5,n=Xt(e.confidence),s=t*n,r=tm(e.daysApart),o=e.embeddingCosine??.5,a=Xt(e.pagerank);if(e.scoring==="pagerank")return Xt(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Xt(s):Xt(o);let c=.35*s+.2*r+.2*o+.25*a;return Xt(c)}function Xt(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function jR(e,t,n,s,r){let o=new Map;function a(c,u){if(c===u)return;let d=o.get(c);d||(d=new Set,o.set(c,d)),d.add(u)}for(let c of t)a(c.source_session_id,c.target_session_id),a(c.target_session_id,c.source_session_id);for(let c of n)a(e,c.session_id);for(let c of n)a(c.session_id,e);for(let c of s)a(e,c.session_id);for(let c of s)a(c.session_id,e);if(r>1){let c=new Set([e]),u=new Set([e]);for(let d=1;d<r;d++){let m=new Set;for(let f of c){let b=o.get(f);if(b)for(let T of b){if(u.has(T))continue;let S=gn(T).filter(R=>R.approved);for(let R of S)a(R.source_session_id,R.target_session_id),a(R.target_session_id,R.source_session_id);u.add(T),m.add(T)}}if(m.size===0)break;for(let f of m)c.add(f)}}return{edges:o}}function PR(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,a=new Map(r.map(d=>[d,o]));for(let d=0;d<n;d++){let m=new Map(r.map(f=>[f,(1-s)/r.length]));for(let f of r){let b=e.edges.get(f);if(!b||b.size===0)continue;let T=(a.get(f)??0)/b.size;for(let S of b)m.set(S,(m.get(S)??0)+s*T)}a=m}let c=0;for(let d of a.values())d>c&&(c=d);if(c<=0)return a;let u=new Map;for(let[d,m]of a)u.set(d,m/c);return u}var am=240;function cm(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function FR(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let o=cm(e.summary,am);return`${r}
1596
+ ${o}`}return r}function $R(e,t,n){let s=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(s.push(c),o+=Tr(c),e.summary){let u=cm(e.summary,am*4);s.push(u),o+=Tr(u)}s.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,m=Tr(d),f=[],b=0;for(let T of u.refs){let S=FR(T),R=Tr(S);if(o+m+b+R>n){r.push({session_id:T.session_id,title:T.title,decimal:T.decimal,summary:T.summary,project:T.project,started_at:T.started_at});continue}f.push(S),b+=R}if(f.length>0){s.push(d);for(let T of f)s.push(T);s.push(""),o+=m+b}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
1597
1597
  `)+`
1598
- `,budgetUsed:o,truncated:r}}function CR(e,t,n,s,r,o){let a=[];for(let c of n){if(s&&!s.has(c.link_type))continue;let u=null;if(c.source_session_id===t.session_id?u=c.target_session_id:c.target_session_id===t.session_id&&(u=c.source_session_id),!u)continue;let d=Sr(e,u);if(!d)continue;let m=zp(t.started_at,d.started_at),f=Zp({confidence:c.confidence,linkType:c.link_type,daysApart:m,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:f,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(m)}d apart`,link_type:c.link_type})}return a}function Tr(e,t={}){let n=Math.max(100,Math.floor(t.budget??ER)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??bR)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=Kp(e);if(!u)throw new Error(`session not found: ${e}`);let d=RR(u.project_id),m={session_id:u.id,title:Qp(u),decimal:d.table?.byId.get(u.id)??null,summary:Vp(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,m);let f=kR(d,e),b=AR(d,e),T=gn(e).filter($=>$.approved).filter($=>!c||c.has($.link_type)).filter($=>o||$.link_type!=="wiki_link"),S=xR(e,T,f,b,r),w=NR(S),A=[],v=[],D=[],X=[];for(let $ of T){let ne=$.source_session_id===e?$.target_session_id:$.source_session_id,i=Sr(d,ne);if(!i)continue;let l=zp(m.started_at,i.started_at),p=Zp({confidence:$.confidence,linkType:$.link_type,daysApart:l,embeddingCosine:null,pagerank:w.get(ne)??0,scoring:s}),g=Yp(l),_=`${$.link_type} confidence=${$.confidence.toFixed(2)} recency=${g.toFixed(2)} (${Math.round(l)}d apart)`,E={...i,score:p,evidence:_,link_type:$.link_type};$.link_type==="citation"?A.push(E):$.link_type==="similar"?v.push(E):$.link_type==="wiki_link"?X.push(E):D.push(E)}if(a){let $=wt({sourceSessionId:e,status:"pending",limit:100}),ne=wt({targetSessionId:e,status:"pending",limit:100}),i=[...$,...ne],l=new Set,p=i.filter(_=>l.has(_.id)?!1:(l.add(_.id),!0)),g=CR(d,m,p,c,s,w);for(let _ of g)_.link_type==="citation"?A.push(_):_.link_type==="similar"?v.push(_):_.link_type==="wiki_link"?X.push(_):D.push(_)}let I=($,ne)=>ne.score-$.score;A.sort(I),v.sort(I),D.sort(I),X.sort(I);let te=LR(m,[{heading:"Parents",refs:f},{heading:"Children",refs:b},{heading:"Citations (approved)",refs:A},{heading:"Similar sessions",refs:v},{heading:"Cousins (skill track + temporal)",refs:D},{heading:"Wiki links (manual)",refs:X}],n);return{origin:m,parents:f,children:b,citations:A,similar:v,cousins:D,wikiLinks:X,bundle:te.bundle,budgetUsed:te.budgetUsed,budgetRemaining:Math.max(0,n-te.budgetUsed),truncated:te.truncated}}import{randomUUID as FR}from"node:crypto";var vR=50;function IR(e){if(e.length===0)return[];let t=[...e].sort((u,d)=>u.added_at<d.added_at?-1:u.added_at>d.added_at?1:0),n=new Map,s=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=n.get(u.parent_session_id)??[];d.push(u),n.set(u.parent_session_id,d)}let o=t.filter(u=>u.role==="origin"),c=[...o.length>0?o:t.filter(u=>!u.parent_session_id)];for(;c.length>0;){let u=c.shift();if(s.has(u.session_id))continue;s.add(u.session_id),r.push(u.session_id);let d=n.get(u.session_id)??[];for(let m of d)s.has(m.session_id)||c.push(m)}for(let u of t)s.has(u.session_id)||(s.add(u.session_id),r.push(u.session_id));return r}function MR(e){let t=ni(e.sessionId),n=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
1598
+ `,budgetUsed:o,truncated:r}}function UR(e,t,n,s,r,o){let a=[];for(let c of n){if(s&&!s.has(c.link_type))continue;let u=null;if(c.source_session_id===t.session_id?u=c.target_session_id:c.target_session_id===t.session_id&&(u=c.source_session_id),!u)continue;let d=yr(e,u);if(!d)continue;let m=nm(t.started_at,d.started_at),f=im({confidence:c.confidence,linkType:c.link_type,daysApart:m,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:f,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(m)}d apart`,link_type:c.link_type})}return a}function wr(e,t={}){let n=Math.max(100,Math.floor(t.budget??xR)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??NR)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=sm(e);if(!u)throw new Error(`session not found: ${e}`);let d=IR(u.project_id),m={session_id:u.id,title:om(u),decimal:d.table?.byId.get(u.id)??null,summary:rm(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,m);let f=MR(d,e),b=DR(d,e),T=gn(e).filter($=>$.approved).filter($=>!c||c.has($.link_type)).filter($=>o||$.link_type!=="wiki_link"),S=jR(e,T,f,b,r),R=PR(S),A=[],v=[],D=[],X=[];for(let $ of T){let ne=$.source_session_id===e?$.target_session_id:$.source_session_id,i=yr(d,ne);if(!i)continue;let l=nm(m.started_at,i.started_at),p=im({confidence:$.confidence,linkType:$.link_type,daysApart:l,embeddingCosine:null,pagerank:R.get(ne)??0,scoring:s}),g=tm(l),_=`${$.link_type} confidence=${$.confidence.toFixed(2)} recency=${g.toFixed(2)} (${Math.round(l)}d apart)`,E={...i,score:p,evidence:_,link_type:$.link_type};$.link_type==="citation"?A.push(E):$.link_type==="similar"?v.push(E):$.link_type==="wiki_link"?X.push(E):D.push(E)}if(a){let $=wt({sourceSessionId:e,status:"pending",limit:100}),ne=wt({targetSessionId:e,status:"pending",limit:100}),i=[...$,...ne],l=new Set,p=i.filter(_=>l.has(_.id)?!1:(l.add(_.id),!0)),g=UR(d,m,p,c,s,R);for(let _ of g)_.link_type==="citation"?A.push(_):_.link_type==="similar"?v.push(_):_.link_type==="wiki_link"?X.push(_):D.push(_)}let I=($,ne)=>ne.score-$.score;A.sort(I),v.sort(I),D.sort(I),X.sort(I);let te=$R(m,[{heading:"Parents",refs:f},{heading:"Children",refs:b},{heading:"Citations (approved)",refs:A},{heading:"Similar sessions",refs:v},{heading:"Cousins (skill track + temporal)",refs:D},{heading:"Wiki links (manual)",refs:X}],n);return{origin:m,parents:f,children:b,citations:A,similar:v,cousins:D,wikiLinks:X,bundle:te.bundle,budgetUsed:te.budgetUsed,budgetRemaining:Math.max(0,n-te.budgetUsed),truncated:te.truncated}}import{randomUUID as GR}from"node:crypto";var BR=50;function HR(e){if(e.length===0)return[];let t=[...e].sort((u,d)=>u.added_at<d.added_at?-1:u.added_at>d.added_at?1:0),n=new Map,s=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=n.get(u.parent_session_id)??[];d.push(u),n.set(u.parent_session_id,d)}let o=t.filter(u=>u.role==="origin"),c=[...o.length>0?o:t.filter(u=>!u.parent_session_id)];for(;c.length>0;){let u=c.shift();if(s.has(u.session_id))continue;s.add(u.session_id),r.push(u.session_id);let d=n.get(u.session_id)??[];for(let m of d)s.has(m.session_id)||c.push(m)}for(let u of t)s.has(u.session_id)||(s.add(u.session_id),r.push(u.session_id));return r}function WR(e){let t=oi(e.sessionId),n=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
1599
1599
  `);return`${t}
1600
- ${n}`}function DR(e){return ve(e)?.auto_title_source??null}async function jR(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(we(),pt));if(!n())throw new Error("claude CLI not found on PATH");let s=await t(e.prompt,[],{model:e.model});if(!s.success)throw new Error(`claude CLI exited ${s.exitCode}: ${s.stderr.slice(-500)}`);let r=PR(s.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,vR)}function PR(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return nm(s)}}catch{}return nm(t)}function nm(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function rm(e,t={}){let n=ce(e);if(!n)throw new Error(`thread not found: ${e}`);let s=IR(n.edges),r=s.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let m=0;m<s.length;m++){let f=s[m],b=m+1;if(a?.aborted){let S={sessionId:f,reason:"cancelled"};u.push(S),t.onSkipped?.(S);continue}if(!o&&DR(f)==="agent"){let S={sessionId:f,reason:"already-titled"};u.push(S),t.onSkipped?.(S);continue}let T;try{if(sm)T=await sm({sessionId:f,current:b,total:r});else{let S=MR({sessionId:f,current:b,total:r});T=await jR({prompt:S,model:t.model})}}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),A={sessionId:f,error:w};d.push(A),t.onFailed?.(A);continue}try{he(f,T,"agent")}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),A={sessionId:f,error:`setAutoTitle failed: ${w}`};d.push(A),t.onFailed?.(A);continue}c.push(f),t.onProgress?.({current:b,total:r,sessionId:f,title:T})}return{generated:c,skipped:u,failed:d}}var sm=null;var $n=new Map,$R=300*1e3;function Fn(e,t,n){let s=e.events.length+1;e.events.push({id:s,kind:t,data:n});for(let r of e.waiters)r.resolve();e.waiters.clear()}function UR(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{$n.delete(e.jobId)},$R)}function HR(e){let t=0,n=0,s=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(n+=1),r.kind==="error"&&(s+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:n,failed:s,result:e.result}}function om(e){let t=FR(),n=new AbortController,s={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:n,result:null,cleanupTimer:null};return $n.set(t,s),(async()=>{await Promise.resolve();try{let r=await rm(e.threadId,{force:e.force??!1,signal:n.signal,model:e.model,onProgress:o=>{s.total=o.total,Fn(s,"progress",o)},onSkipped:o=>{Fn(s,"skipped",o)},onFailed:o=>{Fn(s,"error",o)}});s.result=r,s.status=n.signal.aborted?"cancelled":"done",s.endedAt=new Date().toISOString(),Fn(s,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");s.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},s.status="failed",s.endedAt=new Date().toISOString(),Fn(s,"done",s.result)}finally{UR(s)}})(),t}async function*im(e,t=0){let n=$n.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(s+=1,yield r,r.kind==="done")return}if(n.endedAt)return;await new Promise(r=>{n.waiters.add({resolve:r})})}}function am(e){let t=$n.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Yi(e){let t=$n.get(e);return t?HR(t):null}import{existsSync as cm,mkdirSync as BR,readFileSync as WR,writeFileSync as qR,chmodSync as XR}from"node:fs";import{homedir as JR}from"node:os";import{join as lm}from"node:path";import{z as et}from"zod";function um(){return process.env.RECALL_HOME??lm(JR(),".recall")}function GR(){let e=um();cm(e)||BR(e,{recursive:!0})}function dm(){return lm(um(),"config.json")}var wr=et.object({enabled:et.boolean().default(!1),backend:et.enum(["api","mcp"]).default("api"),apiKey:et.string().optional(),model:et.string().default("claude-opus-4-7"),maxTagsPerSession:et.number().int().min(1).max(10).default(4),minTagsPerSession:et.number().int().min(1).max(10).default(2),autopilot:et.boolean().default(!1)}),yr={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function pm(){let e=dm();if(!cm(e))return{};try{return JSON.parse(WR(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function qe(){let e=pm().autoTag;if(!e)return{...yr};let t=wr.safeParse({...yr,...e});return t.success?t.data:{...yr}}function mm(e){GR();let t=pm(),n=wr.parse({...yr,...t.autoTag??{},...e}),s={...t,autoTag:n},r=dm();qR(r,JSON.stringify(s,null,2));try{XR(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return n}function zi(e){let{apiKey:t,...n}=e;return{...n,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}U();var Rr="claude-haiku-4-5-20251001",gm=80,YR=2e3,Jt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},fm=null;async function zR(e,t){if(fm)return fm(e,t);let{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(we(),pt));return s()?n(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function KR(e){let n=h().prepare(`SELECT s.id,
1600
+ ${n}`}function qR(e){return ve(e)?.auto_title_source??null}async function XR(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(we(),pt));if(!n())throw new Error("claude CLI not found on PATH");let s=await t(e.prompt,[],{model:e.model});if(!s.success)throw new Error(`claude CLI exited ${s.exitCode}: ${s.stderr.slice(-500)}`);let r=JR(s.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,BR)}function JR(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return lm(s)}}catch{}return lm(t)}function lm(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function dm(e,t={}){let n=ce(e);if(!n)throw new Error(`thread not found: ${e}`);let s=HR(n.edges),r=s.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let m=0;m<s.length;m++){let f=s[m],b=m+1;if(a?.aborted){let S={sessionId:f,reason:"cancelled"};u.push(S),t.onSkipped?.(S);continue}if(!o&&qR(f)==="agent"){let S={sessionId:f,reason:"already-titled"};u.push(S),t.onSkipped?.(S);continue}let T;try{if(um)T=await um({sessionId:f,current:b,total:r});else{let S=WR({sessionId:f,current:b,total:r});T=await XR({prompt:S,model:t.model})}}catch(S){let R=S instanceof Error?S.message:String(S??"unknown error"),A={sessionId:f,error:R};d.push(A),t.onFailed?.(A);continue}try{he(f,T,"agent")}catch(S){let R=S instanceof Error?S.message:String(S??"unknown error"),A={sessionId:f,error:`setAutoTitle failed: ${R}`};d.push(A),t.onFailed?.(A);continue}c.push(f),t.onProgress?.({current:b,total:r,sessionId:f,title:T})}return{generated:c,skipped:u,failed:d}}var um=null;var $n=new Map,YR=300*1e3;function Fn(e,t,n){let s=e.events.length+1;e.events.push({id:s,kind:t,data:n});for(let r of e.waiters)r.resolve();e.waiters.clear()}function zR(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{$n.delete(e.jobId)},YR)}function KR(e){let t=0,n=0,s=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(n+=1),r.kind==="error"&&(s+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:n,failed:s,result:e.result}}function pm(e){let t=GR(),n=new AbortController,s={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:n,result:null,cleanupTimer:null};return $n.set(t,s),(async()=>{await Promise.resolve();try{let r=await dm(e.threadId,{force:e.force??!1,signal:n.signal,model:e.model,onProgress:o=>{s.total=o.total,Fn(s,"progress",o)},onSkipped:o=>{Fn(s,"skipped",o)},onFailed:o=>{Fn(s,"error",o)}});s.result=r,s.status=n.signal.aborted?"cancelled":"done",s.endedAt=new Date().toISOString(),Fn(s,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");s.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},s.status="failed",s.endedAt=new Date().toISOString(),Fn(s,"done",s.result)}finally{zR(s)}})(),t}async function*mm(e,t=0){let n=$n.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(s+=1,yield r,r.kind==="done")return}if(n.endedAt)return;await new Promise(r=>{n.waiters.add({resolve:r})})}}function gm(e){let t=$n.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Zi(e){let t=$n.get(e);return t?KR(t):null}import{existsSync as fm,mkdirSync as VR,readFileSync as QR,writeFileSync as ZR,chmodSync as ek}from"node:fs";import{homedir as tk}from"node:os";import{join as _m}from"node:path";import{z as et}from"zod";function hm(){return process.env.RECALL_HOME??_m(tk(),".recall")}function nk(){let e=hm();fm(e)||VR(e,{recursive:!0})}function Em(){return _m(hm(),"config.json")}var kr=et.object({enabled:et.boolean().default(!1),backend:et.enum(["api","mcp"]).default("api"),apiKey:et.string().optional(),model:et.string().default("claude-opus-4-7"),maxTagsPerSession:et.number().int().min(1).max(10).default(4),minTagsPerSession:et.number().int().min(1).max(10).default(2),autopilot:et.boolean().default(!1)}),Rr={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function bm(){let e=Em();if(!fm(e))return{};try{return JSON.parse(QR(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function qe(){let e=bm().autoTag;if(!e)return{...Rr};let t=kr.safeParse({...Rr,...e});return t.success?t.data:{...Rr}}function Sm(e){nk();let t=bm(),n=kr.parse({...Rr,...t.autoTag??{},...e}),s={...t,autoTag:n},r=Em();ZR(r,JSON.stringify(s,null,2));try{ek(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return n}function ea(e){let{apiKey:t,...n}=e;return{...n,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}U();var Ar="claude-haiku-4-5-20251001",Tm=80,sk=2e3,Jt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},ym=null;async function rk(e,t){if(ym)return ym(e,t);let{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(we(),pt));return s()?n(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function ok(e){let n=h().prepare(`SELECT s.id,
1601
1601
  s.auto_title,
1602
1602
  s.auto_title_source,
1603
1603
  CASE WHEN sa.alias IS NOT NULL AND sa.alias != ''
1604
1604
  THEN 1 ELSE 0 END AS has_alias_int
1605
1605
  FROM sessions s
1606
1606
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1607
- WHERE s.id = ?`).get(e);return n?{id:n.id,auto_title:n.auto_title,auto_title_source:n.auto_title_source??null,has_alias:n.has_alias_int===1}:null}function VR(e){return e.parents.length===0&&e.children.length===0&&e.citations.length===0&&e.similar.length===0&&e.cousins.length===0&&e.wikiLinks.length===0}function QR(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let n=Rn({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(n){case"low_signal":case"recursive_meta":case"programmatic":return{eligible:!0};case"agent":return{eligible:!1,reason:"agent_titled"};case"manual_alias":return{eligible:!1,reason:"manual_alias"};default:return{eligible:!1,reason:"clean"}}}function ZR(e){return["You are renaming a Claude Code session. Below is its NEIGHBORHOOD: parent","sessions, child sessions, related sessions, and citations \u2014 assembled by","Claude Recall's cog-graph. The session itself has a low-signal or","self-referential title that needs replacement.","","Read the neighborhood, then mint a single descriptive title that","reflects this session's role in the surrounding work. Constraints:","","- Maximum 80 characters.","- No quotes, no trailing punctuation.","- Output ONLY a JSON object with two fields:",' { "title": "<\u226480 char title>", "evidence": "<one sentence why>" }',"- No markdown, no preface, no commentary outside the JSON.","","--- NEIGHBORHOOD BUNDLE ---",e,"--- END NEIGHBORHOOD BUNDLE ---"].join(`
1608
- `)}function ek(e){let t=e.trim();if(!t)return null;let n=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")n=a.result.trim();else if(typeof a.title=="string")return _m(a)}}catch{}let s=n.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);s&&(n=s[1].trim());let r=null;try{r=JSON.parse(n)}catch{let o=n.indexOf("{"),a=n.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(n.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:_m(r)}function _m(e){let t=e.title;if(typeof t!="string")return null;let n=tk(t).trim();if(!n)return null;let s=e.evidence,r=typeof s=="string"?s.trim():"";return{title:n,evidence:r}}function tk(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function nk(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=gm?t:t.slice(0,gm)}function sk(e,t){let n=e.auto_title??"";return!n&&e.has_alias&&(n=h().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:n,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function Ki(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let n=KR(e);if(!n)throw new Error(`session not found: ${e}`);let s=QR(n,t.force===!0);if(!s.eligible)return sk(n,s.reason);let r=t.budget??YR,o=Tr(e,{budget:r});if(VR(o))throw new Jt(e);if(t.signal?.aborted)return{session_id:e,title:n.auto_title??"",source:n.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=ZR(o.bundle),c=t.model??Rr,u=await zR(a,c);if(!u.success){let T=u.stderr.slice(-300);throw new Error(`claude CLI exited ${u.exitCode}: ${T||"no stderr captured"}`)}let d=ek(u.stdout);if(!d)throw new Error("failed to parse regeneration output: not valid JSON {title, evidence}");if(!d.title||!d.title.trim())throw new Error("regeneration produced empty title");let m=nk(d.title);if(!m)throw new Error("regeneration produced empty title after clamp");he(e,m,"agent");let b=ve(e)?.auto_title??m;return{session_id:e,title:b,source:"agent",confidence:ok(o),evidence:d.evidence||`regenerated from neighborhood (${rk(o)})`,written:!0}}function rk(e){let t=[];return e.parents.length&&t.push(`${e.parents.length} parents`),e.children.length&&t.push(`${e.children.length} children`),e.citations.length&&t.push(`${e.citations.length} citations`),e.similar.length&&t.push(`${e.similar.length} similar`),e.cousins.length&&t.push(`${e.cousins.length} cousins`),e.wikiLinks.length&&t.push(`${e.wikiLinks.length} wiki-links`),t.join(", ")}function ok(e){let t=e.parents.length,n=e.children.length,s=e.citations.length,r=e.similar.length,o=e.cousins.length,a=e.wikiLinks.length,c=.25*Math.min(1,t)+.15*Math.min(1,n)+.2*Math.min(1,s/2)+.15*Math.min(1,r/3)+.1*Math.min(1,o/3)+.15*Math.min(1,a);return Math.min(.95,c)}import{streamSSE as rt}from"hono/streaming";import{bodyLimit as xA}from"hono/body-limit";import{z as j}from"zod";U();U();We();function Un(e){return h().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}U();function hm(e){let t=h(),n=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1607
+ WHERE s.id = ?`).get(e);return n?{id:n.id,auto_title:n.auto_title,auto_title_source:n.auto_title_source??null,has_alias:n.has_alias_int===1}:null}function ik(e){return e.parents.length===0&&e.children.length===0&&e.citations.length===0&&e.similar.length===0&&e.cousins.length===0&&e.wikiLinks.length===0}function ak(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let n=Rn({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(n){case"low_signal":case"recursive_meta":case"programmatic":return{eligible:!0};case"agent":return{eligible:!1,reason:"agent_titled"};case"manual_alias":return{eligible:!1,reason:"manual_alias"};default:return{eligible:!1,reason:"clean"}}}function ck(e){return["You are renaming a Claude Code session. Below is its NEIGHBORHOOD: parent","sessions, child sessions, related sessions, and citations \u2014 assembled by","Claude Recall's cog-graph. The session itself has a low-signal or","self-referential title that needs replacement.","","Read the neighborhood, then mint a single descriptive title that","reflects this session's role in the surrounding work. Constraints:","","- Maximum 80 characters.","- No quotes, no trailing punctuation.","- Output ONLY a JSON object with two fields:",' { "title": "<\u226480 char title>", "evidence": "<one sentence why>" }',"- No markdown, no preface, no commentary outside the JSON.","","--- NEIGHBORHOOD BUNDLE ---",e,"--- END NEIGHBORHOOD BUNDLE ---"].join(`
1608
+ `)}function lk(e){let t=e.trim();if(!t)return null;let n=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")n=a.result.trim();else if(typeof a.title=="string")return wm(a)}}catch{}let s=n.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);s&&(n=s[1].trim());let r=null;try{r=JSON.parse(n)}catch{let o=n.indexOf("{"),a=n.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(n.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:wm(r)}function wm(e){let t=e.title;if(typeof t!="string")return null;let n=uk(t).trim();if(!n)return null;let s=e.evidence,r=typeof s=="string"?s.trim():"";return{title:n,evidence:r}}function uk(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function dk(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=Tm?t:t.slice(0,Tm)}function pk(e,t){let n=e.auto_title??"";return!n&&e.has_alias&&(n=h().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:n,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function ta(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let n=ok(e);if(!n)throw new Error(`session not found: ${e}`);let s=ak(n,t.force===!0);if(!s.eligible)return pk(n,s.reason);let r=t.budget??sk,o=wr(e,{budget:r});if(ik(o))throw new Jt(e);if(t.signal?.aborted)return{session_id:e,title:n.auto_title??"",source:n.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=ck(o.bundle),c=t.model??Ar,u=await rk(a,c);if(!u.success){let T=u.stderr.slice(-300);throw new Error(`claude CLI exited ${u.exitCode}: ${T||"no stderr captured"}`)}let d=lk(u.stdout);if(!d)throw new Error("failed to parse regeneration output: not valid JSON {title, evidence}");if(!d.title||!d.title.trim())throw new Error("regeneration produced empty title");let m=dk(d.title);if(!m)throw new Error("regeneration produced empty title after clamp");he(e,m,"agent");let b=ve(e)?.auto_title??m;return{session_id:e,title:b,source:"agent",confidence:gk(o),evidence:d.evidence||`regenerated from neighborhood (${mk(o)})`,written:!0}}function mk(e){let t=[];return e.parents.length&&t.push(`${e.parents.length} parents`),e.children.length&&t.push(`${e.children.length} children`),e.citations.length&&t.push(`${e.citations.length} citations`),e.similar.length&&t.push(`${e.similar.length} similar`),e.cousins.length&&t.push(`${e.cousins.length} cousins`),e.wikiLinks.length&&t.push(`${e.wikiLinks.length} wiki-links`),t.join(", ")}function gk(e){let t=e.parents.length,n=e.children.length,s=e.citations.length,r=e.similar.length,o=e.cousins.length,a=e.wikiLinks.length,c=.25*Math.min(1,t)+.15*Math.min(1,n)+.2*Math.min(1,s/2)+.15*Math.min(1,r/3)+.1*Math.min(1,o/3)+.15*Math.min(1,a);return Math.min(.95,c)}import{streamSSE as rt}from"hono/streaming";import{bodyLimit as jA}from"hono/body-limit";import{z as j}from"zod";U();U();We();function Un(e){return h().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}U();function Rm(e){let t=h(),n=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1609
1609
  (message_hash, resolved_in_session_id, fix_summary, resolved_at, unresolved_at)
1610
1610
  VALUES (?, ?, ?, ?, NULL)
1611
1611
  ON CONFLICT(message_hash) DO UPDATE SET
1612
1612
  resolved_in_session_id = excluded.resolved_in_session_id,
1613
1613
  fix_summary = excluded.fix_summary,
1614
1614
  resolved_at = excluded.resolved_at,
1615
- unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,n),ik(e.messageHash)}function Em(e){h().prepare(`UPDATE bug_signature_resolutions
1615
+ unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,n),fk(e.messageHash)}function km(e){h().prepare(`UPDATE bug_signature_resolutions
1616
1616
  SET unresolved_at = ?
1617
- WHERE message_hash = ?`).run(new Date().toISOString(),e)}function ik(e){return h().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function Vi(e){if(e.length===0)return new Map;let t=h(),n=e.map(()=>"?").join(","),s=t.prepare(`SELECT * FROM bug_signature_resolutions
1618
- WHERE message_hash IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.message_hash,o);return r}function Qi(e){return!!e&&e.unresolved_at===null}U();function Hn(){return new Date().toISOString()}function Zi(){let e=h(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1617
+ WHERE message_hash = ?`).run(new Date().toISOString(),e)}function fk(e){return h().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function na(e){if(e.length===0)return new Map;let t=h(),n=e.map(()=>"?").join(","),s=t.prepare(`SELECT * FROM bug_signature_resolutions
1618
+ WHERE message_hash IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.message_hash,o);return r}function sa(e){return!!e&&e.unresolved_at===null}U();function Bn(){return new Date().toISOString()}function ra(){let e=h(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1619
1619
  FROM macro_repos
1620
1620
  ORDER BY name COLLATE NOCASE`).all();if(t.length===0)return[];let n=t.map(a=>a.id),s=n.map(()=>"?").join(","),r=e.prepare(`SELECT m.macro_repo_id, m.project_id, p.name AS project_name
1621
1621
  FROM macro_repo_members m
1622
1622
  JOIN projects p ON p.id = m.project_id
1623
1623
  WHERE m.macro_repo_id IN (${s})
1624
- ORDER BY p.name COLLATE NOCASE`).all(...n),o=new Map;for(let a of t)o.set(a.id,{...a,member_project_ids:[],member_project_names:[]});for(let a of r){let c=o.get(a.macro_repo_id);c&&(c.member_project_ids.push(a.project_id),c.member_project_names.push(a.project_name))}return Array.from(o.values())}function Gt(e){return Zi().find(n=>n.id===e)??null}function bm(){return h().prepare(`SELECT p.id, p.name
1624
+ ORDER BY p.name COLLATE NOCASE`).all(...n),o=new Map;for(let a of t)o.set(a.id,{...a,member_project_ids:[],member_project_names:[]});for(let a of r){let c=o.get(a.macro_repo_id);c&&(c.member_project_ids.push(a.project_id),c.member_project_names.push(a.project_name))}return Array.from(o.values())}function Gt(e){return ra().find(n=>n.id===e)??null}function Am(){return h().prepare(`SELECT p.id, p.name
1625
1625
  FROM projects p
1626
1626
  LEFT JOIN macro_repo_members m ON m.project_id = p.id
1627
1627
  WHERE m.project_id IS NULL
1628
- ORDER BY p.name COLLATE NOCASE`).all()}function Sm(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let n=h(),s=Hn(),r=n.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1629
- VALUES (?, ?, ?, ?)`).run(t,e.description??null,s,s),o=Number(r.lastInsertRowid);return Gt(o)}function Tm(e,t){let n=Gt(e);if(!n)throw new Error(`macro repo ${e} not found`);let s=t.name!==void 0?t.name.trim():n.name;if(!s)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:n.description;return h().prepare(`UPDATE macro_repos
1628
+ ORDER BY p.name COLLATE NOCASE`).all()}function xm(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let n=h(),s=Bn(),r=n.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1629
+ VALUES (?, ?, ?, ?)`).run(t,e.description??null,s,s),o=Number(r.lastInsertRowid);return Gt(o)}function Nm(e,t){let n=Gt(e);if(!n)throw new Error(`macro repo ${e} not found`);let s=t.name!==void 0?t.name.trim():n.name;if(!s)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:n.description;return h().prepare(`UPDATE macro_repos
1630
1630
  SET name = ?, description = ?, updated_at = ?
1631
- WHERE id = ?`).run(s,r,Hn(),e),Gt(e)}function ym(e){h().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function wm(e,t){let n=h();if(!n.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!n.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);n.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1632
- VALUES (?, ?, ?)`).run(e,t,Hn()),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Hn(),e)}function Rm(e,t){let n=h();n.prepare(`DELETE FROM macro_repo_members
1633
- WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Hn(),e)}U();function km(e){let t=h(),n=new Date().toISOString(),s=t.prepare(`INSERT INTO bug_synthesis_results
1631
+ WHERE id = ?`).run(s,r,Bn(),e),Gt(e)}function Om(e){h().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function Cm(e,t){let n=h();if(!n.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!n.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);n.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1632
+ VALUES (?, ?, ?)`).run(e,t,Bn()),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Bn(),e)}function Lm(e,t){let n=h();n.prepare(`DELETE FROM macro_repo_members
1633
+ WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Bn(),e)}U();function vm(e){let t=h(),n=new Date().toISOString(),s=t.prepare(`INSERT INTO bug_synthesis_results
1634
1634
  (scope, target_id, mode, model, output_markdown, input_tokens, output_tokens, context_summary, created_at, job_id)
1635
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.scope,e.target_id,e.mode,e.model,e.output_markdown,e.input_tokens,e.output_tokens,JSON.stringify(e.context_summary??{}),n,e.job_id??null),r=Number(s.lastInsertRowid);return ea(r)}function Am(e){let t={};try{t=JSON.parse(e.context_summary)}catch{t={}}return{id:e.id,scope:e.scope,target_id:e.target_id,mode:e.mode,model:e.model,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,context_summary:t,created_at:e.created_at,job_id:e.job_id}}function ea(e){let n=h().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return n?Am(n):null}function xm(e={}){let t=h(),n=[],s=[];e.scope&&(n.push("scope = ?"),s.push(e.scope)),e.target_id&&(n.push("target_id = ?"),s.push(e.target_id));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1635
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.scope,e.target_id,e.mode,e.model,e.output_markdown,e.input_tokens,e.output_tokens,JSON.stringify(e.context_summary??{}),n,e.job_id??null),r=Number(s.lastInsertRowid);return oa(r)}function Im(e){let t={};try{t=JSON.parse(e.context_summary)}catch{t={}}return{id:e.id,scope:e.scope,target_id:e.target_id,mode:e.mode,model:e.model,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,context_summary:t,created_at:e.created_at,job_id:e.job_id}}function oa(e){let n=h().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return n?Im(n):null}function Mm(e={}){let t=h(),n=[],s=[];e.scope&&(n.push("scope = ?"),s.push(e.scope)),e.target_id&&(n.push("target_id = ?"),s.push(e.target_id));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1636
1636
  ${r}
1637
1637
  ORDER BY created_at DESC
1638
- LIMIT ?`).all(...s,o).map(Am)}function Nm(e){let n=h().prepare(`SELECT target_id, COUNT(*) AS n
1638
+ LIMIT ?`).all(...s,o).map(Im)}function Dm(e){let n=h().prepare(`SELECT target_id, COUNT(*) AS n
1639
1639
  FROM bug_synthesis_results
1640
1640
  WHERE scope = ?
1641
- GROUP BY target_id`).all(e),s=new Map;for(let r of n)s.set(r.target_id,r.n);return s}function Om(e){h().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as ak,timingSafeEqual as ck}from"node:crypto";var lk=6e4,uk=new Set(["127.0.0.1","localhost"]),Bn=new Map;function ta(){return Date.now()}function Lm(){let e=ta();for(let[t,n]of Bn)(n.expiresAt<=e||n.used)&&Bn.delete(t)}function je(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!uk.has(r.hostname))return e.json({error:"forbidden: cross-origin launcher request rejected"},403)}catch{return e.json({error:"forbidden: invalid Origin header"},403)}let n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.json({error:"forbidden: Sec-Fetch-Site indicates cross-origin"},403):e.req.header("x-recall-launcher")!=="1"?e.json({error:"forbidden: missing X-Recall-Launcher header (this endpoint is callable only from the Recall web UI)"},403):null}function na(e){Lm();let t=ak(32).toString("hex"),n=ta()+lk;return Bn.set(t,{token:t,intent:e,expiresAt:n,used:!1}),{token:t,expiresAt:n}}function sa(e){if(Lm(),typeof e!="string"||e.length!==64)return null;let t;try{t=Buffer.from(e,"hex")}catch{return null}if(t.length!==32)return null;for(let n of Bn.values()){if(n.used||n.expiresAt<=ta())continue;let s;try{s=Buffer.from(n.token,"hex")}catch{continue}if(s.length===t.length&&ck(s,t))return n.used=!0,Bn.delete(n.token),n.intent}return null}import{existsSync as _k,mkdirSync as iD,readFileSync as hk,writeFileSync as aD}from"node:fs";import{homedir as Ek}from"node:os";import{join as Pm}from"node:path";import{z as ra}from"zod";import{appendFileSync as dk,existsSync as Cm,mkdirSync as pk,readFileSync as mk}from"node:fs";import{homedir as gk}from"node:os";import{join as vm}from"node:path";function Im(){return process.env.RECALL_HOME??vm(gk(),".recall")}function fk(){let e=Im();Cm(e)||pk(e,{recursive:!0})}function Mm(){return vm(Im(),"launcher-audit.log")}function oe(e){fk();let t={ts:new Date().toISOString(),...e};try{dk(Mm(),JSON.stringify(t)+`
1642
- `,"utf8")}catch(n){console.error("[launcher-audit] failed to append:",n)}}function Dm(e){let t=Mm();if(!Cm(t))return{input_tokens:0,output_tokens:0,records_counted:0};let n=Date.now()-e,s=0,r=0,o=0,a;try{a=mk(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1643
- `)){if(!c.trim())continue;let u;try{u=JSON.parse(c)}catch{continue}if(u.kind!=="run-completed"&&u.kind!=="synth-completed")continue;let d=Date.parse(u.ts);!Number.isFinite(d)||d<n||(s+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:s,output_tokens:r,records_counted:o}}var bk=1440*60*1e3,Sk=ra.object({dailyTokenBudget:ra.number().int().nonnegative().default(1e6),sessionCeiling:ra.number().int().positive().max(1e4).default(500)}),oa={dailyTokenBudget:1e6,sessionCeiling:500};function Tk(){return process.env.RECALL_HOME??Pm(Ek(),".recall")}function yk(){return Pm(Tk(),"config.json")}function wk(){let e=yk();if(!_k(e))return{};try{return JSON.parse(hk(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function ia(){let e=wk().launcher;if(!e)return{...oa};let t=Sk.safeParse({...oa,...e});return t.success?t.data:{...oa}}function Yt(){let e=ia(),t=Dm(bk),n=t.input_tokens+t.output_tokens,s=Math.max(0,e.dailyTokenBudget-n);return{daily_token_budget:e.dailyTokenBudget,session_ceiling:e.sessionCeiling,spent_input_tokens_24h:t.input_tokens,spent_output_tokens_24h:t.output_tokens,spent_total_tokens_24h:n,remaining_tokens_24h:s}}function aa(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function ca(e){let t=e.mode==="root_cause"?800:2e3;if(e.scope==="cluster")return{estimated_input_tokens_max:5e3+Math.min(8,Math.max(1,e.member_session_count??1))*3e3,estimated_output_tokens_max:t};let n=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*n),estimated_output_tokens_max:t}}var jm={pro:45,"max-5x":225,"max-20x":900};function Rk(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var kk=4e3;function Fm(e){return Math.max(1,Math.ceil(e/kk))}function tt(e,t){let n=Rk(t),s=e*n,r=Object.keys(jm).map(o=>{let a=s/jm[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:n,per_plan:r,caveat:"Estimates use Anthropic\u2019s public approximate caps and assume a multiplier of ~1x for Haiku, ~5x for Sonnet, ~10x for Opus. Anthropic adjusts these limits without notice; actual consumption depends on session size. Treat as planning guidance, not a contract."}}import{randomUUID as Ak}from"node:crypto";var zt=new Map,Wn=new Map,xk=300*1e3;function kr(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function Nk(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{zt.delete(e.jobId),Wn.get(e.project)===e.jobId&&Wn.delete(e.project)},xk),e.cleanupTimer.unref?.())}function la(e){return{jobId:e.jobId,project:e.project,model:e.model,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.progress.total,processed:e.progress.processed,ok:e.progress.ok,failed:e.progress.failed,skipped:e.progress.skipped,total_input_tokens:e.progress.total_input_tokens,total_output_tokens:e.progress.total_output_tokens,current_session_id:e.progress.current_session_id,error:e.error}}function $m(e){let t=Un(e.project);if(!t)return{error:`project "${e.project}" not found`};let n=Wn.get(t.name);if(n){let m=zt.get(n);if(m&&m.status==="running")return{jobId:n,reused:!0};Wn.delete(t.name)}let s=Ak(),r=e.model??mt,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:s,project:t.name,projectId:t.id,model:r,limit:o,force:a,status:"running",startedAt:u,endedAt:null,events:[],waiters:new Set,controller:c,progress:{total:0,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0},error:null,cleanupTimer:null};return zt.set(s,d),Wn.set(t.name,s),oe({kind:"run-launched",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await hl({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:m=>{d.progress=m,kr(d,"progress",m)},onResult:m=>{!m.ok&&!m.skipped&&kr(d,"error",{session_id:m.session_id,reason:m.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),kr(d,"done",la(d)),oe({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens,sessions_processed:d.progress.processed})}catch(m){let f=m instanceof Error?m.message:String(m??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=f,kr(d,"done",la(d)),oe({kind:"run-failed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,reason:f,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{Nk(d)}})(),{jobId:s,reused:!1}}async function*Um(e,t=0){let n=zt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Hm(e){let t=zt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function ua(e){let t=zt.get(e);return t?la(t):null}U();import{randomUUID as Ok}from"node:crypto";import{spawn as Lk}from"node:child_process";Es();var Bm="claude-haiku-4-5-20251001",Vt=new Map,Jn=new Map,Ck=300*1e3;function Wm(e){return`${e.scope}:${e.target_id}:${e.mode}`}function qn(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function vk(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Vt.delete(e.jobId);let t=Wm(e.intent);Jn.get(t)===e.jobId&&Jn.delete(t)},Ck),e.cleanupTimer.unref?.())}function Kt(e){return{jobId:e.jobId,scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,status:e.status,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,startedAt:e.startedAt,endedAt:e.endedAt,error:e.error,context_summary:e.context_summary}}function qm(){return h().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1641
+ GROUP BY target_id`).all(e),s=new Map;for(let r of n)s.set(r.target_id,r.n);return s}function jm(e){h().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as _k,timingSafeEqual as hk}from"node:crypto";var Ek=6e4,bk=new Set(["127.0.0.1","localhost"]),Hn=new Map;function ia(){return Date.now()}function Pm(){let e=ia();for(let[t,n]of Hn)(n.expiresAt<=e||n.used)&&Hn.delete(t)}function je(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!bk.has(r.hostname))return e.json({error:"forbidden: cross-origin launcher request rejected"},403)}catch{return e.json({error:"forbidden: invalid Origin header"},403)}let n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.json({error:"forbidden: Sec-Fetch-Site indicates cross-origin"},403):e.req.header("x-recall-launcher")!=="1"?e.json({error:"forbidden: missing X-Recall-Launcher header (this endpoint is callable only from the Recall web UI)"},403):null}function aa(e){Pm();let t=_k(32).toString("hex"),n=ia()+Ek;return Hn.set(t,{token:t,intent:e,expiresAt:n,used:!1}),{token:t,expiresAt:n}}function ca(e){if(Pm(),typeof e!="string"||e.length!==64)return null;let t;try{t=Buffer.from(e,"hex")}catch{return null}if(t.length!==32)return null;for(let n of Hn.values()){if(n.used||n.expiresAt<=ia())continue;let s;try{s=Buffer.from(n.token,"hex")}catch{continue}if(s.length===t.length&&hk(s,t))return n.used=!0,Hn.delete(n.token),n.intent}return null}import{existsSync as kk,mkdirSync as TD,readFileSync as Ak,writeFileSync as yD}from"node:fs";import{homedir as xk}from"node:os";import{join as qm}from"node:path";import{z as la}from"zod";import{appendFileSync as Sk,existsSync as Fm,mkdirSync as Tk,readFileSync as yk}from"node:fs";import{homedir as wk}from"node:os";import{join as $m}from"node:path";function Um(){return process.env.RECALL_HOME??$m(wk(),".recall")}function Rk(){let e=Um();Fm(e)||Tk(e,{recursive:!0})}function Bm(){return $m(Um(),"launcher-audit.log")}function oe(e){Rk();let t={ts:new Date().toISOString(),...e};try{Sk(Bm(),JSON.stringify(t)+`
1642
+ `,"utf8")}catch(n){console.error("[launcher-audit] failed to append:",n)}}function Hm(e){let t=Bm();if(!Fm(t))return{input_tokens:0,output_tokens:0,records_counted:0};let n=Date.now()-e,s=0,r=0,o=0,a;try{a=yk(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1643
+ `)){if(!c.trim())continue;let u;try{u=JSON.parse(c)}catch{continue}if(u.kind!=="run-completed"&&u.kind!=="synth-completed")continue;let d=Date.parse(u.ts);!Number.isFinite(d)||d<n||(s+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:s,output_tokens:r,records_counted:o}}var Nk=1440*60*1e3,Ok=la.object({dailyTokenBudget:la.number().int().nonnegative().default(1e6),sessionCeiling:la.number().int().positive().max(1e4).default(500)}),ua={dailyTokenBudget:1e6,sessionCeiling:500};function Ck(){return process.env.RECALL_HOME??qm(xk(),".recall")}function Lk(){return qm(Ck(),"config.json")}function vk(){let e=Lk();if(!kk(e))return{};try{return JSON.parse(Ak(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function da(){let e=vk().launcher;if(!e)return{...ua};let t=Ok.safeParse({...ua,...e});return t.success?t.data:{...ua}}function Yt(){let e=da(),t=Hm(Nk),n=t.input_tokens+t.output_tokens,s=Math.max(0,e.dailyTokenBudget-n);return{daily_token_budget:e.dailyTokenBudget,session_ceiling:e.sessionCeiling,spent_input_tokens_24h:t.input_tokens,spent_output_tokens_24h:t.output_tokens,spent_total_tokens_24h:n,remaining_tokens_24h:s}}function pa(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function ma(e){let t=e.mode==="root_cause"?800:2e3;if(e.scope==="cluster")return{estimated_input_tokens_max:5e3+Math.min(8,Math.max(1,e.member_session_count??1))*3e3,estimated_output_tokens_max:t};let n=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*n),estimated_output_tokens_max:t}}var Wm={pro:45,"max-5x":225,"max-20x":900};function Ik(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var Mk=4e3;function Xm(e){return Math.max(1,Math.ceil(e/Mk))}function tt(e,t){let n=Ik(t),s=e*n,r=Object.keys(Wm).map(o=>{let a=s/Wm[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:n,per_plan:r,caveat:"Estimates use Anthropic\u2019s public approximate caps and assume a multiplier of ~1x for Haiku, ~5x for Sonnet, ~10x for Opus. Anthropic adjusts these limits without notice; actual consumption depends on session size. Treat as planning guidance, not a contract."}}import{randomUUID as Dk}from"node:crypto";var zt=new Map,Wn=new Map,jk=300*1e3;function xr(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function Pk(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{zt.delete(e.jobId),Wn.get(e.project)===e.jobId&&Wn.delete(e.project)},jk),e.cleanupTimer.unref?.())}function ga(e){return{jobId:e.jobId,project:e.project,model:e.model,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.progress.total,processed:e.progress.processed,ok:e.progress.ok,failed:e.progress.failed,skipped:e.progress.skipped,total_input_tokens:e.progress.total_input_tokens,total_output_tokens:e.progress.total_output_tokens,current_session_id:e.progress.current_session_id,error:e.error}}function Jm(e){let t=Un(e.project);if(!t)return{error:`project "${e.project}" not found`};let n=Wn.get(t.name);if(n){let m=zt.get(n);if(m&&m.status==="running")return{jobId:n,reused:!0};Wn.delete(t.name)}let s=Dk(),r=e.model??mt,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:s,project:t.name,projectId:t.id,model:r,limit:o,force:a,status:"running",startedAt:u,endedAt:null,events:[],waiters:new Set,controller:c,progress:{total:0,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0},error:null,cleanupTimer:null};return zt.set(s,d),Wn.set(t.name,s),oe({kind:"run-launched",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await yl({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:m=>{d.progress=m,xr(d,"progress",m)},onResult:m=>{!m.ok&&!m.skipped&&xr(d,"error",{session_id:m.session_id,reason:m.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),xr(d,"done",ga(d)),oe({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens,sessions_processed:d.progress.processed})}catch(m){let f=m instanceof Error?m.message:String(m??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=f,xr(d,"done",ga(d)),oe({kind:"run-failed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,reason:f,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{Pk(d)}})(),{jobId:s,reused:!1}}async function*Gm(e,t=0){let n=zt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Ym(e){let t=zt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function fa(e){let t=zt.get(e);return t?ga(t):null}U();import{randomUUID as Fk}from"node:crypto";import{spawn as $k}from"node:child_process";Es();var zm="claude-haiku-4-5-20251001",Vt=new Map,Jn=new Map,Uk=300*1e3;function Km(e){return`${e.scope}:${e.target_id}:${e.mode}`}function qn(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function Bk(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Vt.delete(e.jobId);let t=Km(e.intent);Jn.get(t)===e.jobId&&Jn.delete(t)},Uk),e.cleanupTimer.unref?.())}function Kt(e){return{jobId:e.jobId,scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,status:e.status,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,startedAt:e.startedAt,endedAt:e.endedAt,error:e.error,context_summary:e.context_summary}}function Vm(){return h().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1644
1644
  oi.bug_signatures
1645
1645
  FROM session_output_index oi
1646
1646
  JOIN sessions s ON s.id = oi.session_id
1647
1647
  JOIN projects p ON p.id = s.project_id
1648
1648
  WHERE oi.bug_signatures IS NOT NULL
1649
- AND oi.bug_signatures != '[]'`).all()}function Xm(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Ik(e){let t=qm(),n=[];for(let a of t)for(let c of Xm(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&n.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(n.length===0)return null;let s=n[0],r=Array.from(new Set(n.map(a=>a.session_id))),o=Array.from(new Set(n.map(a=>a.project))).sort();return{message_hash:s.sig.message_hash??null,error_type:s.sig.error_type??null,snippet:(s.sig.snippet??"").slice(0,400),file:s.sig.file??null,occurrence_count:n.length,projects:o,member_session_ids:r}}function Mk(e){let t=qm().filter(o=>o.project===e),n=new Map;for(let o of t)for(let a of Xm(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=n.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):n.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let s=new Map;try{let o=Array.from(n.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=h(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1649
+ AND oi.bug_signatures != '[]'`).all()}function Qm(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Hk(e){let t=Vm(),n=[];for(let a of t)for(let c of Qm(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&n.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(n.length===0)return null;let s=n[0],r=Array.from(new Set(n.map(a=>a.session_id))),o=Array.from(new Set(n.map(a=>a.project))).sort();return{message_hash:s.sig.message_hash??null,error_type:s.sig.error_type??null,snippet:(s.sig.snippet??"").slice(0,400),file:s.sig.file??null,occurrence_count:n.length,projects:o,member_session_ids:r}}function Wk(e){let t=Vm().filter(o=>o.project===e),n=new Map;for(let o of t)for(let a of Qm(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=n.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):n.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let s=new Map;try{let o=Array.from(n.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=h(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1650
1650
  FROM bug_signature_resolutions
1651
1651
  WHERE message_hash IN (${c})
1652
- AND unresolved_at IS NULL`).all(...o);for(let d of u)s.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of n){let c=a.first.message_hash??null,u=c?s.get(c)??null:null;r.push({cluster_id:c??o,error_type:a.first.error_type??null,snippet:(a.first.snippet??"").slice(0,200),file:a.first.file??null,occurrence_count:a.sessions.size,resolved:u!==null,fix_summary:u?.fix_summary??null})}return r.sort((o,a)=>a.occurrence_count-o.occurrence_count),r}function Dk(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function jk(e,t){let n=h(),s=Dk(t),r=[];for(let o of e.slice(0,8)){let a=n.prepare(`SELECT s.id, p.name AS project, s.auto_title
1652
+ AND unresolved_at IS NULL`).all(...o);for(let d of u)s.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of n){let c=a.first.message_hash??null,u=c?s.get(c)??null:null;r.push({cluster_id:c??o,error_type:a.first.error_type??null,snippet:(a.first.snippet??"").slice(0,200),file:a.first.file??null,occurrence_count:a.sessions.size,resolved:u!==null,fix_summary:u?.fix_summary??null})}return r.sort((o,a)=>a.occurrence_count-o.occurrence_count),r}function qk(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function Xk(e,t){let n=h(),s=qk(t),r=[];for(let o of e.slice(0,8)){let a=n.prepare(`SELECT s.id, p.name AS project, s.auto_title
1653
1653
  FROM sessions s
1654
1654
  JOIN projects p ON p.id = s.project_id
1655
1655
  WHERE s.id = ?`).get(o);if(!a)continue;let c=[];if(s.length>0){let u=n.prepare(`SELECT rowid FROM messages
@@ -1661,9 +1661,9 @@ ${n}`}function DR(e){return ve(e)?.auto_title_source??null}async function jR(e){
1661
1661
  WHERE session_id = ?
1662
1662
  AND is_sidechain = 0
1663
1663
  AND rowid BETWEEN ? AND ?
1664
- ORDER BY rowid`).all(o,f.rowid-2,f.rowid+2);for(let T of b){if(d.has(T.rowid))continue;d.add(T.rowid);let S=(T.content_text??"").slice(0,800);if(m+S.length>4e3)break;m+=S.length,c.push({role:T.role??"unknown",content:S})}if(m>=4e3)break}}r.push({session_id:o,short_id:o.slice(0,8),project:a.project,auto_title:a.auto_title,excerpts:c})}return r}var Pk="You are analyzing extracted bug findings from a developer's past Claude Code sessions. You are NOT being asked to fix code. You are being asked to synthesize patterns, identify likely root causes, or prioritize concerns based ONLY on the structured findings you are given. Output Markdown only, no preamble, no apologies, no questions.";function Fk(e,t){let n=[];n.push("[CONTEXT]"),n.push("Bug fingerprint:"),n.push(`- error_type: ${e.error_type??"unknown"}`),n.push(`- description: ${e.snippet||"(no snippet)"}`),n.push(`- file: ${e.file??"(no file recorded)"}`),n.push(`- occurrences: ${e.occurrence_count} sessions`),n.push(`- projects: ${e.projects.join(", ")}`),n.push(`- finding_id: ${e.message_hash??"(no hash)"}`),n.push(""),n.push("Member session excerpts:");for(let s of t){let r=s.auto_title??"(untitled)";if(n.push(`=== Session ${s.short_id} | ${s.project} | "${r}" ===`),s.excerpts.length===0)n.push("(no surrounding messages found for this snippet)");else for(let o of s.excerpts)n.push(`${o.role}: ${o.content}`);n.push("")}return n.join(`
1665
- `)}function $k(e,t,n){let s=[];s.push("[CONTEXT]"),s.push(`Project: ${e}`),s.push(`Total clusters: ${n}`),s.push(""),s.push("Clusters (sorted by occurrence_count desc):");for(let r of t)s.push(`- cluster_id: ${r.cluster_id}`),s.push(` error_type: ${r.error_type??"unknown"}`),s.push(` snippet: ${r.snippet||"(none)"}`),r.file&&s.push(` file: ${r.file}`),s.push(` occurrence_count: ${r.occurrence_count}`),s.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&s.push(` fix_summary: ${r.fix_summary}`),s.push("");return t.length<n&&s.push(`(Showing top ${t.length} of ${n} clusters by occurrence.)`),s.join(`
1666
- `)}var Uk=`[TASK]
1664
+ ORDER BY rowid`).all(o,f.rowid-2,f.rowid+2);for(let T of b){if(d.has(T.rowid))continue;d.add(T.rowid);let S=(T.content_text??"").slice(0,800);if(m+S.length>4e3)break;m+=S.length,c.push({role:T.role??"unknown",content:S})}if(m>=4e3)break}}r.push({session_id:o,short_id:o.slice(0,8),project:a.project,auto_title:a.auto_title,excerpts:c})}return r}var Jk="You are analyzing extracted bug findings from a developer's past Claude Code sessions. You are NOT being asked to fix code. You are being asked to synthesize patterns, identify likely root causes, or prioritize concerns based ONLY on the structured findings you are given. Output Markdown only, no preamble, no apologies, no questions.";function Gk(e,t){let n=[];n.push("[CONTEXT]"),n.push("Bug fingerprint:"),n.push(`- error_type: ${e.error_type??"unknown"}`),n.push(`- description: ${e.snippet||"(no snippet)"}`),n.push(`- file: ${e.file??"(no file recorded)"}`),n.push(`- occurrences: ${e.occurrence_count} sessions`),n.push(`- projects: ${e.projects.join(", ")}`),n.push(`- finding_id: ${e.message_hash??"(no hash)"}`),n.push(""),n.push("Member session excerpts:");for(let s of t){let r=s.auto_title??"(untitled)";if(n.push(`=== Session ${s.short_id} | ${s.project} | "${r}" ===`),s.excerpts.length===0)n.push("(no surrounding messages found for this snippet)");else for(let o of s.excerpts)n.push(`${o.role}: ${o.content}`);n.push("")}return n.join(`
1665
+ `)}function Yk(e,t,n){let s=[];s.push("[CONTEXT]"),s.push(`Project: ${e}`),s.push(`Total clusters: ${n}`),s.push(""),s.push("Clusters (sorted by occurrence_count desc):");for(let r of t)s.push(`- cluster_id: ${r.cluster_id}`),s.push(` error_type: ${r.error_type??"unknown"}`),s.push(` snippet: ${r.snippet||"(none)"}`),r.file&&s.push(` file: ${r.file}`),s.push(` occurrence_count: ${r.occurrence_count}`),s.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&s.push(` fix_summary: ${r.fix_summary}`),s.push("");return t.length<n&&s.push(`(Showing top ${t.length} of ${n} clusters by occurrence.)`),s.join(`
1666
+ `)}var zk=`[TASK]
1667
1667
  Output a Markdown synopsis with these sections:
1668
1668
 
1669
1669
  ## What this bug is
@@ -1685,7 +1685,7 @@ prefer "consider <pattern>" over "do <action>" unless the evidence
1685
1685
  is overwhelming.
1686
1686
 
1687
1687
  ## Confidence
1688
- One of: high / medium / low. With one sentence justifying the level.`,Hk=`[TASK]
1688
+ One of: high / medium / low. With one sentence justifying the level.`,Kk=`[TASK]
1689
1689
  Output a Markdown response with these sections:
1690
1690
 
1691
1691
  ## Most likely root cause
@@ -1702,7 +1702,7 @@ list is acceptable but explicitly say "(none found)".
1702
1702
  At most 2 alternatives, each with one sentence why it is less likely.
1703
1703
 
1704
1704
  ## Confidence
1705
- high / medium / low + one-sentence justification.`,Bk=`[TASK]
1705
+ high / medium / low + one-sentence justification.`,Vk=`[TASK]
1706
1706
  Output a Markdown response with these sections:
1707
1707
 
1708
1708
  ## Top 5 recurring concerns
@@ -1720,20 +1720,20 @@ Clusters that look small + high-confidence-fix. Empty list is fine.
1720
1720
  Clusters that suggest deeper issues (multiple files, repeated patterns).
1721
1721
 
1722
1722
  ## Confidence
1723
- high / medium / low.`;function Wk(e,t){let n;return e.scope==="cluster"?n=e.mode==="root_cause"?Hk:Uk:n=Bk,[Pk,"",t,"",n].join(`
1724
- `)}var qk=null;var Xn;function Xk(){return Xn!==void 0||(Xn=Nt("claude")??"claude"),Xn}function Jk(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
1723
+ high / medium / low.`;function Qk(e,t){let n;return e.scope==="cluster"?n=e.mode==="root_cause"?Kk:zk:n=Vk,[Jk,"",t,"",n].join(`
1724
+ `)}var Zk=null;var Xn;function e0(){return Xn!==void 0||(Xn=Nt("claude")??"claude"),Xn}function t0(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
1725
1725
  `);for(;s!==-1;){let r=t.slice(0,s);t=t.slice(s+1),r.length>0&&e(r),s=t.indexOf(`
1726
- `)}}}function Gk(e){return new Promise(t=>{let n=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],s="",r=0,o=0,a=null,c=Xk(),u=Lk(c,n,{stdio:["ignore","pipe","pipe"],shell:hs(c)||process.platform==="win32"&&Xn==="claude"}),d=()=>{try{u.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",d,{once:!0});let f=Jk(T=>{let S=T.trim();if(!S.startsWith("{"))return;let w;try{w=JSON.parse(S)}catch{return}if(!w||typeof w!="object")return;let A=w;if(A.type==="assistant"&&A.message?.content){for(let D of A.message.content)D?.type==="text"&&typeof D.text=="string"&&(s+=D.text,e.onPartial(D.text,s));let v=A.message?.usage;v&&(typeof v.input_tokens=="number"&&(r=Math.max(r,v.input_tokens)),typeof v.output_tokens=="number"&&(o=Math.max(o,v.output_tokens)))}});u.stdout.on("data",T=>f(T)),u.stderr.on("data",T=>{let S=T.toString("utf8");S.trim()&&(a=(a??"")+S)});let b=setTimeout(()=>{try{u.kill("SIGKILL")}catch{}},1800*1e3);u.on("close",T=>{clearTimeout(b),e.signal.removeEventListener("abort",d);let S=T===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${T}`);t({output_markdown:s,input_tokens:r,output_tokens:o,error:S})}),u.on("error",T=>{clearTimeout(b),e.signal.removeEventListener("abort",d),t({output_markdown:s,input_tokens:r,output_tokens:o,error:T instanceof Error?T.message:String(T)})})})}function Ar(e){if(e.scope==="cluster"){let r=Ik(e.target_id);return r?{cluster:r,context_summary:{cluster_count:1,session_count:r.member_session_ids.length,findings_count:r.occurrence_count}}:null}let t=Mk(e.target_id);if(t.length===0)return null;let n=t.slice(0,30),s=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:n,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:s}}}function Jm(e){let t=Ar(e.intent);if(!t)return{error:e.intent.scope==="cluster"?`cluster "${e.intent.target_id}" not found in any extracted findings`:`project "${e.intent.target_id}" has no extracted findings to synthesize`};let n=Wm(e.intent),s=Jn.get(n);if(s){let u=Vt.get(s);if(u&&u.status==="running")return{jobId:s,reused:!0};Jn.delete(n)}let r=Ok(),o=new AbortController,a=new Date().toISOString(),c={jobId:r,intent:e.intent,status:"running",startedAt:a,endedAt:null,events:[],waiters:new Set,controller:o,output_markdown:"",input_tokens:0,output_tokens:0,error:null,context_summary:t.context_summary,cleanupTimer:null};return Vt.set(r,c),Jn.set(n,r),oe({kind:"synth-launched",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:`${e.intent.scope}/${e.intent.mode}/${e.intent.target_id}`}),(async()=>{await Promise.resolve();try{let u;if(e.intent.scope==="cluster"&&t.cluster){let b=jk(t.cluster.member_session_ids,t.cluster.snippet);u=Fk(t.cluster,b)}else if(e.intent.scope==="project"&&t.project_clusters)u=$k(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=Wk(e.intent,u),f=await(qk??Gk)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(b,T)=>{c.output_markdown=T,qn(c,"partial",Kt(c))}});if(c.output_markdown=f.output_markdown,c.input_tokens=f.input_tokens,c.output_tokens=f.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),qn(c,"done",Kt(c)),oe({kind:"synth-cancelled",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else if(f.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=f.error,qn(c,"done",Kt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:f.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),qn(c,"done",Kt(c)),oe({kind:"synth-completed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});try{km({scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,output_markdown:c.output_markdown,input_tokens:c.input_tokens,output_tokens:c.output_tokens,job_id:r})}catch(b){console.error("[synthesize-jobs] failed to persist synthesis result:",b)}}}catch(u){let d=u instanceof Error?u.message:String(u??"unknown error");c.status="failed",c.endedAt=new Date().toISOString(),c.error=d,qn(c,"done",Kt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:d,input_tokens:c.input_tokens,output_tokens:c.output_tokens})}finally{vk(c)}})(),{jobId:r,reused:!1}}async function*Gm(e,t=0){let n=Vt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Ym(e){let t=Vt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function da(e){let t=Vt.get(e);return t?Kt(t):null}import{randomUUID as i0}from"node:crypto";U();Ns();import{randomUUID as n0}from"node:crypto";U();var zm=10,Km=20;function Yk(e){if(!e)return!1;let t=e.split(`
1727
- `);if(t.length<4)return!1;let n=0,s=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?n++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&s++;return n/t.length>.7||s/t.length>.5}function zk(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let n=new Float32Array(t),s=new Float32Array(e.buffer,e.byteOffset,t);return n.set(s),n}function Vm(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];if(t<=0)return!1;let n=1/Math.sqrt(t);for(let s=0;s<e.length;s++)e[s]*=n;return!0}function pa(e){if(e.length===0)return null;let t=e[0].length,n=new Float32Array(t);for(let s of e)if(s.length===t)for(let r=0;r<t;r++)n[r]+=s[r];for(let s=0;s<t;s++)n[s]/=e.length;return Vm(n)?n:null}function Qm(e){let t=new Map;if(e.length===0)return t;let n=h(),s=e.map(()=>"?").join(","),r=[];try{r=n.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1726
+ `)}}}function n0(e){return new Promise(t=>{let n=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],s="",r=0,o=0,a=null,c=e0(),u=$k(c,n,{stdio:["ignore","pipe","pipe"],shell:hs(c)||process.platform==="win32"&&Xn==="claude"}),d=()=>{try{u.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",d,{once:!0});let f=t0(T=>{let S=T.trim();if(!S.startsWith("{"))return;let R;try{R=JSON.parse(S)}catch{return}if(!R||typeof R!="object")return;let A=R;if(A.type==="assistant"&&A.message?.content){for(let D of A.message.content)D?.type==="text"&&typeof D.text=="string"&&(s+=D.text,e.onPartial(D.text,s));let v=A.message?.usage;v&&(typeof v.input_tokens=="number"&&(r=Math.max(r,v.input_tokens)),typeof v.output_tokens=="number"&&(o=Math.max(o,v.output_tokens)))}});u.stdout.on("data",T=>f(T)),u.stderr.on("data",T=>{let S=T.toString("utf8");S.trim()&&(a=(a??"")+S)});let b=setTimeout(()=>{try{u.kill("SIGKILL")}catch{}},1800*1e3);u.on("close",T=>{clearTimeout(b),e.signal.removeEventListener("abort",d);let S=T===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${T}`);t({output_markdown:s,input_tokens:r,output_tokens:o,error:S})}),u.on("error",T=>{clearTimeout(b),e.signal.removeEventListener("abort",d),t({output_markdown:s,input_tokens:r,output_tokens:o,error:T instanceof Error?T.message:String(T)})})})}function Nr(e){if(e.scope==="cluster"){let r=Hk(e.target_id);return r?{cluster:r,context_summary:{cluster_count:1,session_count:r.member_session_ids.length,findings_count:r.occurrence_count}}:null}let t=Wk(e.target_id);if(t.length===0)return null;let n=t.slice(0,30),s=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:n,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:s}}}function Zm(e){let t=Nr(e.intent);if(!t)return{error:e.intent.scope==="cluster"?`cluster "${e.intent.target_id}" not found in any extracted findings`:`project "${e.intent.target_id}" has no extracted findings to synthesize`};let n=Km(e.intent),s=Jn.get(n);if(s){let u=Vt.get(s);if(u&&u.status==="running")return{jobId:s,reused:!0};Jn.delete(n)}let r=Fk(),o=new AbortController,a=new Date().toISOString(),c={jobId:r,intent:e.intent,status:"running",startedAt:a,endedAt:null,events:[],waiters:new Set,controller:o,output_markdown:"",input_tokens:0,output_tokens:0,error:null,context_summary:t.context_summary,cleanupTimer:null};return Vt.set(r,c),Jn.set(n,r),oe({kind:"synth-launched",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:`${e.intent.scope}/${e.intent.mode}/${e.intent.target_id}`}),(async()=>{await Promise.resolve();try{let u;if(e.intent.scope==="cluster"&&t.cluster){let b=Xk(t.cluster.member_session_ids,t.cluster.snippet);u=Gk(t.cluster,b)}else if(e.intent.scope==="project"&&t.project_clusters)u=Yk(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=Qk(e.intent,u),f=await(Zk??n0)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(b,T)=>{c.output_markdown=T,qn(c,"partial",Kt(c))}});if(c.output_markdown=f.output_markdown,c.input_tokens=f.input_tokens,c.output_tokens=f.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),qn(c,"done",Kt(c)),oe({kind:"synth-cancelled",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else if(f.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=f.error,qn(c,"done",Kt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:f.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),qn(c,"done",Kt(c)),oe({kind:"synth-completed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});try{vm({scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,output_markdown:c.output_markdown,input_tokens:c.input_tokens,output_tokens:c.output_tokens,job_id:r})}catch(b){console.error("[synthesize-jobs] failed to persist synthesis result:",b)}}}catch(u){let d=u instanceof Error?u.message:String(u??"unknown error");c.status="failed",c.endedAt=new Date().toISOString(),c.error=d,qn(c,"done",Kt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:d,input_tokens:c.input_tokens,output_tokens:c.output_tokens})}finally{Bk(c)}})(),{jobId:r,reused:!1}}async function*eg(e,t=0){let n=Vt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function tg(e){let t=Vt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function _a(e){let t=Vt.get(e);return t?Kt(t):null}import{randomUUID as f0}from"node:crypto";U();Ns();import{randomUUID as d0}from"node:crypto";U();var ng=10,sg=20;function s0(e){if(!e)return!1;let t=e.split(`
1727
+ `);if(t.length<4)return!1;let n=0,s=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?n++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&s++;return n/t.length>.7||s/t.length>.5}function r0(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let n=new Float32Array(t),s=new Float32Array(e.buffer,e.byteOffset,t);return n.set(s),n}function rg(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];if(t<=0)return!1;let n=1/Math.sqrt(t);for(let s=0;s<e.length;s++)e[s]*=n;return!0}function ha(e){if(e.length===0)return null;let t=e[0].length,n=new Float32Array(t);for(let s of e)if(s.length===t)for(let r=0;r<t;r++)n[r]+=s[r];for(let s=0;s<t;s++)n[s]/=e.length;return rg(n)?n:null}function og(e){let t=new Map;if(e.length===0)return t;let n=h(),s=e.map(()=>"?").join(","),r=[];try{r=n.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1728
1728
  cm.text AS text, v.embedding AS embedding
1729
1729
  FROM chunk_meta cm
1730
1730
  JOIN vec_chunks v ON v.rowid = cm.rowid
1731
1731
  WHERE cm.stale = 0
1732
1732
  AND cm.session_id IN (${s})
1733
- ORDER BY cm.session_id, cm.rowid ASC`).all(...e)}catch{return t}if(r.length===0)return t;let o=new Map;for(let a of r){let c=o.get(a.session_id);c||(c=[],o.set(a.session_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(I=>!Yk(I.text)),d=u.length>0?u:c,m=[];for(let I of d){let P=zk(I.embedding);P&&Vm(P)&&m.push(P)}if(m.length===0)continue;let f=Math.min(zm,m.length),b=Math.max(0,m.length-zm),T=m.slice(0,f),S=m.slice(b),w=pa(T),A=pa(S),v=pa(m),D=new Map;for(let I=0;I<T.length&&D.size<Km;I++)D.set(I,T[I]);for(let I=0;I<S.length&&D.size<Km;I++)D.set(b+I,S[I]);let X=Array.from(D.values());t.set(a,{session_id:a,full_mean:v,head_pool:w,tail_pool:A,sample_chunks:X})}return t}function Kk(e,t){if(e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function Zm(e,t=.8){let n=new Map,s=[],r=[];for(let[d,m]of e)m.full_mean&&(s.push(d),r.push(m.full_mean));if(s.length===0)return n;let o=new Int32Array(s.length);for(let d=0;d<o.length;d++)o[d]=d;let a=d=>{let m=d;for(;o[m]!==m;)m=o[m];let f=d;for(;o[f]!==m;){let b=o[f];o[f]=m,f=b}return m},c=(d,m)=>{let f=a(d),b=a(m);f!==b&&(o[f]=b)};for(let d=0;d<s.length;d++)for(let m=d+1;m<s.length;m++)Kk(r[d],r[m])>=t&&c(d,m);let u=new Map;for(let d=0;d<s.length;d++){let m=a(d),f=u.get(m);f===void 0&&(f=u.size,u.set(m,f)),n.set(s[d],f)}return n}var Xe={lo:.4,hi:.7},Qt="claude-haiku-4-5-20251001",Zt=50,eg=1500,tg=50,Vk={same_workflow:.15,unrelated:-.2,unsure:0};function ng(e,t,n=Xe){if(n.lo>n.hi)throw new Error(`borderline band invalid: lo=${n.lo} > hi=${n.hi}`);let s=[];for(let r of e){if(r.confidence<n.lo||r.confidence>n.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||s.push({parent:o,child:a,step1:r})}return s.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),s}function sg(e,t=Xe){let n=0;for(let s of e)s.confidence>=t.lo&&s.confidence<=t.hi&&n++;return n}function Qk(e,t){let n=e.replace(/\s+/g," ").trim();return n.length>t?n.slice(0,t-1)+"\u2026":n}function Zk(e,t){if(e===null)return"unknown";let n=t-e;if(n<0)return"overlap";let s=Math.round(n/6e4);if(s<60)return`${s}m`;let r=Math.round(n/36e5);return r<24?`${r}h`:`${Math.round(n/864e5)}d`}function e0(e){let n=e.parent.recent_user_messages,s=e.child.recent_user_messages,r=n.slice(0,3),o=n.length>3?n.slice(-3):[],a=s.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(m=>` - ${Qk(m,500)}`).join(`
1734
- `),u=Zk(e.parent.ended_at_ms??e.parent.started_at_ms,e.child.started_at_ms);return["You are evaluating whether two Claude Code sessions belong to the same continuous workflow.","","A deterministic scorer rated this pair in the borderline band. Its signals:",e.step1.reasons.length===0?" (no signals fired strongly)":e.step1.reasons.map(d=>` - ${d}`).join(`
1733
+ ORDER BY cm.session_id, cm.rowid ASC`).all(...e)}catch{return t}if(r.length===0)return t;let o=new Map;for(let a of r){let c=o.get(a.session_id);c||(c=[],o.set(a.session_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(I=>!s0(I.text)),d=u.length>0?u:c,m=[];for(let I of d){let P=r0(I.embedding);P&&rg(P)&&m.push(P)}if(m.length===0)continue;let f=Math.min(ng,m.length),b=Math.max(0,m.length-ng),T=m.slice(0,f),S=m.slice(b),R=ha(T),A=ha(S),v=ha(m),D=new Map;for(let I=0;I<T.length&&D.size<sg;I++)D.set(I,T[I]);for(let I=0;I<S.length&&D.size<sg;I++)D.set(b+I,S[I]);let X=Array.from(D.values());t.set(a,{session_id:a,full_mean:v,head_pool:R,tail_pool:A,sample_chunks:X})}return t}function o0(e,t){if(e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function ig(e,t=.8){let n=new Map,s=[],r=[];for(let[d,m]of e)m.full_mean&&(s.push(d),r.push(m.full_mean));if(s.length===0)return n;let o=new Int32Array(s.length);for(let d=0;d<o.length;d++)o[d]=d;let a=d=>{let m=d;for(;o[m]!==m;)m=o[m];let f=d;for(;o[f]!==m;){let b=o[f];o[f]=m,f=b}return m},c=(d,m)=>{let f=a(d),b=a(m);f!==b&&(o[f]=b)};for(let d=0;d<s.length;d++)for(let m=d+1;m<s.length;m++)o0(r[d],r[m])>=t&&c(d,m);let u=new Map;for(let d=0;d<s.length;d++){let m=a(d),f=u.get(m);f===void 0&&(f=u.size,u.set(m,f)),n.set(s[d],f)}return n}var Xe={lo:.4,hi:.7},Qt="claude-haiku-4-5-20251001",Zt=50,ag=1500,cg=50,i0={same_workflow:.15,unrelated:-.2,unsure:0};function lg(e,t,n=Xe){if(n.lo>n.hi)throw new Error(`borderline band invalid: lo=${n.lo} > hi=${n.hi}`);let s=[];for(let r of e){if(r.confidence<n.lo||r.confidence>n.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||s.push({parent:o,child:a,step1:r})}return s.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),s}function ug(e,t=Xe){let n=0;for(let s of e)s.confidence>=t.lo&&s.confidence<=t.hi&&n++;return n}function a0(e,t){let n=e.replace(/\s+/g," ").trim();return n.length>t?n.slice(0,t-1)+"\u2026":n}function c0(e,t){if(e===null)return"unknown";let n=t-e;if(n<0)return"overlap";let s=Math.round(n/6e4);if(s<60)return`${s}m`;let r=Math.round(n/36e5);return r<24?`${r}h`:`${Math.round(n/864e5)}d`}function l0(e){let n=e.parent.recent_user_messages,s=e.child.recent_user_messages,r=n.slice(0,3),o=n.length>3?n.slice(-3):[],a=s.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(m=>` - ${a0(m,500)}`).join(`
1734
+ `),u=c0(e.parent.ended_at_ms??e.parent.started_at_ms,e.child.started_at_ms);return["You are evaluating whether two Claude Code sessions belong to the same continuous workflow.","","A deterministic scorer rated this pair in the borderline band. Its signals:",e.step1.reasons.length===0?" (no signals fired strongly)":e.step1.reasons.map(d=>` - ${d}`).join(`
1735
1735
  `),` step1_confidence: ${e.step1.confidence.toFixed(2)}`,"","PARENT SESSION","First user messages:",c(r),"Last user messages:",c(o),"",`CHILD SESSION (gap from parent: ${u})`,"First user messages:",c(a),"","Reply with EXACTLY one JSON object on a single line, no prose, no markdown:",'{"verdict":"same_workflow"|"unrelated"|"unsure","reason":"<one short sentence>"}',"","Guidance:",'- "same_workflow" = the child is clearly continuing what the parent was doing.','- "unrelated" = different problem, different files, different intent.','- "unsure" = could go either way; do not force a verdict.'].join(`
1736
- `)}function t0(e){if(!e)return null;let t=e.trim();if(!t)return null;let n=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(n=u.result.trim())}catch{}let s=n.match(/\{[\s\S]*\}/);if(!s)return null;let r;try{r=JSON.parse(s[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,a=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(a!=="same_workflow"&&a!=="unrelated"&&a!=="unsure")return null;let c=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:a,reason:c,delta:Vk[a]}}async function rg(e,t={}){if(t.signal?.aborted)return null;let n=t.model??Qt,s=e0(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(we(),pt));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(s,{model:n}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:t0(o.stdout)}function og(e){let t=[];for(let n of e.proposals){let s=`${n.parent_id}::${n.child_id}`,r=e.rescored.get(s);if(!r){t.push(n);continue}let o=Math.max(0,Math.min(1,n.confidence+r.delta));if(o<e.applyThreshold)continue;let a=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...n,confidence:o,reasons:[...n.reasons,a]})}return t}function ig(e){return`${e.parent_id}::${e.child_id}`}async function s0(e){if(e.signal?.aborted)return null;let t,n;try{({spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(we(),pt)))}catch{return null}if(!n())return null;let s=[];for(let o of e.sessionIds){let a=e.rowById.get(o);if(!a)continue;let c=a.alias||a.auto_title||(a.first_user_message?a.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");s.push(`- ${c}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1736
+ `)}function u0(e){if(!e)return null;let t=e.trim();if(!t)return null;let n=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(n=u.result.trim())}catch{}let s=n.match(/\{[\s\S]*\}/);if(!s)return null;let r;try{r=JSON.parse(s[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,a=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(a!=="same_workflow"&&a!=="unrelated"&&a!=="unsure")return null;let c=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:a,reason:c,delta:i0[a]}}async function dg(e,t={}){if(t.signal?.aborted)return null;let n=t.model??Qt,s=l0(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(we(),pt));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(s,{model:n}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:u0(o.stdout)}function pg(e){let t=[];for(let n of e.proposals){let s=`${n.parent_id}::${n.child_id}`,r=e.rescored.get(s);if(!r){t.push(n);continue}let o=Math.max(0,Math.min(1,n.confidence+r.delta));if(o<e.applyThreshold)continue;let a=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...n,confidence:o,reasons:[...n.reasons,a]})}return t}function mg(e){return`${e.parent_id}::${e.child_id}`}async function p0(e){if(e.signal?.aborted)return null;let t,n;try{({spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(we(),pt)))}catch{return null}if(!n())return null;let s=[];for(let o of e.sessionIds){let a=e.rowById.get(o);if(!a)continue;let c=a.alias||a.auto_title||(a.first_user_message?a.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");s.push(`- ${c}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1737
1737
  - 4 to 8 words
1738
1738
  - Title-case, no trailing punctuation
1739
1739
  - Capture the WORKFLOW (e.g., "Update Remotion deps + lint cleanup"), not any one session
@@ -1744,12 +1744,12 @@ Sessions:
1744
1744
  `+s.join(`
1745
1745
  `)+`
1746
1746
 
1747
- Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(f=>{e.signal&&(a=()=>f(null),e.signal.addEventListener("abort",a,{once:!0}))}),u=await Promise.race([o,c]);if(a&&e.signal&&e.signal.removeEventListener("abort",a),!u||!u.success)return null;let d=u.stdout.trim();if(!d)return null;let m;try{let f=JSON.parse(d);m=typeof f.result=="string"?f.result:d}catch{m=d}return m=m.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),m?m.length>80?m.slice(0,77)+"...":m:null}catch{return null}}function ma(e){return r0(e)}function ga(e){let t=new Map;for(let o of e){let a=t.get(o.project);a||(a=[],t.set(o.project,a)),a.push(o)}let n=e.map(o=>o.id),s=new Map;try{s=Qm(n)}catch{s=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let m of a){let f=s.get(m.id);f&&c.set(m.id,f)}let u=Zm(c,.8),d=[];for(let m of a){let f=o0(m,s,u);f&&d.push(f)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function cg(e){return h().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1747
+ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(f=>{e.signal&&(a=()=>f(null),e.signal.addEventListener("abort",a,{once:!0}))}),u=await Promise.race([o,c]);if(a&&e.signal&&e.signal.removeEventListener("abort",a),!u||!u.success)return null;let d=u.stdout.trim();if(!d)return null;let m;try{let f=JSON.parse(d);m=typeof f.result=="string"?f.result:d}catch{m=d}return m=m.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),m?m.length>80?m.slice(0,77)+"...":m:null}catch{return null}}function Ea(e){return m0(e)}function ba(e){let t=new Map;for(let o of e){let a=t.get(o.project);a||(a=[],t.set(o.project,a)),a.push(o)}let n=e.map(o=>o.id),s=new Map;try{s=og(n)}catch{s=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let m of a){let f=s.get(m.id);f&&c.set(m.id,f)}let u=ig(c,.8),d=[];for(let m of a){let f=g0(m,s,u);f&&d.push(f)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function fg(e){return h().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1748
1748
  FROM threads t
1749
1749
  JOIN thread_edges te ON te.thread_id = t.id
1750
1750
  JOIN sessions s ON s.id = te.session_id
1751
1751
  JOIN projects p ON p.id = s.project_id
1752
- WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function r0(e){let t=h(),n={},s="1=1 AND s.message_count > 2";return s+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(s+=" AND p.name = @project",n.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1752
+ WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function m0(e){let t=h(),n={},s="1=1 AND s.message_count > 2";return s+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(s+=" AND p.name = @project",n.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1753
1753
  s.first_user_message, s.auto_title,
1754
1754
  NULLIF(sa.alias, '') AS alias,
1755
1755
  s.file_path
@@ -1757,21 +1757,21 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1757
1757
  JOIN projects p ON p.id = s.project_id
1758
1758
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1759
1759
  WHERE ${s}
1760
- ORDER BY p.name ASC, s.started_at ASC`).all(n)}function o0(e,t,n){if(!e.started_at)return null;let s=Date.parse(e.started_at);if(!Number.isFinite(s))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=hr(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:s,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:a?.full_mean??null,head_pool:a?.head_pool??null,tail_pool:a?.tail_pool??null,sample_chunks:a?.sample_chunks??[],cluster_id:n?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function ag(e){let t=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,60).replace(/\n/g," ").trim():`thread from ${e.id.slice(0,8)}`);return t.length>80?t.slice(0,77)+"...":t}async function lg(e){if(!e.rescore.enabled)return{proposals:e.proposals,ran:!1,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};let t=e.rescore.band??Xe,n=e.rescore.cap??Zt,s=e.rescore.model??Qt,r=new Map(e.scannables.map(b=>[b.id,b])),o=ng(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>n)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let a=new Map,c=0,u=0,d=0,m=0;for(let b=0;b<o.length&&!e.signal?.aborted;b++){let T=o[b],S=await rg(T,{model:s,signal:e.signal});S?(a.set(ig(T.step1),S),S.verdict==="same_workflow"?c++:S.verdict==="unrelated"?u++:d++):m++,e.onProgress?.({phase:"rescoring",current:b+1,total:o.length,verdict:S?.verdict??"failed"})}return{proposals:og({proposals:e.proposals,rescored:a,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:c,demoted:u,unsure:d,failed:m,capped:!1}}async function ug(e){let t=h(),n=new Map(e.rows.map(d=>[d.id,d])),s=vp(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of s){if(c++,e.signal?.aborted)break;let m=n.get(d.rootId),f=ag(m);if(!e.llmNames){a.set(d.rootId,f),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:f});continue}let T=await s0({rootRow:m,sessionIds:d.sessionIds,rowById:n,signal:e.signal,model:e.model})??f;a.set(d.rootId,T),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:T})}return t.transaction(()=>{for(let d of s){if(!a.has(d.rootId))continue;let m=n.get(d.rootId),f=`auto-scan-${n0()}`,b=a.get(d.rootId)??ag(m),T=`Auto-detected workflow chain (${d.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(f,b,T,r),t.prepare(`INSERT INTO thread_edges
1760
+ ORDER BY p.name ASC, s.started_at ASC`).all(n)}function g0(e,t,n){if(!e.started_at)return null;let s=Date.parse(e.started_at);if(!Number.isFinite(s))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=br(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:s,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:a?.full_mean??null,head_pool:a?.head_pool??null,tail_pool:a?.tail_pool??null,sample_chunks:a?.sample_chunks??[],cluster_id:n?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function gg(e){let t=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,60).replace(/\n/g," ").trim():`thread from ${e.id.slice(0,8)}`);return t.length>80?t.slice(0,77)+"...":t}async function _g(e){if(!e.rescore.enabled)return{proposals:e.proposals,ran:!1,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};let t=e.rescore.band??Xe,n=e.rescore.cap??Zt,s=e.rescore.model??Qt,r=new Map(e.scannables.map(b=>[b.id,b])),o=lg(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>n)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let a=new Map,c=0,u=0,d=0,m=0;for(let b=0;b<o.length&&!e.signal?.aborted;b++){let T=o[b],S=await dg(T,{model:s,signal:e.signal});S?(a.set(mg(T.step1),S),S.verdict==="same_workflow"?c++:S.verdict==="unrelated"?u++:d++):m++,e.onProgress?.({phase:"rescoring",current:b+1,total:o.length,verdict:S?.verdict??"failed"})}return{proposals:pg({proposals:e.proposals,rescored:a,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:c,demoted:u,unsure:d,failed:m,capped:!1}}async function hg(e){let t=h(),n=new Map(e.rows.map(d=>[d.id,d])),s=$p(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of s){if(c++,e.signal?.aborted)break;let m=n.get(d.rootId),f=gg(m);if(!e.llmNames){a.set(d.rootId,f),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:f});continue}let T=await p0({rootRow:m,sessionIds:d.sessionIds,rowById:n,signal:e.signal,model:e.model})??f;a.set(d.rootId,T),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:T})}return t.transaction(()=>{for(let d of s){if(!a.has(d.rootId))continue;let m=n.get(d.rootId),f=`auto-scan-${d0()}`,b=a.get(d.rootId)??gg(m),T=`Auto-detected workflow chain (${d.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(f,b,T,r),t.prepare(`INSERT INTO thread_edges
1761
1761
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1762
- VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(f,d.rootId,r);let S=new Map;for(let w of e.edges)d.sessionIds.includes(w.child_id)&&S.set(w.child_id,w);for(let w of d.sessionIds){if(w===d.rootId)continue;let A=S.get(w);A&&t.prepare(`INSERT INTO thread_edges
1762
+ VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(f,d.rootId,r);let S=new Map;for(let R of e.edges)d.sessionIds.includes(R.child_id)&&S.set(R.child_id,R);for(let R of d.sessionIds){if(R===d.rootId)continue;let A=S.get(R);A&&t.prepare(`INSERT INTO thread_edges
1763
1763
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1764
- VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(f,w,A.parent_id,A.confidence,r)}o.push({thread_id:f,name:b,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var tn=new Map,Gn=new Map,a0=300*1e3;function en(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function c0(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{tn.delete(e.jobId),Gn.get(e.project)===e.jobId&&Gn.delete(e.project)},a0),e.cleanupTimer.unref?.())}function xr(e){return{jobId:e.jobId,project:e.project,threshold:e.threshold,llm_names:e.llmNames,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total_threads:e.totalThreads,threads_named:e.threadsNamed,edges_written:e.edgesWritten,threads_created:e.threadsCreated,current_thread_name:e.currentThreadName,error:e.error,rescore:e.rescore}}var l0=500,u0=30,fa="claude-haiku-4-5-20251001",dg={"claude-haiku-4-5-20251001":{in:1,out:5,label:"Haiku 4.5"},"claude-sonnet-4-6":{in:3,out:15,label:"Sonnet 4.6"},"claude-opus-4-7":{in:15,out:75,label:"Opus 4.7"}};function pg(e){return dg[e]??dg[fa]}function mg(e){let t=typeof e.threshold=="number"?e.threshold:fr;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.model??fa,s="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Xe.lo,hi:e.llm_rescore?.band_hi??Xe.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??Qt,c=ma({project:e.project});if(c.length===0)return{eligible_sessions:0,proposed_edges:0,estimated_threads:0,estimated_llm_calls:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,existing_auto_scan_threads:0,plan_window_estimate:tt(0,n),accuracy_caveat:s,model:n,llm_rescore:r?{enabled:!0,band:o,cap:Zt,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:tt(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=ga(c),m=u.get(e.project)??[],f=d.get(e.project)??[],b=r?Math.min(t,o.lo):t,T=qt(f,b),S=r?T.filter(i=>i.confidence>=t):T,w=gg(S),A=w*l0,v=w*u0,D=pg(n),X=A/1e6*D.in,I=v/1e6*D.out,P=X+I,te=tt(w,n),$=cg(e.project),ne=null;if(r){let i=sg(T,o),l=pg(a),p=i*eg,g=i*tg,_=p/1e6*l.in+g/1e6*l.out;ne={enabled:!0,band:o,cap:Zt,estimated_borderline_pairs:i,estimated_input_tokens_max:p,estimated_output_tokens_max:g,estimated_cost_usd_max:Math.round(_*1e4)/1e4,plan_window_estimate:tt(i,a),exceeds_cap:i>Zt,model:a}}return{eligible_sessions:m.length,proposed_edges:S.length,estimated_threads:w,estimated_llm_calls:w,estimated_input_tokens_max:A,estimated_output_tokens_max:v,estimated_cost_usd_max:Math.round(P*1e4)/1e4,existing_auto_scan_threads:$,plan_window_estimate:te,accuracy_caveat:s,model:n,llm_rescore:ne}}function gg(e){let t=new Map,n=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},s=(o,a)=>{let c=n(o),u=n(a);c!==u&&t.set(c,u)};for(let o of e)t.has(o.parent_id)||t.set(o.parent_id,o.parent_id),t.has(o.child_id)||t.set(o.child_id,o.child_id),s(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(n(o));return r.size}function fg(e){let t=typeof e.threshold=="number"?e.threshold:fr;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.llm_names??!0,s=e.model??fa,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Xe.lo,hi:e.llm_rescore?.band_hi??Xe.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??Qt,c=Gn.get(e.project);if(c){let P=tn.get(c);if(P&&P.status==="running")return{jobId:c,reused:!0};Gn.delete(e.project)}let u=ma({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:m}=ga(u),f=d.get(e.project),b=m.get(e.project);if(!f||!b)return{error:`project "${e.project}" not found`};let T=r?Math.min(t,o.lo):t,S=qt(b,T),w=r?S.filter(P=>P.confidence>=t):S;if(S.length===0||w.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let A=i0(),v=new AbortController,D=gg(w),X=new Date().toISOString(),I={jobId:A,project:e.project,threshold:t,llmNames:n,status:"running",startedAt:X,endedAt:null,events:[],waiters:new Set,controller:v,totalThreads:D,threadsNamed:0,edgesWritten:0,threadsCreated:0,currentThreadName:null,error:null,cleanupTimer:null,rescore:r?{enabled:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1}:null};return tn.set(A,I),Gn.set(e.project,A),(async()=>{await Promise.resolve();try{en(I,"progress",{phase:"linking",edges_proposed:S.length,estimated_threads:D});let P=S;if(r){let $=await lg({proposals:S,scannables:b,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:v.signal,onProgress:ne=>{ne.phase==="rescoring"&&en(I,"progress",{phase:"rescoring",current:ne.current,total:ne.total,verdict:ne.verdict})}});P=$.proposals,I.rescore={enabled:!0,considered:$.considered,promoted:$.promoted,demoted:$.demoted,unsure:$.unsure,failed:$.failed,capped:$.capped}}else P=S.filter($=>$.confidence>=t);if(P.length===0){I.status=v.signal.aborted?"cancelled":"done",I.endedAt=new Date().toISOString(),en(I,"done",{...xr(I),threads:[]});return}let te=await ug({project:e.project,rows:f,edges:P,scannables:b,llmNames:n,model:s,signal:v.signal,onProgress:$=>{$.phase==="naming"&&(I.threadsNamed=$.current,I.currentThreadName=$.thread_name??null,en(I,"progress",{phase:"naming",current:$.current,total:$.total,thread_name:$.thread_name}))}});I.threadsCreated=te.threads.length,I.edgesWritten=P.length+te.threads.length,I.status=v.signal.aborted?"cancelled":"done",I.endedAt=new Date().toISOString(),en(I,"done",{...xr(I),threads:te.threads})}catch(P){I.status="failed",I.endedAt=new Date().toISOString(),I.error=P instanceof Error?P.message:String(P??"unknown error"),en(I,"done",xr(I))}finally{c0(I)}})(),{jobId:A,reused:!1}}async function*_g(e,t=0){let n=tn.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function hg(e){let t=tn.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function _a(e){let t=tn.get(e);return t?xr(t):null}fs();import{randomUUID as d0}from"node:crypto";var Nr=new Map;function Eg(e){let t={id:d0(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return Nr.set(t.id,t),t}function Or(e){return Nr.get(e)}function nn(e,t){for(let n of e.listeners)n(t)}function bg(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function Sg(e){let t=Nr.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),nn(t,{type:"status",status:"cancelled"}),!0):!1}function Tg(e){return Nr.delete(e)}At();function Lr(e){let{session:t,knownTags:n,minTags:s,maxTags:r}=e,o=n.slice(0,50).map(c=>c.tag).join(", "),a=t.current_tags.length>0?`already applied, do not repeat: [${t.current_tags.join(", ")}]`:"currently has no tags";return[`You are tagging a software engineering session. Produce ${s}-${r} concise, lowercase, hyphen-separated tags that describe:`," - the domain or subsystem touched (auth, db, frontend, ...)"," - the kind of work (bugfix, feature, refactor, research, ...)"," - any specific tool, library, or product if prominent","","Prefer tags from the known-tags list below when applicable \u2014 consistency over creativity. Never invent marketing-sounding labels. Never tag based on speculation; only on observed work.","",`known tags (most used first, up to 50): ${o||"(none yet)"}`,"","Session:",` project: ${t.project}`,t.alias?` alias: ${JSON.stringify(t.alias)}`:" alias: (none)",t.git_branch?` git_branch: ${t.git_branch}`:"",` ${a}`," first_user_message:",yg(t.first_user_message," ")," message_sample:",yg(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${s}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1765
- `)}function yg(e,t){return e.split(`
1764
+ VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(f,R,A.parent_id,A.confidence,r)}o.push({thread_id:f,name:b,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var tn=new Map,Gn=new Map,_0=300*1e3;function en(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function h0(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{tn.delete(e.jobId),Gn.get(e.project)===e.jobId&&Gn.delete(e.project)},_0),e.cleanupTimer.unref?.())}function Or(e){return{jobId:e.jobId,project:e.project,threshold:e.threshold,llm_names:e.llmNames,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total_threads:e.totalThreads,threads_named:e.threadsNamed,edges_written:e.edgesWritten,threads_created:e.threadsCreated,current_thread_name:e.currentThreadName,error:e.error,rescore:e.rescore}}var E0=500,b0=30,Sa="claude-haiku-4-5-20251001",Eg={"claude-haiku-4-5-20251001":{in:1,out:5,label:"Haiku 4.5"},"claude-sonnet-4-6":{in:3,out:15,label:"Sonnet 4.6"},"claude-opus-4-7":{in:15,out:75,label:"Opus 4.7"}};function bg(e){return Eg[e]??Eg[Sa]}function Sg(e){let t=typeof e.threshold=="number"?e.threshold:hr;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.model??Sa,s="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Xe.lo,hi:e.llm_rescore?.band_hi??Xe.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??Qt,c=Ea({project:e.project});if(c.length===0)return{eligible_sessions:0,proposed_edges:0,estimated_threads:0,estimated_llm_calls:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,existing_auto_scan_threads:0,plan_window_estimate:tt(0,n),accuracy_caveat:s,model:n,llm_rescore:r?{enabled:!0,band:o,cap:Zt,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:tt(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=ba(c),m=u.get(e.project)??[],f=d.get(e.project)??[],b=r?Math.min(t,o.lo):t,T=qt(f,b),S=r?T.filter(i=>i.confidence>=t):T,R=Tg(S),A=R*E0,v=R*b0,D=bg(n),X=A/1e6*D.in,I=v/1e6*D.out,P=X+I,te=tt(R,n),$=fg(e.project),ne=null;if(r){let i=ug(T,o),l=bg(a),p=i*ag,g=i*cg,_=p/1e6*l.in+g/1e6*l.out;ne={enabled:!0,band:o,cap:Zt,estimated_borderline_pairs:i,estimated_input_tokens_max:p,estimated_output_tokens_max:g,estimated_cost_usd_max:Math.round(_*1e4)/1e4,plan_window_estimate:tt(i,a),exceeds_cap:i>Zt,model:a}}return{eligible_sessions:m.length,proposed_edges:S.length,estimated_threads:R,estimated_llm_calls:R,estimated_input_tokens_max:A,estimated_output_tokens_max:v,estimated_cost_usd_max:Math.round(P*1e4)/1e4,existing_auto_scan_threads:$,plan_window_estimate:te,accuracy_caveat:s,model:n,llm_rescore:ne}}function Tg(e){let t=new Map,n=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},s=(o,a)=>{let c=n(o),u=n(a);c!==u&&t.set(c,u)};for(let o of e)t.has(o.parent_id)||t.set(o.parent_id,o.parent_id),t.has(o.child_id)||t.set(o.child_id,o.child_id),s(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(n(o));return r.size}function yg(e){let t=typeof e.threshold=="number"?e.threshold:hr;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.llm_names??!0,s=e.model??Sa,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Xe.lo,hi:e.llm_rescore?.band_hi??Xe.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??Qt,c=Gn.get(e.project);if(c){let P=tn.get(c);if(P&&P.status==="running")return{jobId:c,reused:!0};Gn.delete(e.project)}let u=Ea({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:m}=ba(u),f=d.get(e.project),b=m.get(e.project);if(!f||!b)return{error:`project "${e.project}" not found`};let T=r?Math.min(t,o.lo):t,S=qt(b,T),R=r?S.filter(P=>P.confidence>=t):S;if(S.length===0||R.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let A=f0(),v=new AbortController,D=Tg(R),X=new Date().toISOString(),I={jobId:A,project:e.project,threshold:t,llmNames:n,status:"running",startedAt:X,endedAt:null,events:[],waiters:new Set,controller:v,totalThreads:D,threadsNamed:0,edgesWritten:0,threadsCreated:0,currentThreadName:null,error:null,cleanupTimer:null,rescore:r?{enabled:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1}:null};return tn.set(A,I),Gn.set(e.project,A),(async()=>{await Promise.resolve();try{en(I,"progress",{phase:"linking",edges_proposed:S.length,estimated_threads:D});let P=S;if(r){let $=await _g({proposals:S,scannables:b,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:v.signal,onProgress:ne=>{ne.phase==="rescoring"&&en(I,"progress",{phase:"rescoring",current:ne.current,total:ne.total,verdict:ne.verdict})}});P=$.proposals,I.rescore={enabled:!0,considered:$.considered,promoted:$.promoted,demoted:$.demoted,unsure:$.unsure,failed:$.failed,capped:$.capped}}else P=S.filter($=>$.confidence>=t);if(P.length===0){I.status=v.signal.aborted?"cancelled":"done",I.endedAt=new Date().toISOString(),en(I,"done",{...Or(I),threads:[]});return}let te=await hg({project:e.project,rows:f,edges:P,scannables:b,llmNames:n,model:s,signal:v.signal,onProgress:$=>{$.phase==="naming"&&(I.threadsNamed=$.current,I.currentThreadName=$.thread_name??null,en(I,"progress",{phase:"naming",current:$.current,total:$.total,thread_name:$.thread_name}))}});I.threadsCreated=te.threads.length,I.edgesWritten=P.length+te.threads.length,I.status=v.signal.aborted?"cancelled":"done",I.endedAt=new Date().toISOString(),en(I,"done",{...Or(I),threads:te.threads})}catch(P){I.status="failed",I.endedAt=new Date().toISOString(),I.error=P instanceof Error?P.message:String(P??"unknown error"),en(I,"done",Or(I))}finally{h0(I)}})(),{jobId:A,reused:!1}}async function*wg(e,t=0){let n=tn.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Rg(e){let t=tn.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Ta(e){let t=tn.get(e);return t?Or(t):null}fs();import{randomUUID as S0}from"node:crypto";var Cr=new Map;function kg(e){let t={id:S0(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return Cr.set(t.id,t),t}function Lr(e){return Cr.get(e)}function nn(e,t){for(let n of e.listeners)n(t)}function Ag(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function xg(e){let t=Cr.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),nn(t,{type:"status",status:"cancelled"}),!0):!1}function Ng(e){return Cr.delete(e)}At();function vr(e){let{session:t,knownTags:n,minTags:s,maxTags:r}=e,o=n.slice(0,50).map(c=>c.tag).join(", "),a=t.current_tags.length>0?`already applied, do not repeat: [${t.current_tags.join(", ")}]`:"currently has no tags";return[`You are tagging a software engineering session. Produce ${s}-${r} concise, lowercase, hyphen-separated tags that describe:`," - the domain or subsystem touched (auth, db, frontend, ...)"," - the kind of work (bugfix, feature, refactor, research, ...)"," - any specific tool, library, or product if prominent","","Prefer tags from the known-tags list below when applicable \u2014 consistency over creativity. Never invent marketing-sounding labels. Never tag based on speculation; only on observed work.","",`known tags (most used first, up to 50): ${o||"(none yet)"}`,"","Session:",` project: ${t.project}`,t.alias?` alias: ${JSON.stringify(t.alias)}`:" alias: (none)",t.git_branch?` git_branch: ${t.git_branch}`:"",` ${a}`," first_user_message:",Og(t.first_user_message," ")," message_sample:",Og(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${s}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1765
+ `)}function Og(e,t){return e.split(`
1766
1766
  `).map(n=>t+n).join(`
1767
- `)}At();import{z as Yn}from"zod";var p0=Yn.object({tags:Yn.array(Yn.string()).min(1),confidence:Yn.number().min(0).max(1),rationale:Yn.string().min(1).max(500)});function m0(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function Cr(e,t={}){let n=t.maxTags??10,s;try{s=JSON.parse(m0(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=p0.safeParse(s);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>ut(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,n);return a.length===0?{ok:!1,reason:"no usable tags after normalization"}:{ok:!0,data:{tags:a,confidence:r.data.confidence,rationale:r.data.rationale}}}import g0 from"@anthropic-ai/sdk";async function vr(e){let s=(await new g0({apiKey:e.apiKey}).messages.create({model:e.model,max_tokens:512,temperature:.2,messages:[{role:"user",content:e.prompt}]},e.signal?{signal:e.signal}:void 0)).content.find(r=>r.type==="text");if(!s||s.type!=="text")throw new Error("Anthropic response contained no text block");return s.text}function f0(e){if(!(e instanceof Error))return!1;let t=e;return t.status===429||t.status===502||t.status===503||t.status===504}async function Ir(e,t={}){let n=t.maxAttempts??3,s=t.baseDelayMs??500,r;for(let o=0;o<n;o++)try{return await e()}catch(a){if(r=a,!f0(a)||o===n-1)throw a;await new Promise(c=>setTimeout(c,s*Math.pow(2,o)))}throw r}async function wg(e,t){e.status="running",nn(e,{type:"status",status:"running"});let n=kt();for(let s of t.sessions){if(e.controller.signal.aborted)break;let r=Lr({session:s,knownTags:n,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:s.id,project:s.project,alias:s.alias,first_user_message:s.first_user_message,current_tags:s.current_tags,suggestion:null,error:null,applied:!1};try{let a=await Ir(()=>vr({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=Cr(a,{maxTags:t.maxTags});c.ok?o.suggestion=c.data:o.error=c.reason}catch(a){o.error=a instanceof Error?a.message:String(a)}e.results.push(o),e.completed+=1,nn(e,{type:"result",result:o}),nn(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let s=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;nn(e,{type:"done",summary:{ok:s,failed:r}})}}function Rg(e,t){let n=0,s=0;for(let r of t){let o=e.results.find(a=>a.sessionId===r.sessionId);if(o){for(let a of r.tags)try{let{added:c}=Rt(r.sessionId,a);c&&(n+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),s+=1}o.applied=!0}}return{applied:n,failed:s}}At();fs();var Z={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},ha=new Set;function Kn(){return{status:Z.status,processed:Z.processed,total:Z.total,currentSessionId:Z.currentSessionId,lastError:Z.lastError,lastRunAt:Z.lastRunAt}}function kg(e){return ha.add(e),()=>{ha.delete(e)}}function zn(){let e=Kn();for(let t of ha)t(e)}async function Mr(){let e=qe();if(!(!e.enabled||!e.autopilot)&&!(e.backend!=="api"||!e.apiKey)&&!Z.running){Z.running=!0,Z.status="scanning",Z.processed=0,Z.total=0,Z.currentSessionId=null,Z.lastError=null,Z.controller=new AbortController,zn();try{let t=xt({untaggedOnly:!0,limit:200});if(Z.total=t.length,zn(),t.length===0){Z.status="idle",Z.lastRunAt=new Date().toISOString();return}let n=kt();for(let s of t){if(Z.controller.signal.aborted)break;let r=qe();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;Z.currentSessionId=s.id,zn();let o=Lr({session:s,knownTags:n,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await Ir(()=>vr({apiKey:r.apiKey,model:r.model,prompt:o,signal:Z.controller.signal})),c=Cr(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{Rt(s.id,u)}catch(d){console.error("[autopilot] addTag failed:",s.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",s.id,a)}Z.processed+=1,zn(),await new Promise(a=>setTimeout(a,1500))}Z.status="idle",Z.lastRunAt=new Date().toISOString()}catch(t){Z.status="error",Z.lastError=t instanceof Error?t.message:String(t),console.error("[autopilot] fatal:",t)}finally{Z.running=!1,Z.currentSessionId=null,Z.controller=null,zn()}}}Es();import{copyFileSync as _0,existsSync as Dr,readFileSync as xg,writeFileSync as Ng}from"node:fs";import{homedir as h0}from"node:os";import{dirname as E0,join as b0,resolve as S0}from"node:path";import{fileURLToPath as T0}from"node:url";var Pe=b0(h0(),".claude.json");function y0(){let e=E0(T0(import.meta.url));return S0(e,"..","mcp-server.js")}var w0=y0();function Ea(){if(!Dr(Pe))return{};try{let e=xg(Pe,"utf8"),t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch(e){return console.error("[mcp-installer] failed to parse ~/.claude.json:",e),{}}}var Ag=new Map;function R0(e){let t=Ag.get(e);if(t!==void 0)return t;let n=Nt(e)!==null;return Ag.set(e,n),n}function k0(){return R0("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[w0]}}function Je(){let t=Ea().mcpServers?.recall;return{configPath:Pe,configExists:Dr(Pe),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function ba(){let e=Ea(),t=e.mcpServers??{},n=k0(),s=t.recall;if(s?.command===n.command&&JSON.stringify(s?.args??[])===JSON.stringify(n.args))return Je();let o={command:n.command};n.args.length>0&&(o.args=n.args);let a={...e,mcpServers:{...t,recall:o}};return Ng(Pe,JSON.stringify(a,null,2)),Je()}function Og(){if(!Dr(Pe))return{repointed:!1,reason:"config_missing"};let e;try{e=xg(Pe,"utf8")}catch(f){return console.error("[mcp-installer] validate: failed to read ~/.claude.json:",f),{repointed:!1,reason:"read_error"}}let t;try{t=JSON.parse(e)}catch(f){return console.error("[mcp-installer] validate: ~/.claude.json is not valid JSON \u2014 skipping (operator must fix):",f),{repointed:!1,reason:"parse_error"}}if(!t||typeof t!="object"||Array.isArray(t))return{repointed:!1,reason:"not_an_object"};let s=t.mcpServers;if(!s||typeof s!="object")return{repointed:!1,reason:"no_mcp_servers"};let r=s.recall;if(!r)return{repointed:!1,reason:"no_recall_entry"};let o=r.args;if(!Array.isArray(o)||o.length===0||typeof o[0]!="string")return{repointed:!1,reason:"no_args_path"};let a=o[0];if(Dr(a))return{repointed:!1,reason:"path_live"};let c=new Date().toISOString().replace(/[:.]/g,"-"),u=`${Pe}.bak.${c}`;try{_0(Pe,u)}catch(f){return console.error(`[mcp-installer] validate: backup write failed (${u}) \u2014 refusing to repoint to avoid risking original:`,f),{repointed:!1,reason:"backup_failed"}}try{ba()}catch(f){return console.error(`[mcp-installer] validate: installMcp() failed during repoint \u2014 backup preserved at ${u}:`,f),{repointed:!1,reason:"install_failed"}}let d=Je(),m=d.args&&d.args.length>0?d.args[0]:d.command;return console.log(`[mcp-installer] repointed stale ~/.claude.json from ${a} to ${m??"(command-only)"} (backup: ${u})`),{repointed:!0,reason:"stale_args[0]"}}function Lg(){let e=Ea(),t=e.mcpServers??{};if(!t.recall)return Je();let{recall:n,...s}=t,r={...e,mcpServers:s};return Ng(Pe,JSON.stringify(r,null,2)),Je()}import{existsSync as Cg,mkdirSync as A0,readFileSync as x0,writeFileSync as vg}from"node:fs";import{homedir as N0}from"node:os";import{join as Ig}from"node:path";import{z as nt}from"zod";function Mg(){return process.env.RECALL_HOME??Ig(N0(),".recall")}function Dg(){let e=Mg();Cg(e)||A0(e,{recursive:!0})}function Ta(){return Ig(Mg(),"onboarding.json")}var jr=nt.object({version:nt.literal(1).default(1),completed:nt.boolean().default(!1),skipped:nt.boolean().default(!1),finishedAt:nt.string().nullable().default(null),completedSteps:nt.array(nt.string().max(100)).max(50).default([]),threadsIntroSeen:nt.boolean().default(!1)}),Sa={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function Pr(){let e=Ta();if(!Cg(e))return{...Sa};try{let t=JSON.parse(x0(e,"utf8")),n=jr.safeParse(t);return n.success?n.data:{...Sa}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...Sa}}}function jg(e){Dg();let t=Pr(),n=jr.parse({...t,...e,completedSteps:O0([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return vg(Ta(),JSON.stringify(n,null,2)),n}function Pg(){Dg();let e=Pr(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return vg(Ta(),JSON.stringify(t,null,2)),t}function O0(e){let t=new Set,n=[];for(let s of e)t.has(s)||(t.add(s),n.push(s));return n}we();Eo();ho();U();U();var L0=500,Fr=new Set,Fg=null,$r=!1;function $g(){return Fr.size}function C0(){return`
1767
+ `)}At();import{z as Yn}from"zod";var T0=Yn.object({tags:Yn.array(Yn.string()).min(1),confidence:Yn.number().min(0).max(1),rationale:Yn.string().min(1).max(500)});function y0(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function Ir(e,t={}){let n=t.maxTags??10,s;try{s=JSON.parse(y0(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=T0.safeParse(s);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>ut(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,n);return a.length===0?{ok:!1,reason:"no usable tags after normalization"}:{ok:!0,data:{tags:a,confidence:r.data.confidence,rationale:r.data.rationale}}}import w0 from"@anthropic-ai/sdk";async function Mr(e){let s=(await new w0({apiKey:e.apiKey}).messages.create({model:e.model,max_tokens:512,temperature:.2,messages:[{role:"user",content:e.prompt}]},e.signal?{signal:e.signal}:void 0)).content.find(r=>r.type==="text");if(!s||s.type!=="text")throw new Error("Anthropic response contained no text block");return s.text}function R0(e){if(!(e instanceof Error))return!1;let t=e;return t.status===429||t.status===502||t.status===503||t.status===504}async function Dr(e,t={}){let n=t.maxAttempts??3,s=t.baseDelayMs??500,r;for(let o=0;o<n;o++)try{return await e()}catch(a){if(r=a,!R0(a)||o===n-1)throw a;await new Promise(c=>setTimeout(c,s*Math.pow(2,o)))}throw r}async function Cg(e,t){e.status="running",nn(e,{type:"status",status:"running"});let n=kt();for(let s of t.sessions){if(e.controller.signal.aborted)break;let r=vr({session:s,knownTags:n,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:s.id,project:s.project,alias:s.alias,first_user_message:s.first_user_message,current_tags:s.current_tags,suggestion:null,error:null,applied:!1};try{let a=await Dr(()=>Mr({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=Ir(a,{maxTags:t.maxTags});c.ok?o.suggestion=c.data:o.error=c.reason}catch(a){o.error=a instanceof Error?a.message:String(a)}e.results.push(o),e.completed+=1,nn(e,{type:"result",result:o}),nn(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let s=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;nn(e,{type:"done",summary:{ok:s,failed:r}})}}function Lg(e,t){let n=0,s=0;for(let r of t){let o=e.results.find(a=>a.sessionId===r.sessionId);if(o){for(let a of r.tags)try{let{added:c}=Rt(r.sessionId,a);c&&(n+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),s+=1}o.applied=!0}}return{applied:n,failed:s}}At();fs();var Z={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},ya=new Set;function Kn(){return{status:Z.status,processed:Z.processed,total:Z.total,currentSessionId:Z.currentSessionId,lastError:Z.lastError,lastRunAt:Z.lastRunAt}}function vg(e){return ya.add(e),()=>{ya.delete(e)}}function zn(){let e=Kn();for(let t of ya)t(e)}async function jr(){let e=qe();if(!(!e.enabled||!e.autopilot)&&!(e.backend!=="api"||!e.apiKey)&&!Z.running){Z.running=!0,Z.status="scanning",Z.processed=0,Z.total=0,Z.currentSessionId=null,Z.lastError=null,Z.controller=new AbortController,zn();try{let t=xt({untaggedOnly:!0,limit:200});if(Z.total=t.length,zn(),t.length===0){Z.status="idle",Z.lastRunAt=new Date().toISOString();return}let n=kt();for(let s of t){if(Z.controller.signal.aborted)break;let r=qe();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;Z.currentSessionId=s.id,zn();let o=vr({session:s,knownTags:n,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await Dr(()=>Mr({apiKey:r.apiKey,model:r.model,prompt:o,signal:Z.controller.signal})),c=Ir(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{Rt(s.id,u)}catch(d){console.error("[autopilot] addTag failed:",s.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",s.id,a)}Z.processed+=1,zn(),await new Promise(a=>setTimeout(a,1500))}Z.status="idle",Z.lastRunAt=new Date().toISOString()}catch(t){Z.status="error",Z.lastError=t instanceof Error?t.message:String(t),console.error("[autopilot] fatal:",t)}finally{Z.running=!1,Z.currentSessionId=null,Z.controller=null,zn()}}}Es();import{copyFileSync as k0,existsSync as Pr,readFileSync as Mg,writeFileSync as Dg}from"node:fs";import{homedir as A0}from"node:os";import{dirname as x0,join as N0,resolve as O0}from"node:path";import{fileURLToPath as C0}from"node:url";var Pe=N0(A0(),".claude.json");function L0(){let e=x0(C0(import.meta.url));return O0(e,"..","mcp-server.js")}var v0=L0();function wa(){if(!Pr(Pe))return{};try{let e=Mg(Pe,"utf8"),t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch(e){return console.error("[mcp-installer] failed to parse ~/.claude.json:",e),{}}}var Ig=new Map;function I0(e){let t=Ig.get(e);if(t!==void 0)return t;let n=Nt(e)!==null;return Ig.set(e,n),n}function M0(){return I0("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[v0]}}function Je(){let t=wa().mcpServers?.recall;return{configPath:Pe,configExists:Pr(Pe),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function Ra(){let e=wa(),t=e.mcpServers??{},n=M0(),s=t.recall;if(s?.command===n.command&&JSON.stringify(s?.args??[])===JSON.stringify(n.args))return Je();let o={command:n.command};n.args.length>0&&(o.args=n.args);let a={...e,mcpServers:{...t,recall:o}};return Dg(Pe,JSON.stringify(a,null,2)),Je()}function jg(){if(!Pr(Pe))return{repointed:!1,reason:"config_missing"};let e;try{e=Mg(Pe,"utf8")}catch(f){return console.error("[mcp-installer] validate: failed to read ~/.claude.json:",f),{repointed:!1,reason:"read_error"}}let t;try{t=JSON.parse(e)}catch(f){return console.error("[mcp-installer] validate: ~/.claude.json is not valid JSON \u2014 skipping (operator must fix):",f),{repointed:!1,reason:"parse_error"}}if(!t||typeof t!="object"||Array.isArray(t))return{repointed:!1,reason:"not_an_object"};let s=t.mcpServers;if(!s||typeof s!="object")return{repointed:!1,reason:"no_mcp_servers"};let r=s.recall;if(!r)return{repointed:!1,reason:"no_recall_entry"};let o=r.args;if(!Array.isArray(o)||o.length===0||typeof o[0]!="string")return{repointed:!1,reason:"no_args_path"};let a=o[0];if(Pr(a))return{repointed:!1,reason:"path_live"};let c=new Date().toISOString().replace(/[:.]/g,"-"),u=`${Pe}.bak.${c}`;try{k0(Pe,u)}catch(f){return console.error(`[mcp-installer] validate: backup write failed (${u}) \u2014 refusing to repoint to avoid risking original:`,f),{repointed:!1,reason:"backup_failed"}}try{Ra()}catch(f){return console.error(`[mcp-installer] validate: installMcp() failed during repoint \u2014 backup preserved at ${u}:`,f),{repointed:!1,reason:"install_failed"}}let d=Je(),m=d.args&&d.args.length>0?d.args[0]:d.command;return console.log(`[mcp-installer] repointed stale ~/.claude.json from ${a} to ${m??"(command-only)"} (backup: ${u})`),{repointed:!0,reason:"stale_args[0]"}}function Pg(){let e=wa(),t=e.mcpServers??{};if(!t.recall)return Je();let{recall:n,...s}=t,r={...e,mcpServers:s};return Dg(Pe,JSON.stringify(r,null,2)),Je()}import{existsSync as Fg,mkdirSync as D0,readFileSync as j0,writeFileSync as $g}from"node:fs";import{homedir as P0}from"node:os";import{join as Ug}from"node:path";import{z as nt}from"zod";function Bg(){return process.env.RECALL_HOME??Ug(P0(),".recall")}function Hg(){let e=Bg();Fg(e)||D0(e,{recursive:!0})}function Aa(){return Ug(Bg(),"onboarding.json")}var Fr=nt.object({version:nt.literal(1).default(1),completed:nt.boolean().default(!1),skipped:nt.boolean().default(!1),finishedAt:nt.string().nullable().default(null),completedSteps:nt.array(nt.string().max(100)).max(50).default([]),threadsIntroSeen:nt.boolean().default(!1)}),ka={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function $r(){let e=Aa();if(!Fg(e))return{...ka};try{let t=JSON.parse(j0(e,"utf8")),n=Fr.safeParse(t);return n.success?n.data:{...ka}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...ka}}}function Wg(e){Hg();let t=$r(),n=Fr.parse({...t,...e,completedSteps:F0([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return $g(Aa(),JSON.stringify(n,null,2)),n}function qg(){Hg();let e=$r(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return $g(Aa(),JSON.stringify(t,null,2)),t}function F0(e){let t=new Set,n=[];for(let s of e)t.has(s)||(t.add(s),n.push(s));return n}we();So();bo();U();U();var $0=500,Ur=new Set,Xg=null,Br=!1;function Jg(){return Ur.size}function U0(){return`
1768
1768
  SELECT m.uuid, m.session_id, m.timestamp, m.raw_json
1769
1769
  FROM messages m
1770
1770
  LEFT JOIN message_usage mu ON mu.message_uuid = m.uuid
1771
1771
  WHERE m.role = 'assistant' AND mu.message_uuid IS NULL
1772
1772
  AND m.uuid NOT IN (SELECT value FROM json_each(?))
1773
1773
  LIMIT ?
1774
- `}function Ug(e,t){let n=t.limit??Number.MAX_SAFE_INTEGER,s=Math.max(1,t.chunkSize??L0),r=e.prepare(C0()),o=e.prepare(`
1774
+ `}function Gg(e,t){let n=t.limit??Number.MAX_SAFE_INTEGER,s=Math.max(1,t.chunkSize??$0),r=e.prepare(U0()),o=e.prepare(`
1775
1775
  INSERT INTO message_usage (
1776
1776
  message_uuid, session_id, model,
1777
1777
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1781,7 +1781,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1781
1781
  @input, @output, @cc, @cr, @ts
1782
1782
  )
1783
1783
  ON CONFLICT(message_uuid) DO NOTHING
1784
- `),a=0,c=0,u=new Set;for(;a<n;){let d=Math.min(s,n-a),m=JSON.stringify([...Fr]),f=r.all(m,d);if(f.length===0)break;let b=new Set;if(e.transaction(()=>{for(let S of f){let w;try{w=JSON.parse(S.raw_json)}catch{Fr.add(S.uuid);continue}let A=Bo(w.message);if(!A){Fr.add(S.uuid);continue}o.run({uuid:S.uuid,session_id:S.session_id,model:w.message?.model??null,input:A.inputTokens,output:A.outputTokens,cc:A.cacheCreateTokens,cr:A.cacheReadTokens,ts:S.timestamp}),c+=1,b.add(S.session_id)}for(let S of b)wn(e,S),u.add(S)})(),a+=f.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:f.length<d}),f.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function Hg(e={}){return Ug(h(),e)}function Bg(e={}){return $r?!1:($r=!0,queueMicrotask(()=>{try{let t=Ug(h(),e);Fg={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{$r=!1}}),!0)}function Wg(){return $r}function ya(){return Fg}var v0=[[/opus[-_ ]?4[-_. ]?7/i,{label:"Opus 4.7",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/opus[-_ ]?4[-_. ]?6/i,{label:"Opus 4.6",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4[-_. ]?6/i,{label:"Sonnet 4.6",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/sonnet[-_ ]?4[-_. ]?5/i,{label:"Sonnet 4.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku[-_ ]?4[-_. ]?5/i,{label:"Haiku 4.5",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}],[/opus[-_ ]?4(?!.*[5-9])/i,{label:"Opus 4",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4(?!.*[5-9])/i,{label:"Sonnet 4",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?7[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?7/i,{label:"Sonnet 3.7",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?5/i,{label:"Sonnet 3.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?haiku|haiku[-_ ]?3[-_. ]?5/i,{label:"Haiku 3.5",inputCentsPerMtok:80,outputCentsPerMtok:400,cacheCreateCentsPerMtok:100,cacheReadCentsPerMtok:8}],[/3[-_ ]?opus|opus[-_ ]?3/i,{label:"Opus 3",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/3[-_ ]?haiku|haiku(?!.*3[-_. ]?5)/i,{label:"Haiku 3",inputCentsPerMtok:25,outputCentsPerMtok:125,cacheCreateCentsPerMtok:30,cacheReadCentsPerMtok:3}],[/opus/i,{label:"Opus",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet/i,{label:"Sonnet",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku/i,{label:"Haiku",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}]],qg={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function st(e){if(!e)return qg;for(let[t,n]of v0)if(t.test(e))return n;return qg}function xe(e,t){if(e.byModel&&Object.keys(e.byModel).length>0){let a={input:0,output:0,cacheCreate:0,cacheRead:0},c=0;for(let[d,m]of Object.entries(e.byModel)){let f=st(d);a.input+=m.inputTokens/1e6*f.inputCentsPerMtok,a.output+=m.outputTokens/1e6*f.outputCentsPerMtok,a.cacheCreate+=m.cacheCreateTokens/1e6*f.cacheCreateCentsPerMtok,a.cacheRead+=m.cacheReadTokens/1e6*f.cacheReadCentsPerMtok,c+=m.inputTokens+m.outputTokens+m.cacheCreateTokens+m.cacheReadTokens}let u=a.input+a.output+a.cacheCreate+a.cacheRead;return{cents:u,dollars:u/100,totalTokens:c,parts:a}}let n=st(t),s={input:e.inputTokens/1e6*n.inputCentsPerMtok,output:e.outputTokens/1e6*n.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*n.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*n.cacheReadCentsPerMtok},r=s.input+s.output+s.cacheCreate+s.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:s}}function sn(e){let t=e/100;return t===0?"$0.00":t<.01?"<$0.01":t<1?`$${t.toFixed(2)}`:t<100?`$${t.toFixed(2)}`:t<1e4?`$${t.toFixed(0)}`:`$${(t/1e3).toFixed(1)}k`}function rn(e){return!Number.isFinite(e)||e<0?"0":e<1e3?String(Math.round(e)):e<1e6?`${(e/1e3).toFixed(1)}k`:e<1e9?`${(e/1e6).toFixed(2)}M`:e<1e12?`${(e/1e9).toFixed(2)}B`:`${(e/1e12).toFixed(2)}T`}function wa(e){let t=new Map;for(let s of e){let r=s.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=s.input_tokens,o.outputTokens+=s.output_tokens,o.cacheCreateTokens+=s.cache_create_tokens,o.cacheReadTokens+=s.cache_read_tokens,o.messageCount+=s.n,t.set(r,o)}let n=[];for(let[s,r]of t.entries()){let o=xe({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},s);n.push({model:s,modelLabel:st(s).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return n.sort((s,r)=>r.cost.cents-s.cost.cents)}function Ra(e){let t={};for(let n of e)t[n.model??"__unknown__"]={inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheCreateTokens:n.cacheCreateTokens,cacheReadTokens:n.cacheReadTokens};return{byModel:t}}function Xg(e){let t=h(),n=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1784
+ `),a=0,c=0,u=new Set;for(;a<n;){let d=Math.min(s,n-a),m=JSON.stringify([...Ur]),f=r.all(m,d);if(f.length===0)break;let b=new Set;if(e.transaction(()=>{for(let S of f){let R;try{R=JSON.parse(S.raw_json)}catch{Ur.add(S.uuid);continue}let A=qo(R.message);if(!A){Ur.add(S.uuid);continue}o.run({uuid:S.uuid,session_id:S.session_id,model:R.message?.model??null,input:A.inputTokens,output:A.outputTokens,cc:A.cacheCreateTokens,cr:A.cacheReadTokens,ts:S.timestamp}),c+=1,b.add(S.session_id)}for(let S of b)wn(e,S),u.add(S)})(),a+=f.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:f.length<d}),f.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function Yg(e={}){return Gg(h(),e)}function zg(e={}){return Br?!1:(Br=!0,queueMicrotask(()=>{try{let t=Gg(h(),e);Xg={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{Br=!1}}),!0)}function Kg(){return Br}function xa(){return Xg}var B0=[[/opus[-_ ]?4[-_. ]?7/i,{label:"Opus 4.7",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/opus[-_ ]?4[-_. ]?6/i,{label:"Opus 4.6",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4[-_. ]?6/i,{label:"Sonnet 4.6",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/sonnet[-_ ]?4[-_. ]?5/i,{label:"Sonnet 4.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku[-_ ]?4[-_. ]?5/i,{label:"Haiku 4.5",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}],[/opus[-_ ]?4(?!.*[5-9])/i,{label:"Opus 4",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4(?!.*[5-9])/i,{label:"Sonnet 4",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?7[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?7/i,{label:"Sonnet 3.7",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?5/i,{label:"Sonnet 3.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?haiku|haiku[-_ ]?3[-_. ]?5/i,{label:"Haiku 3.5",inputCentsPerMtok:80,outputCentsPerMtok:400,cacheCreateCentsPerMtok:100,cacheReadCentsPerMtok:8}],[/3[-_ ]?opus|opus[-_ ]?3/i,{label:"Opus 3",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/3[-_ ]?haiku|haiku(?!.*3[-_. ]?5)/i,{label:"Haiku 3",inputCentsPerMtok:25,outputCentsPerMtok:125,cacheCreateCentsPerMtok:30,cacheReadCentsPerMtok:3}],[/opus/i,{label:"Opus",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet/i,{label:"Sonnet",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku/i,{label:"Haiku",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}]],Vg={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function st(e){if(!e)return Vg;for(let[t,n]of B0)if(t.test(e))return n;return Vg}function xe(e,t){if(e.byModel&&Object.keys(e.byModel).length>0){let a={input:0,output:0,cacheCreate:0,cacheRead:0},c=0;for(let[d,m]of Object.entries(e.byModel)){let f=st(d);a.input+=m.inputTokens/1e6*f.inputCentsPerMtok,a.output+=m.outputTokens/1e6*f.outputCentsPerMtok,a.cacheCreate+=m.cacheCreateTokens/1e6*f.cacheCreateCentsPerMtok,a.cacheRead+=m.cacheReadTokens/1e6*f.cacheReadCentsPerMtok,c+=m.inputTokens+m.outputTokens+m.cacheCreateTokens+m.cacheReadTokens}let u=a.input+a.output+a.cacheCreate+a.cacheRead;return{cents:u,dollars:u/100,totalTokens:c,parts:a}}let n=st(t),s={input:e.inputTokens/1e6*n.inputCentsPerMtok,output:e.outputTokens/1e6*n.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*n.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*n.cacheReadCentsPerMtok},r=s.input+s.output+s.cacheCreate+s.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:s}}function sn(e){let t=e/100;return t===0?"$0.00":t<.01?"<$0.01":t<1?`$${t.toFixed(2)}`:t<100?`$${t.toFixed(2)}`:t<1e4?`$${t.toFixed(0)}`:`$${(t/1e3).toFixed(1)}k`}function rn(e){return!Number.isFinite(e)||e<0?"0":e<1e3?String(Math.round(e)):e<1e6?`${(e/1e3).toFixed(1)}k`:e<1e9?`${(e/1e6).toFixed(2)}M`:e<1e12?`${(e/1e9).toFixed(2)}B`:`${(e/1e12).toFixed(2)}T`}function Na(e){let t=new Map;for(let s of e){let r=s.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=s.input_tokens,o.outputTokens+=s.output_tokens,o.cacheCreateTokens+=s.cache_create_tokens,o.cacheReadTokens+=s.cache_read_tokens,o.messageCount+=s.n,t.set(r,o)}let n=[];for(let[s,r]of t.entries()){let o=xe({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},s);n.push({model:s,modelLabel:st(s).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return n.sort((s,r)=>r.cost.cents-s.cost.cents)}function Oa(e){let t={};for(let n of e)t[n.model??"__unknown__"]={inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheCreateTokens:n.cacheCreateTokens,cacheReadTokens:n.cacheReadTokens};return{byModel:t}}function Qg(e){let t=h(),n=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1785
1785
  s.message_count,
1786
1786
  s.total_input_tokens, s.total_output_tokens,
1787
1787
  s.total_cache_create_tokens, s.total_cache_read_tokens,
@@ -1796,7 +1796,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1796
1796
  COUNT(*) AS n
1797
1797
  FROM message_usage
1798
1798
  WHERE session_id = ?
1799
- GROUP BY model`).all(e),r=wa(s),o=n.total_input_tokens??0,a=n.total_output_tokens??0,c=n.total_cache_create_tokens??0,u=n.total_cache_read_tokens??0,d=xe({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...Ra(r)},n.primary_model);return{sessionId:n.id,project:n.project,startedAt:n.started_at,endedAt:n.ended_at,messageCount:n.message_count,primaryModel:n.primary_model,primaryModelLabel:st(n.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:sn(d.cents),tokens:rn(d.totalTokens),model:st(n.primary_model).label}}}function Jg(e){let t=h(),n=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT mu.model,
1799
+ GROUP BY model`).all(e),r=Na(s),o=n.total_input_tokens??0,a=n.total_output_tokens??0,c=n.total_cache_create_tokens??0,u=n.total_cache_read_tokens??0,d=xe({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...Oa(r)},n.primary_model);return{sessionId:n.id,project:n.project,startedAt:n.started_at,endedAt:n.ended_at,messageCount:n.message_count,primaryModel:n.primary_model,primaryModelLabel:st(n.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:sn(d.cents),tokens:rn(d.totalTokens),model:st(n.primary_model).label}}}function Zg(e){let t=h(),n=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT mu.model,
1800
1800
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1801
1801
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1802
1802
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1805,12 +1805,12 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1805
1805
  FROM message_usage mu
1806
1806
  JOIN sessions s ON s.id = mu.session_id
1807
1807
  WHERE s.project_id = ?
1808
- GROUP BY mu.model`).all(n.id),r=wa(s),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1808
+ GROUP BY mu.model`).all(n.id),r=Na(s),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1809
1809
  COALESCE(SUM(total_output_tokens), 0) AS output_tokens,
1810
1810
  COALESCE(SUM(total_cache_create_tokens), 0) AS cache_create_tokens,
1811
1811
  COALESCE(SUM(total_cache_read_tokens), 0) AS cache_read_tokens,
1812
1812
  COUNT(*) AS session_count
1813
- FROM sessions WHERE project_id = ?`).get(n.id),a=xe({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...Ra(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1813
+ FROM sessions WHERE project_id = ?`).get(n.id),a=xe({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...Oa(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1814
1814
  s.total_input_tokens, s.total_output_tokens,
1815
1815
  s.total_cache_create_tokens, s.total_cache_read_tokens,
1816
1816
  s.primary_model
@@ -1821,7 +1821,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1821
1821
  + COALESCE(s.total_output_tokens,0)
1822
1822
  + COALESCE(s.total_cache_create_tokens,0)
1823
1823
  + COALESCE(s.total_cache_read_tokens,0)) DESC
1824
- LIMIT 10`).all(n.id).map(d=>{let m=xe({inputTokens:d.total_input_tokens??0,outputTokens:d.total_output_tokens??0,cacheCreateTokens:d.total_cache_create_tokens??0,cacheReadTokens:d.total_cache_read_tokens??0},d.primary_model);return{sessionId:d.id,alias:d.alias,startedAt:d.started_at,totalTokens:m.totalTokens,cost:m}});return{project:n.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:a.totalTokens,cost:a,byModel:r,topSessions:u,display:{dollars:sn(a.cents),tokens:rn(a.totalTokens)}}}function Gg(e="all"){let t=h(),n=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,s=n?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=n?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=n?{since:n}:{},a=l=>n?t.prepare(l).get(o):t.prepare(l).get(),c=l=>n?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1824
+ LIMIT 10`).all(n.id).map(d=>{let m=xe({inputTokens:d.total_input_tokens??0,outputTokens:d.total_output_tokens??0,cacheCreateTokens:d.total_cache_create_tokens??0,cacheReadTokens:d.total_cache_read_tokens??0},d.primary_model);return{sessionId:d.id,alias:d.alias,startedAt:d.started_at,totalTokens:m.totalTokens,cost:m}});return{project:n.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:a.totalTokens,cost:a,byModel:r,topSessions:u,display:{dollars:sn(a.cents),tokens:rn(a.totalTokens)}}}function ef(e="all"){let t=h(),n=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,s=n?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=n?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=n?{since:n}:{},a=l=>n?t.prepare(l).get(o):t.prepare(l).get(),c=l=>n?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1825
1825
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1826
1826
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1827
1827
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1830,7 +1830,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1830
1830
  FROM message_usage mu
1831
1831
  JOIN sessions s ON s.id = mu.session_id
1832
1832
  ${s}
1833
- GROUP BY mu.model`),d=wa(u),m=0,f=0,b=0,T=0;for(let l of d)m+=l.inputTokens,f+=l.outputTokens,b+=l.cacheCreateTokens,T+=l.cacheReadTokens;let S=xe({inputTokens:m,outputTokens:f,cacheCreateTokens:b,cacheReadTokens:T,...Ra(d)},null),w=n?a(`SELECT
1833
+ GROUP BY mu.model`),d=Na(u),m=0,f=0,b=0,T=0;for(let l of d)m+=l.inputTokens,f+=l.outputTokens,b+=l.cacheCreateTokens,T+=l.cacheReadTokens;let S=xe({inputTokens:m,outputTokens:f,cacheCreateTokens:b,cacheReadTokens:T,...Oa(d)},null),R=n?a(`SELECT
1834
1834
  (SELECT COUNT(DISTINCT m.session_id)
1835
1835
  FROM messages m
1836
1836
  JOIN sessions s2 ON s2.id = m.session_id
@@ -1882,9 +1882,9 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1882
1882
  JOIN sessions s ON s.id = mu.session_id
1883
1883
  LEFT JOIN projects p ON p.id = s.project_id
1884
1884
  ${s}
1885
- GROUP BY p.id, mu.model`),te=new Map;for(let l of P){let p=l.project_id??"__none__",g=te.get(p);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},te.set(p,g)),l.sessions>g.sessionsApprox&&(g.sessionsApprox=l.sessions),g.byModel[l.model??"__unknown__"]={inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens}}let $=[...te.values()].map(l=>{let p=0,g=0,_=0,E=0;for(let R of Object.values(l.byModel))p+=R.inputTokens,g+=R.outputTokens,_+=R.cacheCreateTokens,E+=R.cacheReadTokens;let y=xe({inputTokens:p,outputTokens:g,cacheCreateTokens:_,cacheReadTokens:E,byModel:l.byModel},null);return{project:l.project,sessions:l.sessionsApprox,totalTokens:y.totalTokens,cost:y}});$.sort((l,p)=>p.totalTokens-l.totalTokens);let ne=$.slice(0,20),i=t.prepare(`SELECT
1885
+ GROUP BY p.id, mu.model`),te=new Map;for(let l of P){let p=l.project_id??"__none__",g=te.get(p);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},te.set(p,g)),l.sessions>g.sessionsApprox&&(g.sessionsApprox=l.sessions),g.byModel[l.model??"__unknown__"]={inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens}}let $=[...te.values()].map(l=>{let p=0,g=0,_=0,E=0;for(let k of Object.values(l.byModel))p+=k.inputTokens,g+=k.outputTokens,_+=k.cacheCreateTokens,E+=k.cacheReadTokens;let y=xe({inputTokens:p,outputTokens:g,cacheCreateTokens:_,cacheReadTokens:E,byModel:l.byModel},null);return{project:l.project,sessions:l.sessionsApprox,totalTokens:y.totalTokens,cost:y}});$.sort((l,p)=>p.totalTokens-l.totalTokens);let ne=$.slice(0,20),i=t.prepare(`SELECT
1886
1886
  (SELECT COUNT(*) FROM messages WHERE role='assistant') AS assistant_messages,
1887
- (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:w.total_sessions,sessionsWithUsage:w.sessions_with_usage,inputTokens:m,outputTokens:f,cacheCreateTokens:b,cacheReadTokens:T,totalTokens:S.totalTokens,cost:S,daily:D,byModel:d,topSessions:I,topRepos:ne,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min($g(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:sn(S.cents),tokens:rn(S.totalTokens)}}}U();function Vn(e){return Math.max(0,Math.min(1,e))}function ka(e){let t=h(),n=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT COUNT(*) AS cnt,
1887
+ (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:R.total_sessions,sessionsWithUsage:R.sessions_with_usage,inputTokens:m,outputTokens:f,cacheCreateTokens:b,cacheReadTokens:T,totalTokens:S.totalTokens,cost:S,daily:D,byModel:d,topSessions:I,topRepos:ne,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min(Jg(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:sn(S.cents),tokens:rn(S.totalTokens)}}}U();function Vn(e){return Math.max(0,Math.min(1,e))}function Ca(e){let t=h(),n=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT COUNT(*) AS cnt,
1888
1888
  MAX(started_at) AS latest
1889
1889
  FROM sessions WHERE project_id = ?`).get(e),r=s.cnt;if(r===0)return{projectId:e,projectName:n.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=Vn(r/10),a=s.latest?(Date.now()-new Date(s.latest).getTime())/(1e3*60*60*24):90,c=Vn(1-a/90),d=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1890
1890
  FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,m=Vn((d-2)/3),f=t.prepare(`SELECT COUNT(*) AS total,
@@ -1895,16 +1895,16 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1895
1895
  COUNT(DISTINCT st.session_id) AS tagged
1896
1896
  FROM sessions s
1897
1897
  LEFT JOIN session_tags st ON st.session_id = s.id
1898
- WHERE s.project_id = ?`).get(e),w=S.total>0?S.tagged/S.total:0,A=Vn(w),v=Math.round((o*.2+c*.25+m*.15+T*.2+A*.2)*100);return{projectId:e,projectName:n.name,score:v,breakdown:{sessionCount:{raw:r,score:o,weight:.2},recency:{daysSinceLastSession:Math.round(a),score:c,weight:.25},fragmentation:{avgMessages:Math.round(d*10)/10,score:m,weight:.15},searchCoverage:{ratio:Math.round(b*100)/100,score:T,weight:.2},tagCoverage:{ratio:Math.round(w*100)/100,score:A,weight:.2}}}}function Yg(){let t=h().prepare("SELECT id FROM projects ORDER BY name").all(),n=[];for(let s of t){let r=ka(s.id);r&&n.push(r)}return n}U();import{execFile as I0}from"node:child_process";import{promisify as M0}from"node:util";import{stat as D0}from"node:fs/promises";var j0=M0(I0),P0=60,F0=7,$0=7,U0=5e3;function H0(){let e=h(),t=n=>{try{return!!e.prepare(n).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1898
+ WHERE s.project_id = ?`).get(e),R=S.total>0?S.tagged/S.total:0,A=Vn(R),v=Math.round((o*.2+c*.25+m*.15+T*.2+A*.2)*100);return{projectId:e,projectName:n.name,score:v,breakdown:{sessionCount:{raw:r,score:o,weight:.2},recency:{daysSinceLastSession:Math.round(a),score:c,weight:.25},fragmentation:{avgMessages:Math.round(d*10)/10,score:m,weight:.15},searchCoverage:{ratio:Math.round(b*100)/100,score:T,weight:.2},tagCoverage:{ratio:Math.round(R*100)/100,score:A,weight:.2}}}}function tf(){let t=h().prepare("SELECT id FROM projects ORDER BY name").all(),n=[];for(let s of t){let r=Ca(s.id);r&&n.push(r)}return n}U();import{execFile as H0}from"node:child_process";import{promisify as W0}from"node:util";import{stat as q0}from"node:fs/promises";var X0=W0(H0),J0=60,G0=7,Y0=7,z0=5e3;function K0(){let e=h(),t=n=>{try{return!!e.prepare(n).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1899
1899
  WHERE (COALESCE(total_input_tokens,0)
1900
1900
  + COALESCE(total_output_tokens,0)
1901
1901
  + COALESCE(total_cache_create_tokens,0)
1902
1902
  + COALESCE(total_cache_read_tokens,0)) > 0
1903
- LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function Aa(e){if(!e)return[];let t=new Set,n=[];for(let s of e.split(",")){let r=s.trim().toLowerCase();!r||t.has(r)||(t.add(r),n.push(r))}return n}function zg(e){return{sessionId:e.session_id,project:e.project,alias:e.alias,startedAt:e.started_at,endedAt:e.ended_at,firstUserMessage:e.first_user_message}}function B0(){let e=h(),t=e.prepare(`SELECT ss.keywords
1903
+ LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function La(e){if(!e)return[];let t=new Set,n=[];for(let s of e.split(",")){let r=s.trim().toLowerCase();!r||t.has(r)||(t.add(r),n.push(r))}return n}function nf(e){return{sessionId:e.session_id,project:e.project,alias:e.alias,startedAt:e.started_at,endedAt:e.ended_at,firstUserMessage:e.first_user_message}}function V0(){let e=h(),t=e.prepare(`SELECT ss.keywords
1904
1904
  FROM session_semantic ss
1905
1905
  JOIN sessions s ON s.id = ss.session_id
1906
1906
  WHERE s.started_at IS NOT NULL
1907
- AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:F0});if(t.length===0)return null;let n=new Set;for(let o of t)for(let a of Aa(o.keywords))n.add(a);if(n.size===0)return null;let s=e.prepare(`SELECT ss.session_id AS session_id,
1907
+ AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:G0});if(t.length===0)return null;let n=new Set;for(let o of t)for(let a of La(o.keywords))n.add(a);if(n.size===0)return null;let s=e.prepare(`SELECT ss.session_id AS session_id,
1908
1908
  ss.summary AS summary,
1909
1909
  ss.keywords AS keywords,
1910
1910
  p.name AS project,
@@ -1920,7 +1920,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1920
1920
  WHERE s.started_at IS NOT NULL
1921
1921
  AND s.message_count > 2
1922
1922
  AND julianday('now') - julianday(s.started_at) >= @ageDays
1923
- ORDER BY s.started_at ASC`).all({ageDays:P0});if(s.length===0)return null;let r=null;for(let o of s){let c=Aa(o.keywords).filter(u=>n.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{...zg(r.row),summary:r.row.summary,keywords:Aa(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function W0(){let t=h().prepare(`SELECT s.id AS session_id,
1923
+ ORDER BY s.started_at ASC`).all({ageDays:J0});if(s.length===0)return null;let r=null;for(let o of s){let c=La(o.keywords).filter(u=>n.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{...nf(r.row),summary:r.row.summary,keywords:La(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function Q0(){let t=h().prepare(`SELECT s.id AS session_id,
1924
1924
  p.name AS project,
1925
1925
  NULLIF(sa.alias, '') AS alias,
1926
1926
  s.started_at AS started_at,
@@ -1939,31 +1939,31 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1939
1939
  AND (COALESCE(s.total_input_tokens, 0)
1940
1940
  + COALESCE(s.total_output_tokens, 0)
1941
1941
  + COALESCE(s.total_cache_create_tokens, 0)
1942
- + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:$0});if(t.length===0)return null;let n=null;for(let s of t){let r=xe({inputTokens:s.input_tokens,outputTokens:s.output_tokens,cacheCreateTokens:s.cache_create_tokens,cacheReadTokens:s.cache_read_tokens},s.primary_model);r.cents<=0||(!n||r.cents>n.cents)&&(n={row:s,cents:r.cents,totalTokens:r.totalTokens})}return n?{...zg(n.row),totalTokens:n.totalTokens,costCents:n.cents,costDisplay:sn(n.cents),tokensDisplay:rn(n.totalTokens),primaryModel:n.row.primary_model,primaryModelLabel:st(n.row.primary_model).label}:null}async function q0(e){try{if(!(await D0(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await j0("git",["rev-parse","HEAD"],{cwd:e,timeout:U0}),n=t.trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}async function X0(){let e=h(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1942
+ + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:Y0});if(t.length===0)return null;let n=null;for(let s of t){let r=xe({inputTokens:s.input_tokens,outputTokens:s.output_tokens,cacheCreateTokens:s.cache_create_tokens,cacheReadTokens:s.cache_read_tokens},s.primary_model);r.cents<=0||(!n||r.cents>n.cents)&&(n={row:s,cents:r.cents,totalTokens:r.totalTokens})}return n?{...nf(n.row),totalTokens:n.totalTokens,costCents:n.cents,costDisplay:sn(n.cents),tokensDisplay:rn(n.totalTokens),primaryModel:n.row.primary_model,primaryModelLabel:st(n.row.primary_model).label}:null}async function Z0(e){try{if(!(await q0(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await X0("git",["rev-parse","HEAD"],{cwd:e,timeout:z0}),n=t.trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}async function eA(){let e=h(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1943
1943
  FROM sessions s
1944
1944
  WHERE s.cwd IS NOT NULL AND s.started_at IS NOT NULL
1945
1945
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1946
- LIMIT 1`).get();if(!t?.cwd)return null;let n=await q0(t.cwd);if(!n)return null;let s=Fs(n);if(s.length===0)return null;let r=s[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1946
+ LIMIT 1`).get();if(!t?.cwd)return null;let n=await Z0(t.cwd);if(!n)return null;let s=$s(n);if(s.length===0)return null;let r=s[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1947
1947
  FROM sessions s
1948
- WHERE s.id = ?`).get(r.sessionId);return{sessionId:r.sessionId,project:r.project,alias:r.alias,startedAt:r.startedAt,endedAt:o?.ended_at??r.endedAt,firstUserMessage:o?.first_user_message??null,commitSha:r.commitSha,shortSha:r.commitSha.slice(0,7),subject:r.subject,committedAt:r.committedAt,cwd:t.cwd}}async function Kg(){let e=H0(),t=e.semantic?Promise.resolve().then(()=>{try{return B0()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),n=e.cost?Promise.resolve().then(()=>{try{return W0()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),s=e.git?X0().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,n,s]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}We();U();ts();var xa=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,n){super(`semantic search refused: vec_chunks has ${t} rows (cap ${n}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=n}};function Na(e,t={}){let n=t.limit??J0()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new xa(r,n)}function J0(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}ss();import{Worker as G0}from"node:worker_threads";import{join as Y0}from"node:path";var Oa=class extends Error{constructor(n){super(`semantic kNN exceeded ${n}ms wall-clock budget -- query aborted`);this.timeoutMs=n}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},z0=1e4;function K0(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function La(e){let t=e.timeoutMs??K0()??z0,n=(e.workerFactory??V0)();return new Promise((s,r)=>{let o=setTimeout(()=>{n.terminate().catch(()=>{}),r(new Oa(t))},t);n.once("message",a=>{clearTimeout(o);let c=a;c&&c.ok===!0&&Array.isArray(c.hits)?s(c.hits):r(new Error(c?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",a=>{clearTimeout(o),n.terminate().catch(()=>{}),r(a)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function V0(){let e=Y0(yt(),"dist","daemon","query-worker.js"),t={...process.env,RECALL_DB_PROFILE:"worker"};return new G0(e,{env:t})}async function Vg(e,t=50){let n=h();return at(n),Na(n),La({query:e,limit:t})}async function Qg(e,t=10,n=.65){let s=h();at(s),Na(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=await La({precomputedVector:o.embedding,limit:t*5}),c=new Map;for(let d of a){if(d.sessionId===e)continue;let m=c.get(d.sessionId);(m===void 0||d.distance<m)&&c.set(d.sessionId,d.distance)}let u=[];for(let[d,m]of c){let f=1-m;f>=n&&u.push({sessionId:d,similarity:f})}return u.sort((d,m)=>m.similarity-d.similarity),u.slice(0,t)}function Q0(){let e=process.env.RECALL_RRF_K;if(e){let t=parseInt(e,10);if(!isNaN(t)&&t>=1&&t<=1e3)return t}return 60}function Zg(e){let t=Q0(),n=new Map;for(let r of e)for(let o=0;o<r.length;o++){let a=r[o],c=1/(t+o+1),u=n.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):n.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let s=[];for(let[r,o]of n)s.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return s.sort((r,o)=>o.score-r.score),s}Q();import{existsSync as nf,mkdirSync as ef,rmSync as tf,createWriteStream as Z0,statSync as eA}from"node:fs";import{join as Ur}from"node:path";import{createHash as tA}from"node:crypto";import{readFile as nA}from"node:fs/promises";var sA="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",sf=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function rf(){return Ur(B,"models","BAAI","bge-base-en-v1.5")}function Ge(){let e=rf();return sf.every(t=>nf(Ur(e,t.path)))}async function of(e){let t=rf();ef(t,{recursive:!0}),ef(Ur(t,"onnx"),{recursive:!0});for(let n of sf){let s=Ur(t,n.path),r=sA+n.path,o=0;nf(s)&&(o=eA(s).size);let a={};o>0&&(a.Range=`bytes=${o}-`);let c=await fetch(r,{headers:a});if(!c.ok&&c.status!==206)throw new Error(`Failed to download ${n.path}: HTTP ${c.status}`);let u=c.headers.get("content-length"),d=u?o+Number(u):0,m=c.body;if(!m)throw new Error(`No response body for ${n.path}`);let f=Z0(s,{flags:o>0?"a":"w"}),b=m.getReader(),T=o;for(;;){let{done:A,value:v}=await b.read();if(A)break;f.write(Buffer.from(v)),T+=v.byteLength,e?.(n.path,T,d)}if(f.end(),await new Promise((A,v)=>{f.on("finish",A),f.on("error",v)}),n.sha256==="TODO_PLACEHOLDER")throw tf(s),new Error(`Refusing to install: SHA-256 not pinned for ${n.path}. Update model-download.ts.`);let S=await nA(s);if(tA("sha256").update(S).digest("hex")!==n.sha256)throw tf(s),new Error(`SHA-256 mismatch for ${n.path}`)}}U();var rA=[/\btask\s+complete/i,/\bdone\b/i,/\bfinished\b/i,/\bimplemented\b/i,/\bcompleted\b/i,/\bshipped\b/i,/\ball\s+(?:tests?\s+)?pass/i,/\bsuccessfully\b/i],oA=1440*60*1e3;function iA(e){let t=h(),n=t.prepare(`SELECT content_text, tool_names FROM messages
1948
+ WHERE s.id = ?`).get(r.sessionId);return{sessionId:r.sessionId,project:r.project,alias:r.alias,startedAt:r.startedAt,endedAt:o?.ended_at??r.endedAt,firstUserMessage:o?.first_user_message??null,commitSha:r.commitSha,shortSha:r.commitSha.slice(0,7),subject:r.subject,committedAt:r.committedAt,cwd:t.cwd}}async function sf(){let e=K0(),t=e.semantic?Promise.resolve().then(()=>{try{return V0()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),n=e.cost?Promise.resolve().then(()=>{try{return Q0()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),s=e.git?eA().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,n,s]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}We();U();ts();var va=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,n){super(`semantic search refused: vec_chunks has ${t} rows (cap ${n}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=n}};function Ia(e,t={}){let n=t.limit??tA()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new va(r,n)}function tA(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}ss();import{Worker as nA}from"node:worker_threads";import{join as sA}from"node:path";var Ma=class extends Error{constructor(n){super(`semantic kNN exceeded ${n}ms wall-clock budget -- query aborted`);this.timeoutMs=n}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},rA=1e4;function oA(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function Da(e){let t=e.timeoutMs??oA()??rA,n=(e.workerFactory??iA)();return new Promise((s,r)=>{let o=setTimeout(()=>{n.terminate().catch(()=>{}),r(new Ma(t))},t);n.once("message",a=>{clearTimeout(o);let c=a;c&&c.ok===!0&&Array.isArray(c.hits)?s(c.hits):r(new Error(c?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",a=>{clearTimeout(o),n.terminate().catch(()=>{}),r(a)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function iA(){let e=sA(yt(),"dist","daemon","query-worker.js"),t={...process.env,RECALL_DB_PROFILE:"worker"};return new nA(e,{env:t})}async function rf(e,t=50){let n=h();return at(n),Ia(n),Da({query:e,limit:t})}async function of(e,t=10,n=.65){let s=h();at(s),Ia(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=await Da({precomputedVector:o.embedding,limit:t*5}),c=new Map;for(let d of a){if(d.sessionId===e)continue;let m=c.get(d.sessionId);(m===void 0||d.distance<m)&&c.set(d.sessionId,d.distance)}let u=[];for(let[d,m]of c){let f=1-m;f>=n&&u.push({sessionId:d,similarity:f})}return u.sort((d,m)=>m.similarity-d.similarity),u.slice(0,t)}function aA(){let e=process.env.RECALL_RRF_K;if(e){let t=parseInt(e,10);if(!isNaN(t)&&t>=1&&t<=1e3)return t}return 60}function af(e){let t=aA(),n=new Map;for(let r of e)for(let o=0;o<r.length;o++){let a=r[o],c=1/(t+o+1),u=n.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):n.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let s=[];for(let[r,o]of n)s.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return s.sort((r,o)=>o.score-r.score),s}Q();import{existsSync as uf,mkdirSync as cf,rmSync as lf,createWriteStream as cA,statSync as lA}from"node:fs";import{join as Hr}from"node:path";import{createHash as uA}from"node:crypto";import{readFile as dA}from"node:fs/promises";var pA="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",df=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function pf(){return Hr(H,"models","BAAI","bge-base-en-v1.5")}function Ge(){let e=pf();return df.every(t=>uf(Hr(e,t.path)))}async function mf(e){let t=pf();cf(t,{recursive:!0}),cf(Hr(t,"onnx"),{recursive:!0});for(let n of df){let s=Hr(t,n.path),r=pA+n.path,o=0;uf(s)&&(o=lA(s).size);let a={};o>0&&(a.Range=`bytes=${o}-`);let c=await fetch(r,{headers:a});if(!c.ok&&c.status!==206)throw new Error(`Failed to download ${n.path}: HTTP ${c.status}`);let u=c.headers.get("content-length"),d=u?o+Number(u):0,m=c.body;if(!m)throw new Error(`No response body for ${n.path}`);let f=cA(s,{flags:o>0?"a":"w"}),b=m.getReader(),T=o;for(;;){let{done:A,value:v}=await b.read();if(A)break;f.write(Buffer.from(v)),T+=v.byteLength,e?.(n.path,T,d)}if(f.end(),await new Promise((A,v)=>{f.on("finish",A),f.on("error",v)}),n.sha256==="TODO_PLACEHOLDER")throw lf(s),new Error(`Refusing to install: SHA-256 not pinned for ${n.path}. Update model-download.ts.`);let S=await dA(s);if(uA("sha256").update(S).digest("hex")!==n.sha256)throw lf(s),new Error(`SHA-256 mismatch for ${n.path}`)}}U();var mA=[/\btask\s+complete/i,/\bdone\b/i,/\bfinished\b/i,/\bimplemented\b/i,/\bcompleted\b/i,/\bshipped\b/i,/\ball\s+(?:tests?\s+)?pass/i,/\bsuccessfully\b/i],gA=1440*60*1e3;function fA(e){let t=h(),n=t.prepare(`SELECT content_text, tool_names FROM messages
1949
1949
  WHERE session_id = ? AND role = 'assistant'
1950
- ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let u of n)if(u.content_text&&rA.some(d=>d.test(u.content_text))){s=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1951
- WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let u of r){let d=u.tool_names??"",m=u.content_text??"";/\bWrite\b|\bEdit\b/.test(d)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(m)&&/pass|ok|✓/i.test(m)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(m)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(m)||/tsc.*(?:0 errors|no errors)/i.test(m))&&(o.buildSuccess=!0)}return s?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:s}:{status:"neutral",evidence:o,claimFound:s}}function aA(e){let t=iA(e);return h().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function af(e){let n=h().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(n?.verification_status&&n.verification_computed_at&&Date.now()-n.verification_computed_at<oA){let s={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:n.verification_status,evidence:s,claimFound:n.verification_status!=="neutral"}}return aA(e)}import{readFileSync as cA,writeFileSync as lA,mkdirSync as uA,chmodSync as dA}from"node:fs";import{join as cf}from"node:path";import{homedir as lf}from"node:os";function uf(){return cf(lf(),".recall","config.json")}function df(){try{return JSON.parse(cA(uf(),"utf-8"))}catch{return{}}}function pA(e){let t=uf();uA(cf(lf(),".recall"),{recursive:!0}),lA(t,JSON.stringify(e,null,2)+`
1952
- `,"utf-8"),dA(t,384)}function Ca(){let t=df().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function pf(e){let t=df();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},pA(t)}var TA=5e3,$a={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},va=0,Ia=$a,Ma=null;async function yA(){return Date.now()-va>=TA&&!Ma&&(Ma=Ps().then(n=>(Ia=n,va=Date.now(),n)).catch(()=>(va=Date.now(),Ia=$a,$a)).finally(()=>{Ma=null})),Ia}var wA=2e3,RA=6,Wr=new Map;function Qn(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function kA(e,t){let n=Date.now(),s=(Wr.get(e)??[]).filter(a=>n-a.ts<wA);return Wr.set(e,s),s.length<2||s[s.length-1].name===t?!1:s.slice(0,-1).some(a=>a.name===t)}function AA(e,t){let n=Wr.get(e)??[];for(n.push({name:t,ts:Date.now()});n.length>RA;)n.shift();Wr.set(e,n)}function gf(e,t){let n=t.trim();if(!n)return 0;if(ue(n)){let o=It(n);if(!o)return 0;n=o}if(pe(n))return 0;if(kA(e,n))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${n}", flap signature (competing editor sync sources)`),0;AA(e,n);let s=M.sessionsFor(e),r=0;for(let o of s)try{if(Se(o)===n)continue;_e(o,n),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${n}" propagated to ${r} session(s)`),r}var ff=(()=>{try{let e=qr(Ha(Ba(import.meta.url)),"..","..","package.json");return JSON.parse(Fa(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),Da=!1,ja=!1,Pa=!1,NA=Ha(Ba(import.meta.url)),Ua=qr(NA,"..","web"),Ef=qr(Ua,"index.html"),OA=fA(Ef);function _f(){return h().prepare(`SELECT
1950
+ ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let u of n)if(u.content_text&&mA.some(d=>d.test(u.content_text))){s=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1951
+ WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let u of r){let d=u.tool_names??"",m=u.content_text??"";/\bWrite\b|\bEdit\b/.test(d)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(m)&&/pass|ok|✓/i.test(m)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(m)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(m)||/tsc.*(?:0 errors|no errors)/i.test(m))&&(o.buildSuccess=!0)}return s?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:s}:{status:"neutral",evidence:o,claimFound:s}}function _A(e){let t=fA(e);return h().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function gf(e){let n=h().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(n?.verification_status&&n.verification_computed_at&&Date.now()-n.verification_computed_at<gA){let s={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:n.verification_status,evidence:s,claimFound:n.verification_status!=="neutral"}}return _A(e)}import{readFileSync as hA,writeFileSync as EA,mkdirSync as bA,chmodSync as SA}from"node:fs";import{join as ff}from"node:path";import{homedir as _f}from"node:os";function hf(){return ff(_f(),".recall","config.json")}function Ef(){try{return JSON.parse(hA(hf(),"utf-8"))}catch{return{}}}function TA(e){let t=hf();bA(ff(_f(),".recall"),{recursive:!0}),EA(t,JSON.stringify(e,null,2)+`
1952
+ `,"utf-8"),SA(t,384)}function ja(){let t=Ef().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function bf(e){let t=Ef();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},TA(t)}var CA=5e3,qa={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},Pa=0,Fa=qa,$a=null;async function LA(){return Date.now()-Pa>=CA&&!$a&&($a=Fs().then(n=>(Fa=n,Pa=Date.now(),n)).catch(()=>(Pa=Date.now(),Fa=qa,qa)).finally(()=>{$a=null})),Fa}var vA=2e3,IA=6,Xr=new Map;function Qn(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function MA(e,t){let n=Date.now(),s=(Xr.get(e)??[]).filter(a=>n-a.ts<vA);return Xr.set(e,s),s.length<2||s[s.length-1].name===t?!1:s.slice(0,-1).some(a=>a.name===t)}function DA(e,t){let n=Xr.get(e)??[];for(n.push({name:t,ts:Date.now()});n.length>IA;)n.shift();Xr.set(e,n)}function Tf(e,t){let n=t.trim();if(!n)return 0;if(ue(n)){let o=It(n);if(!o)return 0;n=o}if(pe(n))return 0;if(MA(e,n))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${n}", flap signature (competing editor sync sources)`),0;DA(e,n);let s=M.sessionsFor(e),r=0;for(let o of s)try{if(Se(o)===n)continue;_e(o,n),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${n}" propagated to ${r} session(s)`),r}var yf=(()=>{try{let e=Jr(Ja(Ga(import.meta.url)),"..","..","package.json");return JSON.parse(Wa(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),Ua=!1,Ba=!1,Ha=!1,PA=Ja(Ga(import.meta.url)),Xa=Jr(PA,"..","web"),kf=Jr(Xa,"index.html"),FA=RA(kf);function wf(){return h().prepare(`SELECT
1953
1953
  (SELECT COUNT(*) FROM projects) AS projects,
1954
1954
  (SELECT COUNT(DISTINCT COALESCE(repo_root, decoded_path)) FROM projects) AS repos,
1955
1955
  (SELECT COUNT(*) FROM sessions) AS sessions,
1956
1956
  (SELECT COUNT(*) FROM messages) AS messages,
1957
1957
  (SELECT MIN(started_at) FROM sessions WHERE started_at IS NOT NULL) AS earliest,
1958
- (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var LA=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,CA=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function Fe(e,t){if((await it()).tier!=="pro")return e.json({error:"pro_required",message:"This feature requires a Claude Recall Pro license.",upgrade_url:"https://clauderecall.com/pricing",activate_command:"recall activate <license-key>"},402);await t()}var Hr=new Map,bf=0,hf=0,Sf=null,vA=6e4;function IA(){bf+=1;let e=Date.now();e-hf<vA||(hf=e,console.warn("[auth] /api/terminal/* request rejected without a valid X-Recall-Token. The VS Code / Cursor extension is likely outdated relative to the daemon \u2014 tab names will not flow through, and session titles will fall back to the heuristic first-message snippet. Reinstall: `code --install-extension extensions/vscode/clauderecall-vscode-*.vsix` and restart the extension host. Run `recall doctor` for a full pipeline view."))}function MA(){Sf=new Date().toISOString()}var DA=6e4,jA=5,Ye=[];function PA(e){let t=Date.now(),n=Ye[Ye.length-1];if(!n||t-n.ts>=DA)for(Ye.push({ts:t,remainingChunks:e});Ye.length>jA;)Ye.shift();if(Ye.length<2)return{deltaPerSec:0};let s=Ye[0],r=Ye[Ye.length-1],o=Math.max(1,(r.ts-s.ts)/1e3),a=r.remainingChunks-s.remainingChunks;return{deltaPerSec:Math.max(0,a/o)}}function FA(){let e=Hl();return{silentTerminalRejections:bf,lastTerminalSyncAt:Sf,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns},watcherReindexHotFiles:nr(10)}}function Br(e,t){if(t)return"";let n=e;return` AND COALESCE(${n}.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[skill]%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are summarizing a Claude Code session%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are extracting a structured Output Index%' AND COALESCE(${n}.title_quality, '') != 'programmatic'`}function $A(e){let t=new mA;if(t.use("*",xA({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let p=i.req.raw.headers.get("host")??"";if(!LA.test(p))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!CA.test(g))return i.text("Forbidden: cross-origin request rejected",403);await l()}),e){let i=Buffer.from(e,"utf8");t.use("/api/*",async(l,p)=>{if(l.req.method==="GET"&&l.req.path==="/api/health")return p();let g=l.req.raw.headers.get("x-recall-token")??"";!g&&l.req.method==="GET"&&(g=new URL(l.req.url).searchParams.get("token")??"");let _=!1;if(g.length===e.length)try{_=bA(Buffer.from(g,"utf8"),i)}catch{_=!1}return _?p():(l.req.path.startsWith("/api/terminal/")&&IA(),l.json({error:"unauthorized"},401))})}t.use("*",async(i,l)=>{await l(),i.res.headers.set("Content-Security-Policy","default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://clauderecall.com; font-src 'self' data:; connect-src 'self' data: https://clauderecall.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"),i.res.headers.set("X-Content-Type-Options","nosniff"),i.res.headers.set("X-Frame-Options","DENY"),i.res.headers.set("Referrer-Policy","no-referrer"),i.res.headers.set("Cross-Origin-Resource-Policy","same-origin")}),t.get("/api/health",i=>i.json({status:"ok",version:ff,uptimeSeconds:Math.round(process.uptime()),pipeline:FA()})),t.get("/api/stats",i=>i.json(_f())),t.get("/api/stats/session/:id",i=>{let l=Xg(i.req.param("id"));return l?i.json(l):i.json({error:"session not found"},404)}),t.get("/api/stats/project/:name",i=>{let l=Jg(i.req.param("name"));return l?i.json(l):i.json({error:"project not found"},404)}),t.get("/api/stats/overview",i=>{let l=i.req.query("range"),p=l==="7d"?"7d":l==="30d"?"30d":"all";return i.json(Gg(p))}),t.post("/api/stats/backfill",async i=>{let l=await i.req.json().catch(()=>({})),p=l.limit?Math.max(1,Math.min(1e5,Number(l.limit))):5e3;if(p>5e3){let _=Bg({limit:p});return i.json({mode:"background",started:_,alreadyRunning:!_&&Wg(),limit:p,lastRun:ya()})}let g=Hg({limit:p});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:p,result:g,lastRun:ya()})}),t.get("/api/stats/health",i=>i.json(Yg())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),p=ka(l);return p?i.json(p):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:Ca()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&pf(l.enabled),i.json({enabled:Ca()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),p=af(l);return i.json(p)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=h().prepare(`SELECT tool_names, raw_json FROM messages
1959
- WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''`).all(l),_=g.length,E=new Set;for(let y of g){if(!/Read|Write|Edit/.test(y.tool_names))continue;let R=y.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(R)for(let O of R){let N=O.match(/":\s*"([^"]+)"/);N&&E.add(N[1])}}return i.json({filesReferenced:E.size,toolCallCount:_})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),p=Ko(l);if(p.length>0||i.req.query("refresh")!=="1")return i.json({commits:p});let g=await zo(l);return i.json({commits:Ko(l),status:g.status})}),t.get("/api/commits/:sha/session",i=>{let l=i.req.param("sha");return/^[0-9a-fA-F]{4,40}$/.test(l)?i.json({sessions:Fs(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await it();return i.json(l)}),t.post("/api/feedback",async i=>{let l;try{l=await i.req.json()}catch{return i.json({error:"invalid json"},400)}if(!l||typeof l!="object")return i.json({error:"invalid body"},400);let p=process.env.RECALL_FEEDBACK_API??"https://clauderecall.com/api/feedback",g=p==="https://clauderecall.com/api/feedback",_=await it(),E=cn(),y=g&&_.tier==="pro"&&E?E.license_jwt:null,R=(()=>{try{let C=qr(Ha(Ba(import.meta.url)),"..","..","package.json");return JSON.parse(Fa(C,"utf8")).version}catch{return"unknown"}})(),O=l,N={score:O.score,comment:O.comment??null,surface:"web",version:typeof O.version=="string"?O.version:R,os:typeof O.os=="string"?O.os:process.platform,trigger_kind:typeof O.trigger_kind=="string"?O.trigger_kind:"manual",license_jwt:y};try{let C=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(N)}),x=await C.json().catch(()=>({}));return i.json(x,C.status)}catch(C){let x=C instanceof Error?C.message:"network error";return i.json({error:"upstream_unreachable",detail:x},502)}}),t.get("/api/discover/today",Fe,async i=>{try{return i.json(await Kg())}catch(l){return console.error("[discover.today]",l),i.json({rediscovered:null,expensive:null,authored:null,availability:{semantic:!1,cost:!1,git:!1},generatedAt:new Date().toISOString(),error:l.message},500)}}),t.get("/api/macro-repos",i=>i.json({macro_repos:Zi(),orphan_projects:bm()})),t.get("/api/bug-synthesis",i=>{let l=i.req.query("scope"),p=i.req.query("target_id"),g=i.req.query("limit"),_=l==="cluster"||l==="project"?l:void 0,E=g?Math.max(1,Number(g)):50,y=xm({scope:_,target_id:p??void 0,limit:E});return i.json({results:y})}),t.get("/api/bug-synthesis/counts",i=>{let l=i.req.query("scope");if(l!=="cluster"&&l!=="project")return i.json({error:'scope must be "cluster" or "project"'},400);let p=Nm(l);return i.json({counts:Array.from(p.entries()).map(([g,_])=>({target_id:g,count:_}))})}),t.get("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=ea(l);return p?i.json({result:p}):i.json({error:"not found"},404)}),t.delete("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(Om(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let n=j.object({name:j.string().min(1).max(100),description:j.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),p=n.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=Sm({name:p.data.name,description:p.data.description??null});return i.json({macro_repo:g},201)}catch(g){let _=g instanceof Error?g.message:String(g);return _.includes("UNIQUE constraint")?i.json({error:`a macro repo named "${p.data.name}" already exists`},409):i.json({error:_},400)}});let s=j.object({name:j.string().min(1).max(100).optional(),description:j.string().max(500).nullable().optional()});t.patch("/api/macro-repos/:id",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),g=s.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{let _=Tm(l,g.data);return i.json({macro_repo:_})}catch(_){let E=_ instanceof Error?_.message:String(_);return E.includes("not found")?i.json({error:E},404):E.includes("UNIQUE constraint")?i.json({error:"another macro repo already has that name"},409):i.json({error:E},400)}}),t.delete("/api/macro-repos/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(ym(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=j.object({project_id:j.number().int().positive()});t.post("/api/macro-repos/:id/members",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),g=r.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{return wm(l,g.data.project_id),i.json({macro_repo:Gt(l)})}catch(_){let E=_ instanceof Error?_.message:String(_);return i.json({error:E},E.includes("not found")?404:400)}}),t.delete("/api/macro-repos/:id/members/:projectId",i=>{let l=Number(i.req.param("id")),p=Number(i.req.param("projectId"));return!Number.isFinite(l)||!Number.isFinite(p)?i.json({error:"invalid id"},400):(Rm(l,p),i.json({macro_repo:Gt(l)}))}),t.get("/api/projects",i=>{let l=h(),p=i.req.query("system")==="1"||i.req.query("system")==="true",g=Br("s",p),_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1958
+ (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var $A=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,UA=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function Fe(e,t){if((await it()).tier!=="pro")return e.json({error:"pro_required",message:"This feature requires a Claude Recall Pro license.",upgrade_url:"https://clauderecall.com/pricing",activate_command:"recall activate <license-key>"},402);await t()}var Wr=new Map,Af=0,Rf=0,xf=null,BA=6e4;function HA(){Af+=1;let e=Date.now();e-Rf<BA||(Rf=e,console.warn("[auth] /api/terminal/* request rejected without a valid X-Recall-Token. The VS Code / Cursor extension is likely outdated relative to the daemon \u2014 tab names will not flow through, and session titles will fall back to the heuristic first-message snippet. Reinstall: `code --install-extension extensions/vscode/clauderecall-vscode-*.vsix` and restart the extension host. Run `recall doctor` for a full pipeline view."))}function WA(){xf=new Date().toISOString()}var qA=6e4,XA=5,Ye=[];function JA(e){let t=Date.now(),n=Ye[Ye.length-1];if(!n||t-n.ts>=qA)for(Ye.push({ts:t,remainingChunks:e});Ye.length>XA;)Ye.shift();if(Ye.length<2)return{deltaPerSec:0};let s=Ye[0],r=Ye[Ye.length-1],o=Math.max(1,(r.ts-s.ts)/1e3),a=r.remainingChunks-s.remainingChunks;return{deltaPerSec:Math.max(0,a/o)}}function GA(){let e=Jl();return{silentTerminalRejections:Af,lastTerminalSyncAt:xf,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns},watcherReindexHotFiles:sr(10)}}function qr(e,t){if(t)return"";let n=e;return` AND COALESCE(${n}.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[skill]%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are summarizing a Claude Code session%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are extracting a structured Output Index%' AND COALESCE(${n}.title_quality, '') != 'programmatic'`}function YA(e){let t=new yA;if(t.use("*",jA({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let p=i.req.raw.headers.get("host")??"";if(!$A.test(p))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!UA.test(g))return i.text("Forbidden: cross-origin request rejected",403);await l()}),e){let i=Buffer.from(e,"utf8");t.use("/api/*",async(l,p)=>{if(l.req.method==="GET"&&l.req.path==="/api/health")return p();let g=l.req.raw.headers.get("x-recall-token")??"";!g&&l.req.method==="GET"&&(g=new URL(l.req.url).searchParams.get("token")??"");let _=!1;if(g.length===e.length)try{_=NA(Buffer.from(g,"utf8"),i)}catch{_=!1}return _?p():(l.req.path.startsWith("/api/terminal/")&&HA(),l.json({error:"unauthorized"},401))})}t.use("*",async(i,l)=>{await l(),i.res.headers.set("Content-Security-Policy","default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://clauderecall.com; font-src 'self' data:; connect-src 'self' data: https://clauderecall.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"),i.res.headers.set("X-Content-Type-Options","nosniff"),i.res.headers.set("X-Frame-Options","DENY"),i.res.headers.set("Referrer-Policy","no-referrer"),i.res.headers.set("Cross-Origin-Resource-Policy","same-origin")}),t.get("/api/health",i=>i.json({status:"ok",version:yf,uptimeSeconds:Math.round(process.uptime()),pipeline:GA()})),t.get("/api/stats",i=>i.json(wf())),t.get("/api/stats/session/:id",i=>{let l=Qg(i.req.param("id"));return l?i.json(l):i.json({error:"session not found"},404)}),t.get("/api/stats/project/:name",i=>{let l=Zg(i.req.param("name"));return l?i.json(l):i.json({error:"project not found"},404)}),t.get("/api/stats/overview",i=>{let l=i.req.query("range"),p=l==="7d"?"7d":l==="30d"?"30d":"all";return i.json(ef(p))}),t.post("/api/stats/backfill",async i=>{let l=await i.req.json().catch(()=>({})),p=l.limit?Math.max(1,Math.min(1e5,Number(l.limit))):5e3;if(p>5e3){let _=zg({limit:p});return i.json({mode:"background",started:_,alreadyRunning:!_&&Kg(),limit:p,lastRun:xa()})}let g=Yg({limit:p});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:p,result:g,lastRun:xa()})}),t.get("/api/stats/health",i=>i.json(tf())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),p=Ca(l);return p?i.json(p):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:ja()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&bf(l.enabled),i.json({enabled:ja()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),p=gf(l);return i.json(p)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=h().prepare(`SELECT tool_names, raw_json FROM messages
1959
+ WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''`).all(l),_=g.length,E=new Set;for(let y of g){if(!/Read|Write|Edit/.test(y.tool_names))continue;let k=y.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(k)for(let O of k){let N=O.match(/":\s*"([^"]+)"/);N&&E.add(N[1])}}return i.json({filesReferenced:E.size,toolCallCount:_})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),p=Zo(l);if(p.length>0||i.req.query("refresh")!=="1")return i.json({commits:p});let g=await Qo(l);return i.json({commits:Zo(l),status:g.status})}),t.get("/api/commits/:sha/session",i=>{let l=i.req.param("sha");return/^[0-9a-fA-F]{4,40}$/.test(l)?i.json({sessions:$s(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await it();return i.json(l)}),t.post("/api/feedback",async i=>{let l;try{l=await i.req.json()}catch{return i.json({error:"invalid json"},400)}if(!l||typeof l!="object")return i.json({error:"invalid body"},400);let p=process.env.RECALL_FEEDBACK_API??"https://clauderecall.com/api/feedback",g=p==="https://clauderecall.com/api/feedback",_=await it(),E=cn(),y=g&&_.tier==="pro"&&E?E.license_jwt:null,k=(()=>{try{let L=Jr(Ja(Ga(import.meta.url)),"..","..","package.json");return JSON.parse(Wa(L,"utf8")).version}catch{return"unknown"}})(),O=l,N={score:O.score,comment:O.comment??null,surface:"web",version:typeof O.version=="string"?O.version:k,os:typeof O.os=="string"?O.os:process.platform,trigger_kind:typeof O.trigger_kind=="string"?O.trigger_kind:"manual",license_jwt:y};try{let L=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(N)}),x=await L.json().catch(()=>({}));return i.json(x,L.status)}catch(L){let x=L instanceof Error?L.message:"network error";return i.json({error:"upstream_unreachable",detail:x},502)}}),t.get("/api/discover/today",Fe,async i=>{try{return i.json(await sf())}catch(l){return console.error("[discover.today]",l),i.json({rediscovered:null,expensive:null,authored:null,availability:{semantic:!1,cost:!1,git:!1},generatedAt:new Date().toISOString(),error:l.message},500)}}),t.get("/api/macro-repos",i=>i.json({macro_repos:ra(),orphan_projects:Am()})),t.get("/api/bug-synthesis",i=>{let l=i.req.query("scope"),p=i.req.query("target_id"),g=i.req.query("limit"),_=l==="cluster"||l==="project"?l:void 0,E=g?Math.max(1,Number(g)):50,y=Mm({scope:_,target_id:p??void 0,limit:E});return i.json({results:y})}),t.get("/api/bug-synthesis/counts",i=>{let l=i.req.query("scope");if(l!=="cluster"&&l!=="project")return i.json({error:'scope must be "cluster" or "project"'},400);let p=Dm(l);return i.json({counts:Array.from(p.entries()).map(([g,_])=>({target_id:g,count:_}))})}),t.get("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=oa(l);return p?i.json({result:p}):i.json({error:"not found"},404)}),t.delete("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(jm(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let n=j.object({name:j.string().min(1).max(100),description:j.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),p=n.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=xm({name:p.data.name,description:p.data.description??null});return i.json({macro_repo:g},201)}catch(g){let _=g instanceof Error?g.message:String(g);return _.includes("UNIQUE constraint")?i.json({error:`a macro repo named "${p.data.name}" already exists`},409):i.json({error:_},400)}});let s=j.object({name:j.string().min(1).max(100).optional(),description:j.string().max(500).nullable().optional()});t.patch("/api/macro-repos/:id",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),g=s.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{let _=Nm(l,g.data);return i.json({macro_repo:_})}catch(_){let E=_ instanceof Error?_.message:String(_);return E.includes("not found")?i.json({error:E},404):E.includes("UNIQUE constraint")?i.json({error:"another macro repo already has that name"},409):i.json({error:E},400)}}),t.delete("/api/macro-repos/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(Om(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=j.object({project_id:j.number().int().positive()});t.post("/api/macro-repos/:id/members",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),g=r.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{return Cm(l,g.data.project_id),i.json({macro_repo:Gt(l)})}catch(_){let E=_ instanceof Error?_.message:String(_);return i.json({error:E},E.includes("not found")?404:400)}}),t.delete("/api/macro-repos/:id/members/:projectId",i=>{let l=Number(i.req.param("id")),p=Number(i.req.param("projectId"));return!Number.isFinite(l)||!Number.isFinite(p)?i.json({error:"invalid id"},400):(Lm(l,p),i.json({macro_repo:Gt(l)}))}),t.get("/api/projects",i=>{let l=h(),p=i.req.query("system")==="1"||i.req.query("system")==="true",g=qr("s",p),_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1960
1960
  COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count,
1961
1961
  COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count,
1962
1962
  MAX(COALESCE(s.ended_at, s.started_at)) AS latest
1963
1963
  FROM projects p
1964
1964
  LEFT JOIN sessions s ON s.project_id = p.id
1965
1965
  GROUP BY p.id
1966
- ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),E=i.req.query("groupBy"),y=E==="repo"||E==="repo-flat"?E:"folder";if(y==="folder")return i.json(_);let R=y==="repo-flat"?"COALESCE(p.main_repo, p.repo_root, p.decoded_path)":"COALESCE(p.repo_root, p.decoded_path)",O=l.prepare(`SELECT ${R} AS repo_root,
1966
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),E=i.req.query("groupBy"),y=E==="repo"||E==="repo-flat"?E:"folder";if(y==="folder")return i.json(_);let k=y==="repo-flat"?"COALESCE(p.main_repo, p.repo_root, p.decoded_path)":"COALESCE(p.repo_root, p.decoded_path)",O=l.prepare(`SELECT ${k} AS repo_root,
1967
1967
  COUNT(DISTINCT p.id) AS folder_count,
1968
1968
  COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count_total,
1969
1969
  COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count_total,
@@ -1973,16 +1973,16 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1973
1973
  ${y==="repo-flat"?"NULL":"MAX(p.main_repo)"} AS main_repo
1974
1974
  FROM projects p
1975
1975
  LEFT JOIN sessions s ON s.project_id = p.id
1976
- GROUP BY ${R}
1976
+ GROUP BY ${k}
1977
1977
  ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),N=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1978
1978
  COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count,
1979
1979
  COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count,
1980
1980
  MAX(COALESCE(s.ended_at, s.started_at)) AS latest
1981
1981
  FROM projects p
1982
1982
  LEFT JOIN sessions s ON s.project_id = p.id
1983
- WHERE ${R} = ?
1983
+ WHERE ${k} = ?
1984
1984
  GROUP BY p.id
1985
- ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`),C=O.map(x=>({repo_root:x.repo_root,name:x.repo_root?x.repo_root.split("/").filter(Boolean).pop()??x.repo_root:"(unknown)",is_repo:x.is_repo===1,is_worktree:x.is_worktree===1,main_repo:x.main_repo,session_count_total:x.session_count_total,message_count_total:x.message_count_total,latest:x.latest,folders:N.all(x.repo_root)}));return i.json({groupBy:y,groups:C})}),t.get("/api/graph/:project",i=>{let l=h(),p=i.req.param("project"),g=l.prepare(`SELECT id, name, decoded_path FROM projects
1985
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`),L=O.map(x=>({repo_root:x.repo_root,name:x.repo_root?x.repo_root.split("/").filter(Boolean).pop()??x.repo_root:"(unknown)",is_repo:x.is_repo===1,is_worktree:x.is_worktree===1,main_repo:x.main_repo,session_count_total:x.session_count_total,message_count_total:x.message_count_total,latest:x.latest,folders:N.all(x.repo_root)}));return i.json({groupBy:y,groups:L})}),t.get("/api/graph/:project",i=>{let l=h(),p=i.req.param("project"),g=l.prepare(`SELECT id, name, decoded_path FROM projects
1986
1986
  WHERE name = ? LIMIT 1`).get(p);if(!g)return i.json({error:`project "${p}" not found`},404);let _=i.req.query("system")==="1"||i.req.query("system")==="true",E=l.prepare(`SELECT s.id,
1987
1987
  s.auto_title,
1988
1988
  s.auto_title_source,
@@ -1993,12 +1993,12 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1993
1993
  sa.alias AS alias
1994
1994
  FROM sessions s
1995
1995
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1996
- WHERE s.project_id = ?${Br("s",_)}
1997
- ORDER BY s.started_at`).all(g.id),y=E.map(N=>N.id),R=[];if(y.length>0){let N=y.map(()=>"?").join(",");R=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1996
+ WHERE s.project_id = ?${qr("s",_)}
1997
+ ORDER BY s.started_at`).all(g.id),y=E.map(N=>N.id),k=[];if(y.length>0){let N=y.map(()=>"?").join(",");k=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1998
1998
  FROM thread_edges
1999
1999
  WHERE session_id IN (${N})
2000
2000
  AND (parent_session_id IS NULL
2001
- OR parent_session_id IN (${N}))`).all(...y,...y)}let O=E.map(N=>{let C=N.alias??N.auto_title??N.first_user_message??N.id.slice(0,8),x=null,k=null;if(N.auto_title?.startsWith("/")){let L=N.auto_title.split(" \xB7 ");x=L[0],k=L.length>1?L.slice(1).join(" \xB7 "):null}return{id:N.id.slice(0,8),full_id:N.id,title:C,alias:N.alias,auto_title:N.auto_title,auto_title_source:N.auto_title_source,title_quality:N.title_quality,started_at:N.started_at,msgs:N.message_count,skill:x,brand:k}});return i.json({project:g,sessions:O,thread_edges:R})});let o=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),a=new Set(["pending","approved","rejected"]),c=new Set(["L1","L2","L3","L4","user"]);t.get("/api/links",i=>{let l=i.req.query("source_id")??void 0,p=i.req.query("target_id")??void 0,g=i.req.query("type"),_=i.req.query("approved"),E=i.req.query("limit"),y;if(g){if(!o.has(g))return i.json({error:`invalid type: ${g}`},400);y=g}let R=_==="1"||_==="true",O=E?Number(E):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);try{let N=ms({sourceSessionId:l,targetSessionId:p,linkType:y,approvedOnly:R,limit:O});return i.json({links:N})}catch(N){return i.json({error:N.message},400)}}),t.get("/api/links/suggestions",i=>{let l=i.req.query("status"),p=i.req.query("source_id")??void 0,g=i.req.query("target_id")??void 0,_=i.req.query("inferred_by"),E=i.req.query("limit"),y;if(l){if(!a.has(l))return i.json({error:`invalid status: ${l}`},400);y=l}let R;if(_){if(!c.has(_))return i.json({error:`invalid inferred_by: ${_}`},400);R=_}let O=E?Number(E):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);try{let N=wt({status:y,sourceSessionId:p,targetSessionId:g,inferredBy:R,limit:O}),C=new Set;for(let L of N)C.add(L.source_session_id),C.add(L.target_session_id);let x=new Map;if(C.size>0){let L=Array.from(C),F=L.map(()=>"?").join(","),K=h().prepare(`SELECT s.id,
2001
+ OR parent_session_id IN (${N}))`).all(...y,...y)}let O=E.map(N=>{let L=N.alias??N.auto_title??N.first_user_message??N.id.slice(0,8),x=null,w=null;if(N.auto_title?.startsWith("/")){let C=N.auto_title.split(" \xB7 ");x=C[0],w=C.length>1?C.slice(1).join(" \xB7 "):null}return{id:N.id.slice(0,8),full_id:N.id,title:L,alias:N.alias,auto_title:N.auto_title,auto_title_source:N.auto_title_source,title_quality:N.title_quality,started_at:N.started_at,msgs:N.message_count,skill:x,brand:w}});return i.json({project:g,sessions:O,thread_edges:k})});let o=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),a=new Set(["pending","approved","rejected"]),c=new Set(["L1","L2","L3","L4","user"]);t.get("/api/links",i=>{let l=i.req.query("source_id")??void 0,p=i.req.query("target_id")??void 0,g=i.req.query("type"),_=i.req.query("approved"),E=i.req.query("limit"),y;if(g){if(!o.has(g))return i.json({error:`invalid type: ${g}`},400);y=g}let k=_==="1"||_==="true",O=E?Number(E):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);try{let N=ms({sourceSessionId:l,targetSessionId:p,linkType:y,approvedOnly:k,limit:O});return i.json({links:N})}catch(N){return i.json({error:N.message},400)}}),t.get("/api/links/suggestions",i=>{let l=i.req.query("status"),p=i.req.query("source_id")??void 0,g=i.req.query("target_id")??void 0,_=i.req.query("inferred_by"),E=i.req.query("limit"),y;if(l){if(!a.has(l))return i.json({error:`invalid status: ${l}`},400);y=l}let k;if(_){if(!c.has(_))return i.json({error:`invalid inferred_by: ${_}`},400);k=_}let O=E?Number(E):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);try{let N=wt({status:y,sourceSessionId:p,targetSessionId:g,inferredBy:k,limit:O}),L=new Set;for(let C of N)L.add(C.source_session_id),L.add(C.target_session_id);let x=new Map;if(L.size>0){let C=Array.from(L),F=C.map(()=>"?").join(","),K=h().prepare(`SELECT s.id,
2002
2002
  NULLIF(sa.alias, '') AS alias,
2003
2003
  s.auto_title,
2004
2004
  s.first_user_message,
@@ -2006,7 +2006,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2006
2006
  FROM sessions s
2007
2007
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2008
2008
  LEFT JOIN projects p ON p.id = s.project_id
2009
- WHERE s.id IN (${F})`).all(...L);for(let J of K){let H=J.first_user_message?J.first_user_message.slice(0,80):null,q=J.alias??J.auto_title??H??J.id.slice(0,8);x.set(J.id,{title:q,project:J.project})}}let k=N.map(L=>{let F=x.get(L.source_session_id),W=x.get(L.target_session_id);return{...L,source_title:F?.title??L.source_session_id.slice(0,8),source_project:F?.project??null,target_title:W?.title??L.target_session_id.slice(0,8),target_project:W?.project??null}});return i.json({suggestions:k})}catch(N){return i.json({error:N.message},400)}}),t.get("/api/output-index/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=lt(l);return p?i.json(p):i.json({error:`no output index for session ${l}`},404)});let u=new Set(["pagerank","embedding-rerank","hybrid"]);t.get("/api/neighborhood/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=i.req.query("budget"),g=p!==void 0?Number(p):4e3;if(!Number.isFinite(g)||g<100)return i.json({error:"budget must be a number \u2265 100"},400);let _=i.req.query("scoring")??"hybrid";if(!u.has(_))return i.json({error:`invalid scoring: ${_}; valid: pagerank, embedding-rerank, hybrid`},400);let E=_,y=i.req.query("max_depth"),R=y!==void 0?Number(y):2;if(!Number.isFinite(R)||R<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let O,N=i.req.query("edge_types");if(N){let F=N.split(",").map(W=>W.trim()).filter(Boolean);for(let W of F)if(!o.has(W))return i.json({error:`invalid edge_type: ${W}`},400);O=F}let C=i.req.query("include_wiki_links"),x=C===void 0?!0:!(C==="0"||C==="false"),k=i.req.query("include_suggestions"),L=k==="1"||k==="true";try{let F=Tr(l,{budget:g,scoring:E,maxDepth:R,edgeTypes:O,includeWikiLinks:x,includeSuggestions:L});return i.json(F)}catch(F){let W=F instanceof Error?F.message:"unknown error",K=/not found/.test(W)?404:500;return i.json({error:W},K)}}),t.get("/api/bug-patterns",i=>{let l=i.req.query("min_count"),p=i.req.query("status"),g=i.req.query("project")??void 0,_=i.req.query("limit"),E=i.req.query("offset"),y=l?Number(l):void 0;if(y!==void 0&&(!Number.isFinite(y)||y<1))return i.json({error:"min_count must be a positive integer"},400);let R;if(p==="open")R=!1;else if(p==="resolved")R=!0;else if(p&&p!=="all")return i.json({error:`invalid status: ${p}; valid: open, resolved, all`},400);let O=_?Number(_):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);let N=E?Number(E):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<0))return i.json({error:"invalid offset"},400);try{let C=bc({minOccurrenceCount:y,hasResolved:R,project:g,limit:O,offset:N});return i.json(C)}catch(C){return i.json({error:C.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=h(),g=l.prepare(`SELECT p.name AS project,
2009
+ WHERE s.id IN (${F})`).all(...C);for(let J of K){let B=J.first_user_message?J.first_user_message.slice(0,80):null,q=J.alias??J.auto_title??B??J.id.slice(0,8);x.set(J.id,{title:q,project:J.project})}}let w=N.map(C=>{let F=x.get(C.source_session_id),W=x.get(C.target_session_id);return{...C,source_title:F?.title??C.source_session_id.slice(0,8),source_project:F?.project??null,target_title:W?.title??C.target_session_id.slice(0,8),target_project:W?.project??null}});return i.json({suggestions:w})}catch(N){return i.json({error:N.message},400)}}),t.get("/api/output-index/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=lt(l);return p?i.json(p):i.json({error:`no output index for session ${l}`},404)});let u=new Set(["pagerank","embedding-rerank","hybrid"]);t.get("/api/neighborhood/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=i.req.query("budget"),g=p!==void 0?Number(p):4e3;if(!Number.isFinite(g)||g<100)return i.json({error:"budget must be a number \u2265 100"},400);let _=i.req.query("scoring")??"hybrid";if(!u.has(_))return i.json({error:`invalid scoring: ${_}; valid: pagerank, embedding-rerank, hybrid`},400);let E=_,y=i.req.query("max_depth"),k=y!==void 0?Number(y):2;if(!Number.isFinite(k)||k<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let O,N=i.req.query("edge_types");if(N){let F=N.split(",").map(W=>W.trim()).filter(Boolean);for(let W of F)if(!o.has(W))return i.json({error:`invalid edge_type: ${W}`},400);O=F}let L=i.req.query("include_wiki_links"),x=L===void 0?!0:!(L==="0"||L==="false"),w=i.req.query("include_suggestions"),C=w==="1"||w==="true";try{let F=wr(l,{budget:g,scoring:E,maxDepth:k,edgeTypes:O,includeWikiLinks:x,includeSuggestions:C});return i.json(F)}catch(F){let W=F instanceof Error?F.message:"unknown error",K=/not found/.test(W)?404:500;return i.json({error:W},K)}}),t.get("/api/bug-patterns",i=>{let l=i.req.query("min_count"),p=i.req.query("status"),g=i.req.query("project")??void 0,_=i.req.query("limit"),E=i.req.query("offset"),y=l?Number(l):void 0;if(y!==void 0&&(!Number.isFinite(y)||y<1))return i.json({error:"min_count must be a positive integer"},400);let k;if(p==="open")k=!1;else if(p==="resolved")k=!0;else if(p&&p!=="all")return i.json({error:`invalid status: ${p}; valid: open, resolved, all`},400);let O=_?Number(_):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);let N=E?Number(E):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<0))return i.json({error:"invalid offset"},400);try{let L=Rc({minOccurrenceCount:y,hasResolved:k,project:g,limit:O,offset:N});return i.json(L)}catch(L){return i.json({error:L.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=h(),g=l.prepare(`SELECT p.name AS project,
2010
2010
  COUNT(s.id) AS total_sessions,
2011
2011
  SUM(CASE WHEN oi.session_id IS NOT NULL THEN 1 ELSE 0 END) AS extracted_sessions,
2012
2012
  MAX(oi.extracted_at) AS last_extracted_at
@@ -2014,20 +2014,20 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2014
2014
  LEFT JOIN sessions s ON s.project_id = p.id
2015
2015
  LEFT JOIN session_output_index oi ON oi.session_id = s.id
2016
2016
  GROUP BY p.id
2017
- ORDER BY total_sessions DESC`).all().map(y=>({project:y.project,total_sessions:y.total_sessions??0,extracted_sessions:y.extracted_sessions??0,remaining_sessions:(y.total_sessions??0)-(y.extracted_sessions??0),last_extracted_at:y.last_extracted_at})),_=g.reduce((y,R)=>(y.total_sessions+=R.total_sessions,y.extracted_sessions+=R.extracted_sessions,y.remaining_sessions+=R.remaining_sessions,y),{total_sessions:0,extracted_sessions:0,remaining_sessions:0}),E=l.prepare("SELECT COUNT(*) AS n FROM bug_pattern_clusters").get();return i.json({projects:g,totals:{..._,cluster_count:E.n}})});let d=j.object({project:j.string().min(1),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:j.number().int().positive().optional(),force:j.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=je(i);if(l)return l;let p=Fi();if(p>0&&p<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(p/1024**3).toFixed(2)} GB free \u2014 extract-outputs needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:p},507);let g=await i.req.json().catch(()=>null),_=d.safeParse(g);if(!_.success)return i.json({error:"invalid request body",details:_.error.format()},400);let E={project:_.data.project,model:_.data.model??mt,limit:_.data.limit??200,force:_.data.force??!1},y=ia();if(E.limit>y.sessionCeiling)return oe({kind:"run-rejected",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,reason:`limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}`}),i.json({error:`requested limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}. Lower the limit or edit launcher.sessionCeiling in ~/.recall/config.json.`},400);let R=Un(E.project);if(!R)return i.json({error:`project "${E.project}" not found`},404);let N=Lt({projectId:R.id,limit:E.limit,force:E.force}).eligible.length,C=aa(N),x=tt(N,E.model),k=Yt(),L=C.estimated_input_tokens_max+C.estimated_output_tokens_max>k.remaining_tokens_24h;if(oe({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:N}),N===0)return i.json({eligible_session_count:0,...C,plan_window_estimate:x,budget:k,would_exceed_budget:!1,preflight_token:null,expires_at:null,message:"No eligible sessions to extract. Pass force=true to re-extract sessions already at the current extractor version."});let{token:F,expiresAt:W}=na(E);return i.json({preflight_token:F,expires_at:new Date(W).toISOString(),eligible_session_count:N,...C,plan_window_estimate:x,budget:k,would_exceed_budget:L})});let m=j.object({preflight_token:j.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=m.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);let _=sa(g.data.preflight_token);if(!_)return oe({kind:"run-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let E=Yt(),R=(()=>{let x=Un(_.project);return x?Lt({projectId:x.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,O=aa(R),N=O.estimated_input_tokens_max+O.estimated_output_tokens_max;if(N>E.remaining_tokens_24h)return oe({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${N} exceeds remaining 24h budget ${E.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:E,projected_spend:N},429);let C=$m({project:_.project,model:_.model,limit:_.limit,force:_.force,origin:i.req.header("origin")??null});return"error"in C?i.json({error:C.error},400):i.json({jobId:C.jobId,reused:C.reused},C.reused?409:200)}),t.get("/api/extract-outputs/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!ua(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of Um(l,g))if(E||(await _.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=ua(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/extract-outputs/jobs/:jobId",i=>{let l=je(i);return l||(Hm(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/extract-outputs/budget",i=>i.json(Yt()));let f=j.object({scope:j.enum(["cluster","project"]),target_id:j.string().min(1),mode:j.enum(["synopsis","priorities","root_cause"]),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let W=Fi();if(W>0&&W<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(W/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:W},507)}let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=f.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _={scope:g.data.scope,target_id:g.data.target_id,mode:g.data.mode,model:g.data.model??Bm},E=Ar(_);if(!E)return oe({kind:"synth-rejected",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:"target not found"}),i.json({error:_.scope==="cluster"?`cluster "${_.target_id}" not found in any extracted findings`:`project "${_.target_id}" has no extracted findings to synthesize`},404);let y=ca({scope:_.scope,mode:_.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),R=y.estimated_input_tokens_max+y.estimated_output_tokens_max,O=Fm(R),N=tt(O,_.model),C=Yt(),k=y.estimated_input_tokens_max+y.estimated_output_tokens_max>C.remaining_tokens_24h;oe({kind:"synth-preflight",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:`${_.scope}/${_.mode}/${_.target_id}`});let{token:L,expiresAt:F}=na({project:_.target_id,model:_.model,limit:1,force:!1});return Hr.set(L,_),setTimeout(()=>Hr.delete(L),9e4).unref?.(),i.json({preflight_token:L,expires_at:new Date(F).toISOString(),estimated_input_tokens_max:y.estimated_input_tokens_max,estimated_output_tokens_max:y.estimated_output_tokens_max,plan_window_estimate:N,budget:C,would_exceed_budget:k,context_summary:E.context_summary})});let b=j.object({preflight_token:j.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=b.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);let _=sa(g.data.preflight_token),E=Hr.get(g.data.preflight_token)??null;if(Hr.delete(g.data.preflight_token),!E||!_)return oe({kind:"synth-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let y=Yt(),R=Ar(E);if(!R)return i.json({error:E.scope==="cluster"?`cluster "${E.target_id}" no longer exists`:`project "${E.target_id}" has no findings`},404);let O=ca({scope:E.scope,mode:E.mode,member_session_count:R.context_summary.session_count,cluster_count:R.context_summary.cluster_count}),N=O.estimated_input_tokens_max+O.estimated_output_tokens_max;if(N>y.remaining_tokens_24h)return oe({kind:"synth-rejected",job_id:null,project:E.scope==="project"?E.target_id:null,model:E.model,limit:null,origin:i.req.header("origin")??null,reason:`projected spend ${N} exceeds remaining 24h budget ${y.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:y,projected_spend:N},429);let C=Jm({intent:E,origin:i.req.header("origin")??null});return"error"in C?i.json({error:C.error},400):i.json({jobId:C.jobId,reused:C.reused},C.reused?409:200)}),t.get("/api/bug-patterns/synthesize/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!da(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of Gm(l,g))if(E||(await _.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=da(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=je(i);return l||(Ym(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/bug-signatures",i=>{let l=i.req.query("project")??null,p=Math.min(Math.max(1,Number(i.req.query("limit")??100)),500),g=h(),_=["oi.bug_signatures IS NOT NULL"],E=[];l&&(_.push("p.name = ?"),E.push(l));let R=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
2017
+ ORDER BY total_sessions DESC`).all().map(y=>({project:y.project,total_sessions:y.total_sessions??0,extracted_sessions:y.extracted_sessions??0,remaining_sessions:(y.total_sessions??0)-(y.extracted_sessions??0),last_extracted_at:y.last_extracted_at})),_=g.reduce((y,k)=>(y.total_sessions+=k.total_sessions,y.extracted_sessions+=k.extracted_sessions,y.remaining_sessions+=k.remaining_sessions,y),{total_sessions:0,extracted_sessions:0,remaining_sessions:0}),E=l.prepare("SELECT COUNT(*) AS n FROM bug_pattern_clusters").get();return i.json({projects:g,totals:{..._,cluster_count:E.n}})});let d=j.object({project:j.string().min(1),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:j.number().int().positive().optional(),force:j.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=je(i);if(l)return l;let p=Wi();if(p>0&&p<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(p/1024**3).toFixed(2)} GB free \u2014 extract-outputs needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:p},507);let g=await i.req.json().catch(()=>null),_=d.safeParse(g);if(!_.success)return i.json({error:"invalid request body",details:_.error.format()},400);let E={project:_.data.project,model:_.data.model??mt,limit:_.data.limit??200,force:_.data.force??!1},y=da();if(E.limit>y.sessionCeiling)return oe({kind:"run-rejected",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,reason:`limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}`}),i.json({error:`requested limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}. Lower the limit or edit launcher.sessionCeiling in ~/.recall/config.json.`},400);let k=Un(E.project);if(!k)return i.json({error:`project "${E.project}" not found`},404);let N=Ct({projectId:k.id,limit:E.limit,force:E.force}).eligible.length,L=pa(N),x=tt(N,E.model),w=Yt(),C=L.estimated_input_tokens_max+L.estimated_output_tokens_max>w.remaining_tokens_24h;if(oe({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:N}),N===0)return i.json({eligible_session_count:0,...L,plan_window_estimate:x,budget:w,would_exceed_budget:!1,preflight_token:null,expires_at:null,message:"No eligible sessions to extract. Pass force=true to re-extract sessions already at the current extractor version."});let{token:F,expiresAt:W}=aa(E);return i.json({preflight_token:F,expires_at:new Date(W).toISOString(),eligible_session_count:N,...L,plan_window_estimate:x,budget:w,would_exceed_budget:C})});let m=j.object({preflight_token:j.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=m.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);let _=ca(g.data.preflight_token);if(!_)return oe({kind:"run-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let E=Yt(),k=(()=>{let x=Un(_.project);return x?Ct({projectId:x.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,O=pa(k),N=O.estimated_input_tokens_max+O.estimated_output_tokens_max;if(N>E.remaining_tokens_24h)return oe({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${N} exceeds remaining 24h budget ${E.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:E,projected_spend:N},429);let L=Jm({project:_.project,model:_.model,limit:_.limit,force:_.force,origin:i.req.header("origin")??null});return"error"in L?i.json({error:L.error},400):i.json({jobId:L.jobId,reused:L.reused},L.reused?409:200)}),t.get("/api/extract-outputs/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!fa(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let k of Gm(l,g))if(E||(await _.writeSSE({id:String(k.id),event:k.kind,data:JSON.stringify(k.data)}),k.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=fa(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/extract-outputs/jobs/:jobId",i=>{let l=je(i);return l||(Ym(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/extract-outputs/budget",i=>i.json(Yt()));let f=j.object({scope:j.enum(["cluster","project"]),target_id:j.string().min(1),mode:j.enum(["synopsis","priorities","root_cause"]),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let W=Wi();if(W>0&&W<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(W/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:W},507)}let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=f.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _={scope:g.data.scope,target_id:g.data.target_id,mode:g.data.mode,model:g.data.model??zm},E=Nr(_);if(!E)return oe({kind:"synth-rejected",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:"target not found"}),i.json({error:_.scope==="cluster"?`cluster "${_.target_id}" not found in any extracted findings`:`project "${_.target_id}" has no extracted findings to synthesize`},404);let y=ma({scope:_.scope,mode:_.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),k=y.estimated_input_tokens_max+y.estimated_output_tokens_max,O=Xm(k),N=tt(O,_.model),L=Yt(),w=y.estimated_input_tokens_max+y.estimated_output_tokens_max>L.remaining_tokens_24h;oe({kind:"synth-preflight",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:`${_.scope}/${_.mode}/${_.target_id}`});let{token:C,expiresAt:F}=aa({project:_.target_id,model:_.model,limit:1,force:!1});return Wr.set(C,_),setTimeout(()=>Wr.delete(C),9e4).unref?.(),i.json({preflight_token:C,expires_at:new Date(F).toISOString(),estimated_input_tokens_max:y.estimated_input_tokens_max,estimated_output_tokens_max:y.estimated_output_tokens_max,plan_window_estimate:N,budget:L,would_exceed_budget:w,context_summary:E.context_summary})});let b=j.object({preflight_token:j.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=b.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);let _=ca(g.data.preflight_token),E=Wr.get(g.data.preflight_token)??null;if(Wr.delete(g.data.preflight_token),!E||!_)return oe({kind:"synth-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let y=Yt(),k=Nr(E);if(!k)return i.json({error:E.scope==="cluster"?`cluster "${E.target_id}" no longer exists`:`project "${E.target_id}" has no findings`},404);let O=ma({scope:E.scope,mode:E.mode,member_session_count:k.context_summary.session_count,cluster_count:k.context_summary.cluster_count}),N=O.estimated_input_tokens_max+O.estimated_output_tokens_max;if(N>y.remaining_tokens_24h)return oe({kind:"synth-rejected",job_id:null,project:E.scope==="project"?E.target_id:null,model:E.model,limit:null,origin:i.req.header("origin")??null,reason:`projected spend ${N} exceeds remaining 24h budget ${y.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:y,projected_spend:N},429);let L=Zm({intent:E,origin:i.req.header("origin")??null});return"error"in L?i.json({error:L.error},400):i.json({jobId:L.jobId,reused:L.reused},L.reused?409:200)}),t.get("/api/bug-patterns/synthesize/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!_a(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let k of eg(l,g))if(E||(await _.writeSSE({id:String(k.id),event:k.kind,data:JSON.stringify(k.data)}),k.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=_a(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=je(i);return l||(tg(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/bug-signatures",i=>{let l=i.req.query("project")??null,p=Math.min(Math.max(1,Number(i.req.query("limit")??100)),500),g=h(),_=["oi.bug_signatures IS NOT NULL"],E=[];l&&(_.push("p.name = ?"),E.push(l));let k=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
2018
2018
  s.started_at, oi.extracted_at, oi.bug_signatures
2019
2019
  FROM session_output_index oi
2020
2020
  JOIN sessions s ON s.id = oi.session_id
2021
2021
  JOIN projects p ON p.id = s.project_id
2022
2022
  WHERE ${_.join(" AND ")}
2023
2023
  ORDER BY oi.extracted_at DESC
2024
- LIMIT ?`).all(...E,p).map(k=>{let L=[];try{let F=JSON.parse(k.bug_signatures);Array.isArray(F)&&(L=F)}catch{L=[]}return{session_id:k.session_id,project:k.project,auto_title:k.auto_title,started_at:k.started_at,extracted_at:k.extracted_at,rawSignatures:L}}),O=R.flatMap(k=>k.rawSignatures.map(L=>L.message_hash).filter(L=>typeof L=="string")),N=Vi(O),C=R.map(k=>({session_id:k.session_id,project:k.project,auto_title:k.auto_title,started_at:k.started_at,extracted_at:k.extracted_at,signatures:k.rawSignatures.map(L=>{let F=L.message_hash?N.get(L.message_hash)??null:null;return{...L,resolved:Qi(F),resolution:F}}),signature_count:k.rawSignatures.length})),x=C.reduce((k,L)=>(k.sessions_total+=1,L.signature_count>0?(k.sessions_with_findings+=1,k.total_findings+=L.signature_count):k.sessions_empty+=1,k),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:C,totals:x})}),t.post("/api/bug-signatures/:hash/resolve",async i=>{let l=i.req.param("hash");if(!l||l.length<4)return i.json({error:"invalid message hash"},400);let p=await i.req.json().catch(()=>({})),g=hm({messageHash:l,resolvedInSessionId:p.resolved_in_session_id??null,fixSummary:p.fix_summary??null});return i.json({resolution:g})}),t.post("/api/bug-signatures/:hash/unresolve",i=>{let l=i.req.param("hash");return!l||l.length<4?i.json({error:"invalid message hash"},400):(Em(l),i.json({ok:!0}))}),t.get("/api/bug-patterns/graph",i=>{let l=i.req.query("project")??null,p=i.req.query("include_resolved")!=="0",g=h(),_=["oi.bug_signatures IS NOT NULL","oi.bug_signatures != '[]'"],E=[];l&&(_.push("p.name = ?"),E.push(l));let y=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
2024
+ LIMIT ?`).all(...E,p).map(w=>{let C=[];try{let F=JSON.parse(w.bug_signatures);Array.isArray(F)&&(C=F)}catch{C=[]}return{session_id:w.session_id,project:w.project,auto_title:w.auto_title,started_at:w.started_at,extracted_at:w.extracted_at,rawSignatures:C}}),O=k.flatMap(w=>w.rawSignatures.map(C=>C.message_hash).filter(C=>typeof C=="string")),N=na(O),L=k.map(w=>({session_id:w.session_id,project:w.project,auto_title:w.auto_title,started_at:w.started_at,extracted_at:w.extracted_at,signatures:w.rawSignatures.map(C=>{let F=C.message_hash?N.get(C.message_hash)??null:null;return{...C,resolved:sa(F),resolution:F}}),signature_count:w.rawSignatures.length})),x=L.reduce((w,C)=>(w.sessions_total+=1,C.signature_count>0?(w.sessions_with_findings+=1,w.total_findings+=C.signature_count):w.sessions_empty+=1,w),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:L,totals:x})}),t.post("/api/bug-signatures/:hash/resolve",async i=>{let l=i.req.param("hash");if(!l||l.length<4)return i.json({error:"invalid message hash"},400);let p=await i.req.json().catch(()=>({})),g=Rm({messageHash:l,resolvedInSessionId:p.resolved_in_session_id??null,fixSummary:p.fix_summary??null});return i.json({resolution:g})}),t.post("/api/bug-signatures/:hash/unresolve",i=>{let l=i.req.param("hash");return!l||l.length<4?i.json({error:"invalid message hash"},400):(km(l),i.json({ok:!0}))}),t.get("/api/bug-patterns/graph",i=>{let l=i.req.query("project")??null,p=i.req.query("include_resolved")!=="0",g=h(),_=["oi.bug_signatures IS NOT NULL","oi.bug_signatures != '[]'"],E=[];l&&(_.push("p.name = ?"),E.push(l));let y=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
2025
2025
  s.started_at, oi.extracted_at, oi.bug_signatures
2026
2026
  FROM session_output_index oi
2027
2027
  JOIN sessions s ON s.id = oi.session_id
2028
2028
  JOIN projects p ON p.id = s.project_id
2029
2029
  WHERE ${_.join(" AND ")}
2030
- ORDER BY oi.extracted_at DESC`).all(...E),R=[];for(let H of y){let q=[];try{let Y=JSON.parse(H.bug_signatures);Array.isArray(Y)&&(q=Y)}catch{continue}for(let Y of q)R.push({sig:Y,session_id:H.session_id,project:H.project,auto_title:H.auto_title})}let O=new Map;for(let H of R){let q=H.sig.message_hash??`nohash:${(H.sig.snippet??"").slice(0,64)}`,Y=O.get(q);Y?Y.push(H):O.set(q,[H])}let N=Array.from(O.keys()).filter(H=>!H.startsWith("nohash:")),C=Vi(N),x=[],k=new Map,L=[];for(let[H,q]of O){let Y=q[0],ee=Y.sig.message_hash??null,V=ee?C.get(ee)??null:null,G=Qi(V);if(!p&&G)continue;let ie=Array.from(new Set(q.map(ge=>ge.project))).sort(),ot=Array.from(new Set(q.map(ge=>ge.session_id))),$e={id:ee??H,message_hash:ee,error_type:Y.sig.error_type??null,snippet:(Y.sig.snippet??"").slice(0,200),file:Y.sig.file??null,occurrence_count:q.length,projects:ie,resolved:G,fix_summary:V?.fix_summary??null,member_session_ids:ot};x.push($e);for(let ge of q)k.has(ge.session_id)||k.set(ge.session_id,{session_id:ge.session_id,project:ge.project,auto_title:ge.auto_title}),L.push({cluster_id:$e.id,session_id:ge.session_id})}let F=[],W=4,K=new Map;function J(H){let q=K.get(H)??0;return q>=W?!1:(K.set(H,q+1),!0)}for(let H=0;H<x.length;H+=1)for(let q=H+1;q<x.length;q+=1){let Y=x[H],ee=x[q],V=null;Y.error_type&&Y.error_type!=="unknown"&&Y.error_type===ee.error_type?V="same_error_type":Y.file&&ee.file&&Y.file===ee.file&&(V="same_file"),V&&(!J(Y.id)||!J(ee.id)||F.push({a:Y.id,b:ee.id,reason:V}))}return i.json({clusters:x,sessions:Array.from(k.values()),member_edges:L,related_edges:F,totals:{cluster_count:x.length,singleton_count:x.filter(H=>H.occurrence_count===1).length,recurring_count:x.filter(H=>H.occurrence_count>1).length,session_count:k.size,resolved_count:x.filter(H=>H.resolved).length}})}),t.get("/api/bug-patterns/:clusterId",i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=Sc(l);return p?i.json(p):i.json({error:`cluster ${l} not found`},404)}),t.post("/api/bug-patterns/:clusterId/resolve",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let g=p.resolved_in_session_id,_=p.fix_summary;if(typeof g!="string"||g.length===0)return i.json({error:"resolved_in_session_id required"},400);if(typeof _!="string"||_.trim().length===0)return i.json({error:"fix_summary required"},400);try{let E=Tc(l,g,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",R=/not found/.test(y)?404:(/not a member/.test(y),400);return i.json({error:y},R)}}),t.post("/api/bug-patterns/:clusterId/split",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let g=p.member_session_ids;if(!Array.isArray(g)||g.length===0)return i.json({error:"member_session_ids must be a non-empty array of strings"},400);let _=[];for(let E of g){if(typeof E!="string"||E.length===0)return i.json({error:"member_session_ids must contain only non-empty strings"},400);_.push(E)}try{let E=yc(l,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",R=/not found/.test(y)?404:(/cannot split|none of the supplied/.test(y),400);return i.json({error:y},R)}}),t.post("/api/links",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.source_session_id,g=l.target_session_id,_=l.link_type;if(typeof p!="string"||p.length===0)return i.json({error:"source_session_id required"},400);if(typeof g!="string"||g.length===0)return i.json({error:"target_session_id required"},400);if(typeof _!="string")return i.json({error:"link_type required"},400);if(!o.has(_))return i.json({error:`invalid link_type: ${_}`},400);if(_!=="wiki_link")return i.json({error:`link_type '${_}' is not user-writable; only wiki_link is exposed via this endpoint. Other types must go through the suggestions-queue review flow.`},403);if(p===g)return i.json({error:"a session cannot link to itself"},400);let E=h();if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(p))return i.json({error:`source session not found: ${p}`},404);if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(g))return i.json({error:`target session not found: ${g}`},404);let O=ms({sourceSessionId:g,targetSessionId:p,linkType:"wiki_link"});if(O.length>0)return i.json({link:O[0]});try{let N=Bc({source_session_id:p,target_session_id:g,link_type:"wiki_link",confidence:1,source:"manual",evidence:l.evidence??{created_via:"context_menu"},approved:!0});return i.json({link:N})}catch(N){return i.json({error:N.message},400)}}),t.delete("/api/links/:id",i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let g=Wc(p);return g.removed===0?i.json({error:`link ${p} not found`},404):i.json(g)}),t.get("/api/sessions/:id/links",i=>{let l=i.req.param("id");if(!l)return i.json({error:"sessionId required"},400);let p=h();if(!p.prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:`session not found: ${l}`},404);let _=i.req.query("type")??"wiki_link";if(!o.has(_))return i.json({error:`invalid type: ${_}`},400);let E=_,y=gn(l).filter(k=>k.link_type===E),R=Gp(l,y);if(R.length===0)return i.json({links:[]});let O=R.map(k=>k.otherSessionId),N=O.map(()=>"?").join(","),C=p.prepare(`SELECT s.id,
2030
+ ORDER BY oi.extracted_at DESC`).all(...E),k=[];for(let B of y){let q=[];try{let Y=JSON.parse(B.bug_signatures);Array.isArray(Y)&&(q=Y)}catch{continue}for(let Y of q)k.push({sig:Y,session_id:B.session_id,project:B.project,auto_title:B.auto_title})}let O=new Map;for(let B of k){let q=B.sig.message_hash??`nohash:${(B.sig.snippet??"").slice(0,64)}`,Y=O.get(q);Y?Y.push(B):O.set(q,[B])}let N=Array.from(O.keys()).filter(B=>!B.startsWith("nohash:")),L=na(N),x=[],w=new Map,C=[];for(let[B,q]of O){let Y=q[0],ee=Y.sig.message_hash??null,V=ee?L.get(ee)??null:null,G=sa(V);if(!p&&G)continue;let ie=Array.from(new Set(q.map(ge=>ge.project))).sort(),ot=Array.from(new Set(q.map(ge=>ge.session_id))),$e={id:ee??B,message_hash:ee,error_type:Y.sig.error_type??null,snippet:(Y.sig.snippet??"").slice(0,200),file:Y.sig.file??null,occurrence_count:q.length,projects:ie,resolved:G,fix_summary:V?.fix_summary??null,member_session_ids:ot};x.push($e);for(let ge of q)w.has(ge.session_id)||w.set(ge.session_id,{session_id:ge.session_id,project:ge.project,auto_title:ge.auto_title}),C.push({cluster_id:$e.id,session_id:ge.session_id})}let F=[],W=4,K=new Map;function J(B){let q=K.get(B)??0;return q>=W?!1:(K.set(B,q+1),!0)}for(let B=0;B<x.length;B+=1)for(let q=B+1;q<x.length;q+=1){let Y=x[B],ee=x[q],V=null;Y.error_type&&Y.error_type!=="unknown"&&Y.error_type===ee.error_type?V="same_error_type":Y.file&&ee.file&&Y.file===ee.file&&(V="same_file"),V&&(!J(Y.id)||!J(ee.id)||F.push({a:Y.id,b:ee.id,reason:V}))}return i.json({clusters:x,sessions:Array.from(w.values()),member_edges:C,related_edges:F,totals:{cluster_count:x.length,singleton_count:x.filter(B=>B.occurrence_count===1).length,recurring_count:x.filter(B=>B.occurrence_count>1).length,session_count:w.size,resolved_count:x.filter(B=>B.resolved).length}})}),t.get("/api/bug-patterns/:clusterId",i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=kc(l);return p?i.json(p):i.json({error:`cluster ${l} not found`},404)}),t.post("/api/bug-patterns/:clusterId/resolve",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let g=p.resolved_in_session_id,_=p.fix_summary;if(typeof g!="string"||g.length===0)return i.json({error:"resolved_in_session_id required"},400);if(typeof _!="string"||_.trim().length===0)return i.json({error:"fix_summary required"},400);try{let E=Ac(l,g,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",k=/not found/.test(y)?404:(/not a member/.test(y),400);return i.json({error:y},k)}}),t.post("/api/bug-patterns/:clusterId/split",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let g=p.member_session_ids;if(!Array.isArray(g)||g.length===0)return i.json({error:"member_session_ids must be a non-empty array of strings"},400);let _=[];for(let E of g){if(typeof E!="string"||E.length===0)return i.json({error:"member_session_ids must contain only non-empty strings"},400);_.push(E)}try{let E=xc(l,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",k=/not found/.test(y)?404:(/cannot split|none of the supplied/.test(y),400);return i.json({error:y},k)}}),t.post("/api/links",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.source_session_id,g=l.target_session_id,_=l.link_type;if(typeof p!="string"||p.length===0)return i.json({error:"source_session_id required"},400);if(typeof g!="string"||g.length===0)return i.json({error:"target_session_id required"},400);if(typeof _!="string")return i.json({error:"link_type required"},400);if(!o.has(_))return i.json({error:`invalid link_type: ${_}`},400);if(_!=="wiki_link")return i.json({error:`link_type '${_}' is not user-writable; only wiki_link is exposed via this endpoint. Other types must go through the suggestions-queue review flow.`},403);if(p===g)return i.json({error:"a session cannot link to itself"},400);let E=h();if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(p))return i.json({error:`source session not found: ${p}`},404);if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(g))return i.json({error:`target session not found: ${g}`},404);let O=ms({sourceSessionId:g,targetSessionId:p,linkType:"wiki_link"});if(O.length>0)return i.json({link:O[0]});try{let N=Gc({source_session_id:p,target_session_id:g,link_type:"wiki_link",confidence:1,source:"manual",evidence:l.evidence??{created_via:"context_menu"},approved:!0});return i.json({link:N})}catch(N){return i.json({error:N.message},400)}}),t.delete("/api/links/:id",i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let g=Yc(p);return g.removed===0?i.json({error:`link ${p} not found`},404):i.json(g)}),t.get("/api/sessions/:id/links",i=>{let l=i.req.param("id");if(!l)return i.json({error:"sessionId required"},400);let p=h();if(!p.prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:`session not found: ${l}`},404);let _=i.req.query("type")??"wiki_link";if(!o.has(_))return i.json({error:`invalid type: ${_}`},400);let E=_,y=gn(l).filter(w=>w.link_type===E),k=em(l,y);if(k.length===0)return i.json({links:[]});let O=k.map(w=>w.otherSessionId),N=O.map(()=>"?").join(","),L=p.prepare(`SELECT s.id,
2031
2031
  NULLIF(sa.alias, '') AS alias,
2032
2032
  s.auto_title,
2033
2033
  s.first_user_message,
@@ -2035,7 +2035,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2035
2035
  FROM sessions s
2036
2036
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2037
2037
  LEFT JOIN projects p ON p.id = s.project_id
2038
- WHERE s.id IN (${N})`).all(...O),x=new Map(C.map(k=>[k.id,k]));return i.json({links:R.map(k=>{let L=x.get(k.otherSessionId),F=L?.alias?.trim()||L?.auto_title?.trim()||(L?.first_user_message?L.first_user_message.slice(0,80):"")||k.otherSessionId.slice(0,8);return{linkId:k.linkId,otherSessionId:k.otherSessionId,direction:k.direction,updatedAt:k.updatedAt,title:F,project:L?.project??null}})})}),t.patch("/api/links/suggestions/:id",async i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let g=await i.req.json().catch(()=>null);if(!g||typeof g.status!="string")return i.json({error:"status required (approved|rejected)"},400);if(g.status!=="approved"&&g.status!=="rejected")return i.json({error:`invalid status: ${g.status}`},400);try{let _=lo(p,g.status);return i.json(_)}catch(_){let E=_.message,y=/already decided/.test(E)?409:/not found/.test(E)?404:400;return i.json({error:E},y)}}),t.post("/api/links/suggestions/bulk-decide",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=Array.isArray(l.ids)?l.ids:null;if(!p||p.length===0)return i.json({error:"ids must be a non-empty array"},400);if(p.length>1e3)return i.json({error:"bulk-decide capped at 1000 ids per call"},400);if(l.status!=="approved"&&l.status!=="rejected")return i.json({error:`invalid status: ${l.status}`},400);let g=l.status,_=0,E=0,y=[];for(let R of p){let O=Number(R);if(!Number.isInteger(O)||O<=0){y.push({id:Number.isFinite(Number(R))?Number(R):-1,error:"invalid id"});continue}try{lo(O,g),_+=1}catch(N){let C=N.message;/already decided/.test(C)?E+=1:y.push({id:O,error:C})}}return i.json({decided:_,skipped:E,errors:y})}),t.get("/api/sessions",i=>{let l=h(),p=i.req.query("project"),g=i.req.query("since"),_=i.req.query("until"),E=i.req.queries("tag")??[],y=i.req.query("collection"),R=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),O=i.req.query("cursor"),N=null;if(O)try{let K=Buffer.from(O,"base64url").toString("utf8"),J=JSON.parse(K);typeof J.ts=="string"&&typeof J.id=="string"&&(N={ts:J.ts,id:J.id})}catch{}let C=i.req.query("system")==="1"||i.req.query("system")==="true",x={limit:R},k="s.message_count > 2"+Br("s",C);if(p&&(k+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",x.proj=`%${Qn(p)}%`),g&&(k+=" AND s.started_at >= @since",x.since=g),_&&(k+=" AND s.started_at <= @until",/^\d{4}-\d{2}-\d{2}$/.test(_)?x.until=`${_}T23:59:59.999Z`:x.until=_),E.length>0&&E.map(J=>ut(J)).filter(Boolean).forEach((J,H)=>{k+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${H})`,x[`tag_${H}`]=J}),y){let K=ri(y);if(K.length===0)return i.json({items:[],nextCursor:null});let J=K.map((H,q)=>`@col_${q}`).join(",");k+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${J}))`,K.forEach((H,q)=>{x[`col_${q}`]=H})}N&&(k+=" AND (COALESCE(s.ended_at, s.started_at, '') < @cursor_ts OR (COALESCE(s.ended_at, s.started_at, '') = @cursor_ts AND s.id < @cursor_id))",x.cursor_ts=N.ts,x.cursor_id=N.id);let L=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
2038
+ WHERE s.id IN (${N})`).all(...O),x=new Map(L.map(w=>[w.id,w]));return i.json({links:k.map(w=>{let C=x.get(w.otherSessionId),F=C?.alias?.trim()||C?.auto_title?.trim()||(C?.first_user_message?C.first_user_message.slice(0,80):"")||w.otherSessionId.slice(0,8);return{linkId:w.linkId,otherSessionId:w.otherSessionId,direction:w.direction,updatedAt:w.updatedAt,title:F,project:C?.project??null}})})}),t.patch("/api/links/suggestions/:id",async i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let g=await i.req.json().catch(()=>null);if(!g||typeof g.status!="string")return i.json({error:"status required (approved|rejected)"},400);if(g.status!=="approved"&&g.status!=="rejected")return i.json({error:`invalid status: ${g.status}`},400);try{let _=po(p,g.status);return i.json(_)}catch(_){let E=_.message,y=/already decided/.test(E)?409:/not found/.test(E)?404:400;return i.json({error:E},y)}}),t.post("/api/links/suggestions/bulk-decide",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=Array.isArray(l.ids)?l.ids:null;if(!p||p.length===0)return i.json({error:"ids must be a non-empty array"},400);if(p.length>1e3)return i.json({error:"bulk-decide capped at 1000 ids per call"},400);if(l.status!=="approved"&&l.status!=="rejected")return i.json({error:`invalid status: ${l.status}`},400);let g=l.status,_=0,E=0,y=[];for(let k of p){let O=Number(k);if(!Number.isInteger(O)||O<=0){y.push({id:Number.isFinite(Number(k))?Number(k):-1,error:"invalid id"});continue}try{po(O,g),_+=1}catch(N){let L=N.message;/already decided/.test(L)?E+=1:y.push({id:O,error:L})}}return i.json({decided:_,skipped:E,errors:y})}),t.get("/api/sessions",i=>{let l=h(),p=i.req.query("project"),g=i.req.query("since"),_=i.req.query("until"),E=i.req.queries("tag")??[],y=i.req.query("collection"),k=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),O=i.req.query("cursor"),N=null;if(O)try{let K=Buffer.from(O,"base64url").toString("utf8"),J=JSON.parse(K);typeof J.ts=="string"&&typeof J.id=="string"&&(N={ts:J.ts,id:J.id})}catch{}let L=i.req.query("system")==="1"||i.req.query("system")==="true",x={limit:k},w="s.message_count > 2"+qr("s",L);if(p&&(w+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",x.proj=`%${Qn(p)}%`),g&&(w+=" AND s.started_at >= @since",x.since=g),_&&(w+=" AND s.started_at <= @until",/^\d{4}-\d{2}-\d{2}$/.test(_)?x.until=`${_}T23:59:59.999Z`:x.until=_),E.length>0&&E.map(J=>ut(J)).filter(Boolean).forEach((J,B)=>{w+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${B})`,x[`tag_${B}`]=J}),y){let K=ai(y);if(K.length===0)return i.json({items:[],nextCursor:null});let J=K.map((B,q)=>`@col_${q}`).join(",");w+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${J}))`,K.forEach((B,q)=>{x[`col_${q}`]=B})}N&&(w+=" AND (COALESCE(s.ended_at, s.started_at, '') < @cursor_ts OR (COALESCE(s.ended_at, s.started_at, '') = @cursor_ts AND s.id < @cursor_id))",x.cursor_ts=N.ts,x.cursor_id=N.id);let C=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
2039
2039
  s.message_count, s.first_user_message, s.git_branch,
2040
2040
  s.auto_title, s.auto_title_source, s.verification_status,
2041
2041
  COALESCE(s.ended_at, s.started_at, '') AS _cursor_ts,
@@ -2054,17 +2054,17 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2054
2054
  JOIN projects p ON p.id = s.project_id
2055
2055
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2056
2056
  LEFT JOIN session_notes sn ON sn.session_id = s.id
2057
- WHERE ${k}
2057
+ WHERE ${w}
2058
2058
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC, s.id DESC
2059
- LIMIT @limit`).all(x),F=L.map(({tags_csv:K,_cursor_ts:J,...H})=>{let q=H.id,Y=M.getOrigin(q),ee=H.alias,V=ee==null?null:M.isSessionAutoLinked(q)?"auto":"manual",G=Rn({auto_title:H.auto_title,auto_title_source:H.auto_title_source??null,has_alias:ee!=null&&V==="manual"});return{...H,tags:K?K.split(","):[],origin:Y?{editor:Y.editor,label:Y.label}:null,alias_source:V,title_quality:G}}),W=null;if(L.length===R&&L.length>0){let K=L[L.length-1],J=JSON.stringify({ts:K._cursor_ts??"",id:K.id});W=Buffer.from(J,"utf8").toString("base64url")}return i.json({items:F,nextCursor:W})}),t.get("/api/sessions/:id",i=>{let l=h(),p=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
2059
+ LIMIT @limit`).all(x),F=C.map(({tags_csv:K,_cursor_ts:J,...B})=>{let q=B.id,Y=M.getOrigin(q),ee=B.alias,V=ee==null?null:M.isSessionAutoLinked(q)?"auto":"manual",G=Rn({auto_title:B.auto_title,auto_title_source:B.auto_title_source??null,has_alias:ee!=null&&V==="manual"});return{...B,tags:K?K.split(","):[],origin:Y?{editor:Y.editor,label:Y.label}:null,alias_source:V,title_quality:G}}),W=null;if(C.length===k&&C.length>0){let K=C[C.length-1],J=JSON.stringify({ts:K._cursor_ts??"",id:K.id});W=Buffer.from(J,"utf8").toString("base64url")}return i.json({items:F,nextCursor:W})}),t.get("/api/sessions/:id",i=>{let l=h(),p=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
2060
2060
  NULLIF(sa.alias, '') AS alias
2061
2061
  FROM sessions s
2062
2062
  JOIN projects p ON p.id = s.project_id
2063
2063
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2064
- WHERE s.id = ?`).get(p);if(!g)return i.json({error:"not found"},404);let _=hn(p),E=M.getOrigin(p),y=E?{editor:E.editor,label:E.label}:null,R=g.alias==null?null:M.isSessionAutoLinked(p)?"auto":"manual",O=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2064
+ WHERE s.id = ?`).get(p);if(!g)return i.json({error:"not found"},404);let _=hn(p),E=M.getOrigin(p),y=E?{editor:E.editor,label:E.label}:null,k=g.alias==null?null:M.isSessionAutoLinked(p)?"auto":"manual",O=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2065
2065
  FROM messages
2066
2066
  WHERE session_id = ?
2067
- ORDER BY COALESCE(timestamp, ''), rowid`).all(p);return i.json({session:{...g,tags:_,origin:y,alias_source:R},messages:O})}),t.get("/api/tags",i=>i.json(kt())),t.get("/api/sessions/:id/tags",i=>i.json({tags:hn(i.req.param("id"))})),t.post("/api/sessions/:id/tags",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.tag!="string")return i.json({error:"tag required"},400);try{let g=Rt(l,p.tag);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/tags/:tag",i=>{let l=i.req.param("id"),p=i.req.param("tag");return i.json(Qc(l,p))}),t.get("/api/config/auto-tag",i=>i.json(zi(qe()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),p=wr.partial().safeParse(l);if(!p.success)return i.json({error:"invalid config",issues:p.error.issues},400);let g=p.data;g.apiKey===void 0&&delete g.apiKey;let _=mm(g);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&Mr(),i.json(zi(_))}),t.get("/api/onboarding",i=>{let p=h().prepare(`SELECT s.id,
2067
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(p);return i.json({session:{...g,tags:_,origin:y,alias_source:k},messages:O})}),t.get("/api/tags",i=>i.json(kt())),t.get("/api/sessions/:id/tags",i=>i.json({tags:hn(i.req.param("id"))})),t.post("/api/sessions/:id/tags",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.tag!="string")return i.json({error:"tag required"},400);try{let g=Rt(l,p.tag);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/tags/:tag",i=>{let l=i.req.param("id"),p=i.req.param("tag");return i.json(sl(l,p))}),t.get("/api/config/auto-tag",i=>i.json(ea(qe()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),p=kr.partial().safeParse(l);if(!p.success)return i.json({error:"invalid config",issues:p.error.issues},400);let g=p.data;g.apiKey===void 0&&delete g.apiKey;let _=Sm(g);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&jr(),i.json(ea(_))}),t.get("/api/onboarding",i=>{let p=h().prepare(`SELECT s.id,
2068
2068
  p.name AS project,
2069
2069
  s.started_at,
2070
2070
  s.ended_at,
@@ -2076,7 +2076,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2076
2076
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2077
2077
  WHERE s.message_count > 2
2078
2078
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
2079
- LIMIT 1`).get();return i.json({state:Pr(),mostRecentSession:p??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),p=jr.partial().safeParse(l);return p.success?i.json(jg(p.data)):i.json({error:"invalid onboarding state",issues:p.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(Pg())),t.get("/api/config/mcp-install",i=>i.json({...Je(),claudeCliAvailable:me()})),t.post("/api/config/mcp-install",i=>i.json({...ba(),claudeCliAvailable:me()})),t.delete("/api/config/mcp-install",i=>i.json({...Lg(),claudeCliAvailable:me()}));let T=j.object({scope:j.object({untaggedOnly:j.boolean().optional(),project:j.string().optional(),collectionId:j.string().optional(),sessionIds:j.array(j.string()).optional(),limit:j.number().int().min(1).max(500).optional()}).default({}),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:j.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(Da)return i.json({error:"a scan is already running"},409);if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!Je().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),g=T.safeParse(p);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=g.data.scope,E=qe(),y=g.data.model??E.model,R=h(),O=()=>R.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,N=O();Da=!0;let C;try{let x=g.data.scanId;C=await bo(_,{model:y,scanId:x});let k=O(),L=Math.max(0,k-N);return x&&_s(x,{type:"done",result:{success:C.success,exitCode:C.exitCode,tagsAdded:L}}),i.json({success:C.success,exitCode:C.exitCode,tagsAdded:L,model:y,stdout:be(C.stdout.slice(0,2e3)).redacted,stderrTail:be(C.stderr.slice(-2e3)).redacted})}finally{Da=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return rt(i,async p=>{let g=[],_={resolve:()=>{}},E=new Promise(N=>{_.resolve=N}),y=tl(l,N=>{g.push(N);let C=_.resolve;E=new Promise(x=>{_.resolve=x}),C()}),R=!1,O=setInterval(()=>{R||p.writeSSE({event:"heartbeat",data:""}).catch(()=>{R=!0})},15e3);try{for(;!R;){g.length===0&&await E;let N=g.shift();if(N&&(await p.writeSSE({event:N.type,data:JSON.stringify(N)}),N.type==="done"))break}}finally{R=!0,clearInterval(O),y()}})}),t.get("/api/prompts",i=>i.json({prompts:_o.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:me()})),t.post("/api/prompts/run",async i=>{if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!Je().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),_=j.object({name:j.string(),args:j.record(j.string(),j.unknown()).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).safeParse(p);if(!_.success)return i.json({error:"invalid request",issues:_.error.issues},400);let E=el(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let y=E.build(_.data.args??{}),R=qe(),O=_.data.model??R.model,N=await dt(y,E.allowedTools,{model:O});return i.json({success:N.success,exitCode:N.exitCode,promptName:E.name,model:O,stdout:N.stdout,stderrTail:N.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(Kn())),t.get("/api/autopilot/events",i=>rt(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(Kn())});let p=[],g=()=>{},_=new Promise(y=>g=y),E=kg(y=>{p.push(y);let R=g;_=new Promise(O=>g=O),R()});try{for(;;){if(p.length===0){let R=new Promise(N=>setTimeout(()=>N("tick"),3e4));if(await Promise.race([_.then(()=>"event"),R])==="tick"){await l.writeSSE({event:"heartbeat",data:"1"});continue}}let y=p.shift();y&&await l.writeSSE({event:"state",data:JSON.stringify(y)})}}finally{E()}})),t.post("/api/autopilot/kick",i=>(Mr(),i.json({ok:!0,snapshot:Kn()})));let S=j.object({scope:j.object({untaggedOnly:j.boolean().optional(),project:j.string().optional(),collectionId:j.string().optional(),sessionIds:j.array(j.string()).optional(),limit:j.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=qe();if(!l.enabled)return i.json({error:"auto-tagging is disabled"},403);if(l.backend!=="api")return i.json({error:"api-backend scan requires backend=api in config"},400);if(!l.apiKey)return i.json({error:"no api key configured"},400);let p=await i.req.json().catch(()=>({})),g=S.safeParse(p);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=xt(g.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=Eg(_.length);return wg(E,{apiKey:l.apiKey,model:l.model,minTags:l.minTagsPerSession,maxTags:l.maxTagsPerSession,sessions:_}),i.json({scanId:E.id,total:E.total})}),t.get("/api/tags/scan/:id",i=>{let l=Or(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let{controller:p,listeners:g,..._}=l;return i.json(_)}),t.get("/api/tags/scan/:id/events",i=>{let l=Or(i.req.param("id"));return l?rt(i,async p=>{await p.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let R of l.results)await p.writeSSE({event:"result",data:JSON.stringify(R)});let g=[],_={resolve:()=>{}},E=new Promise(R=>{_.resolve=R}),y=bg(l,R=>{g.push(R);let O=_.resolve;E=new Promise(N=>{_.resolve=N}),O()});try{for(;l.status==="running"||l.status==="pending";){g.length===0&&await E;let R=g.shift();if(R&&(await p.writeSSE({event:R.type,data:JSON.stringify(R)}),R.type==="done"||R.type==="status"&&(R.status==="cancelled"||R.status==="failed")))break}}finally{y()}}):i.json({error:"scan not found"},404)});let w=j.object({selection:j.array(j.object({sessionId:j.string(),tags:j.array(j.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=Or(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let p=await i.req.json().catch(()=>({})),g=w.safeParse(p);if(!g.success)return i.json({error:"invalid selection"},400);let _=Rg(l,g.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return Sg(l),Tg(l),i.json({ok:!0})}),t.put("/api/sessions/:id/alias",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.alias!="string")return i.json({error:"alias required"},400);try{let g=_e(l,p.alias);if(p.pin===!0)M.unlinkSession(l);else{let _=h().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=_?.cwd?_.cwd.replace(/\/+$/,""):null,y=!1;if(E&&_?.started_at){let R=Date.parse(_.started_at),O=_.started_at,N=M.all().filter(C=>C.cwd&&C.cwd.replace(/\/+$/,"")===E&&Dn({sessionStartedAt:O,terminalOpenedAt:C.opened_at??null}).allowed);if(Number.isFinite(R)&&N.length>0){let x=N.map(k=>({t:k,gap:R-Date.parse(k.opened_at??"")})).filter(k=>Number.isFinite(k.gap)).sort((k,L)=>k.gap-L.gap)[0];x&&(M.linkSession(l,x.t.shell_pid),y=!0)}}y||M.unlinkSession(l)}return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return Is(l),M.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:Se(l)})}),t.get("/api/config/auto-title",i=>i.json(_t())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),p=Ws.partial().safeParse(l);return p.success?i.json(vu(p.data)):i.json({error:"invalid config",issues:p.error.issues},400)}),t.get("/api/sessions/:id/auto-title",i=>{let l=i.req.param("id"),p=ve(l);return p?i.json(p):i.json({error:"session not found"},404)}),t.post("/api/sessions/:id/auto-title",async i=>{let l=i.req.param("id");if(!_t().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!h().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await yu(l);return he(l,E,"agent"),i.json(ve(l))}catch(E){return i.json({error:E.message,code:"agent-title-failed"},500)}}),t.post("/api/sessions/:id/auto-title/revert",i=>{let l=i.req.param("id"),p=ve(l);if(!p)return i.json({error:"session not found"},404);let g=p.auto_title_history;if(!g||g.length===0)return i.json({error:"no prior title to revert to",code:"no-history"},422);let _=g[g.length-1];return he(l,_.title,"agent"),i.json(ve(l))}),t.post("/api/sessions/:id/regenerate-title",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),g=p.model??Rr;try{let _=await Ki(l,{model:g,force:p.force===!0,budget:typeof p.budget=="number"?p.budget:void 0,signal:i.req.raw.signal}),E=ve(l),y=E?.auto_title_history&&E.auto_title_history.length>0?E.auto_title_history[E.auto_title_history.length-1].title:null;return i.json({..._,previous_title:y})}catch(_){if(_ instanceof Jt)return i.json({error:_.message,code:"no-context-available",session_id:_.sessionId},422);let E=_ instanceof Error?_.message:"unknown error",y=/not found|unknown/i.test(E)?404:500;return i.json({error:E,code:"regenerate-failed"},y)}}),t.post("/api/sessions/regenerate-titles-batch",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.project;if(typeof p!="string"||p.length===0)return i.json({error:"project (string) required"},400);let g=l.quality_filter;if(!Array.isArray(g)||g.length===0)return i.json({error:"quality_filter (non-empty array) required"},400);let _=new Set(["low_signal","recursive_meta","programmatic"]),E=[];for(let L of g){if(typeof L!="string")return i.json({error:`invalid quality_filter entry: ${L}`},400);if(!_.has(L))return i.json({error:`quality_filter must be a subset of ${[..._].join(",")}; got ${L}`},400);E.push(L)}let y=typeof l.model=="string"&&l.model.length>0?l.model:Rr,R=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,O=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,C=h().prepare(`SELECT s.id,
2079
+ LIMIT 1`).get();return i.json({state:$r(),mostRecentSession:p??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),p=Fr.partial().safeParse(l);return p.success?i.json(Wg(p.data)):i.json({error:"invalid onboarding state",issues:p.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(qg())),t.get("/api/config/mcp-install",i=>i.json({...Je(),claudeCliAvailable:me()})),t.post("/api/config/mcp-install",i=>i.json({...Ra(),claudeCliAvailable:me()})),t.delete("/api/config/mcp-install",i=>i.json({...Pg(),claudeCliAvailable:me()}));let T=j.object({scope:j.object({untaggedOnly:j.boolean().optional(),project:j.string().optional(),collectionId:j.string().optional(),sessionIds:j.array(j.string()).optional(),limit:j.number().int().min(1).max(500).optional()}).default({}),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:j.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(Ua)return i.json({error:"a scan is already running"},409);if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!Je().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),g=T.safeParse(p);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=g.data.scope,E=qe(),y=g.data.model??E.model,k=h(),O=()=>k.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,N=O();Ua=!0;let L;try{let x=g.data.scanId;L=await To(_,{model:y,scanId:x});let w=O(),C=Math.max(0,w-N);return x&&_s(x,{type:"done",result:{success:L.success,exitCode:L.exitCode,tagsAdded:C}}),i.json({success:L.success,exitCode:L.exitCode,tagsAdded:C,model:y,stdout:be(L.stdout.slice(0,2e3)).redacted,stderrTail:be(L.stderr.slice(-2e3)).redacted})}finally{Ua=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return rt(i,async p=>{let g=[],_={resolve:()=>{}},E=new Promise(N=>{_.resolve=N}),y=il(l,N=>{g.push(N);let L=_.resolve;E=new Promise(x=>{_.resolve=x}),L()}),k=!1,O=setInterval(()=>{k||p.writeSSE({event:"heartbeat",data:""}).catch(()=>{k=!0})},15e3);try{for(;!k;){g.length===0&&await E;let N=g.shift();if(N&&(await p.writeSSE({event:N.type,data:JSON.stringify(N)}),N.type==="done"))break}}finally{k=!0,clearInterval(O),y()}})}),t.get("/api/prompts",i=>i.json({prompts:Eo.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:me()})),t.post("/api/prompts/run",async i=>{if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!Je().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),_=j.object({name:j.string(),args:j.record(j.string(),j.unknown()).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).safeParse(p);if(!_.success)return i.json({error:"invalid request",issues:_.error.issues},400);let E=ol(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let y=E.build(_.data.args??{}),k=qe(),O=_.data.model??k.model,N=await dt(y,E.allowedTools,{model:O});return i.json({success:N.success,exitCode:N.exitCode,promptName:E.name,model:O,stdout:N.stdout,stderrTail:N.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(Kn())),t.get("/api/autopilot/events",i=>rt(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(Kn())});let p=[],g=()=>{},_=new Promise(y=>g=y),E=vg(y=>{p.push(y);let k=g;_=new Promise(O=>g=O),k()});try{for(;;){if(p.length===0){let k=new Promise(N=>setTimeout(()=>N("tick"),3e4));if(await Promise.race([_.then(()=>"event"),k])==="tick"){await l.writeSSE({event:"heartbeat",data:"1"});continue}}let y=p.shift();y&&await l.writeSSE({event:"state",data:JSON.stringify(y)})}}finally{E()}})),t.post("/api/autopilot/kick",i=>(jr(),i.json({ok:!0,snapshot:Kn()})));let S=j.object({scope:j.object({untaggedOnly:j.boolean().optional(),project:j.string().optional(),collectionId:j.string().optional(),sessionIds:j.array(j.string()).optional(),limit:j.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=qe();if(!l.enabled)return i.json({error:"auto-tagging is disabled"},403);if(l.backend!=="api")return i.json({error:"api-backend scan requires backend=api in config"},400);if(!l.apiKey)return i.json({error:"no api key configured"},400);let p=await i.req.json().catch(()=>({})),g=S.safeParse(p);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=xt(g.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=kg(_.length);return Cg(E,{apiKey:l.apiKey,model:l.model,minTags:l.minTagsPerSession,maxTags:l.maxTagsPerSession,sessions:_}),i.json({scanId:E.id,total:E.total})}),t.get("/api/tags/scan/:id",i=>{let l=Lr(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let{controller:p,listeners:g,..._}=l;return i.json(_)}),t.get("/api/tags/scan/:id/events",i=>{let l=Lr(i.req.param("id"));return l?rt(i,async p=>{await p.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let k of l.results)await p.writeSSE({event:"result",data:JSON.stringify(k)});let g=[],_={resolve:()=>{}},E=new Promise(k=>{_.resolve=k}),y=Ag(l,k=>{g.push(k);let O=_.resolve;E=new Promise(N=>{_.resolve=N}),O()});try{for(;l.status==="running"||l.status==="pending";){g.length===0&&await E;let k=g.shift();if(k&&(await p.writeSSE({event:k.type,data:JSON.stringify(k)}),k.type==="done"||k.type==="status"&&(k.status==="cancelled"||k.status==="failed")))break}}finally{y()}}):i.json({error:"scan not found"},404)});let R=j.object({selection:j.array(j.object({sessionId:j.string(),tags:j.array(j.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=Lr(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let p=await i.req.json().catch(()=>({})),g=R.safeParse(p);if(!g.success)return i.json({error:"invalid selection"},400);let _=Lg(l,g.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return xg(l),Ng(l),i.json({ok:!0})}),t.put("/api/sessions/:id/alias",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.alias!="string")return i.json({error:"alias required"},400);try{let g=_e(l,p.alias);if(p.pin===!0)M.unlinkSession(l);else{let _=h().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=_?.cwd?_.cwd.replace(/\/+$/,""):null,y=!1;if(E&&_?.started_at){let k=Date.parse(_.started_at),O=_.started_at,N=M.all().filter(L=>L.cwd&&L.cwd.replace(/\/+$/,"")===E&&Dn({sessionStartedAt:O,terminalOpenedAt:L.opened_at??null}).allowed);if(Number.isFinite(k)&&N.length>0){let x=N.map(w=>({t:w,gap:k-Date.parse(w.opened_at??"")})).filter(w=>Number.isFinite(w.gap)).sort((w,C)=>w.gap-C.gap)[0];x&&(M.linkSession(l,x.t.shell_pid),y=!0)}}y||M.unlinkSession(l)}return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return Ms(l),M.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:Se(l)})}),t.get("/api/config/auto-title",i=>i.json(_t())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),p=qs.partial().safeParse(l);return p.success?i.json(Pu(p.data)):i.json({error:"invalid config",issues:p.error.issues},400)}),t.get("/api/sessions/:id/auto-title",i=>{let l=i.req.param("id"),p=ve(l);return p?i.json(p):i.json({error:"session not found"},404)}),t.post("/api/sessions/:id/auto-title",async i=>{let l=i.req.param("id");if(!_t().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!h().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await xu(l);return he(l,E,"agent"),i.json(ve(l))}catch(E){return i.json({error:E.message,code:"agent-title-failed"},500)}}),t.post("/api/sessions/:id/auto-title/revert",i=>{let l=i.req.param("id"),p=ve(l);if(!p)return i.json({error:"session not found"},404);let g=p.auto_title_history;if(!g||g.length===0)return i.json({error:"no prior title to revert to",code:"no-history"},422);let _=g[g.length-1];return he(l,_.title,"agent"),i.json(ve(l))}),t.post("/api/sessions/:id/regenerate-title",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),g=p.model??Ar;try{let _=await ta(l,{model:g,force:p.force===!0,budget:typeof p.budget=="number"?p.budget:void 0,signal:i.req.raw.signal}),E=ve(l),y=E?.auto_title_history&&E.auto_title_history.length>0?E.auto_title_history[E.auto_title_history.length-1].title:null;return i.json({..._,previous_title:y})}catch(_){if(_ instanceof Jt)return i.json({error:_.message,code:"no-context-available",session_id:_.sessionId},422);let E=_ instanceof Error?_.message:"unknown error",y=/not found|unknown/i.test(E)?404:500;return i.json({error:E,code:"regenerate-failed"},y)}}),t.post("/api/sessions/regenerate-titles-batch",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.project;if(typeof p!="string"||p.length===0)return i.json({error:"project (string) required"},400);let g=l.quality_filter;if(!Array.isArray(g)||g.length===0)return i.json({error:"quality_filter (non-empty array) required"},400);let _=new Set(["low_signal","recursive_meta","programmatic"]),E=[];for(let C of g){if(typeof C!="string")return i.json({error:`invalid quality_filter entry: ${C}`},400);if(!_.has(C))return i.json({error:`quality_filter must be a subset of ${[..._].join(",")}; got ${C}`},400);E.push(C)}let y=typeof l.model=="string"&&l.model.length>0?l.model:Ar,k=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,O=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,L=h().prepare(`SELECT s.id,
2080
2080
  s.auto_title,
2081
2081
  s.auto_title_source,
2082
2082
  NULLIF(sa.alias, '') AS alias
@@ -2085,16 +2085,16 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2085
2085
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2086
2086
  WHERE p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\'
2087
2087
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
2088
- LIMIT @limit`).all({proj:`%${Qn(p)}%`,limit:R}),x=new Set(E),k=C.filter(L=>{let F=L.alias==null?null:M.isSessionAutoLinked(L.id)?"auto":"manual",W=Rn({auto_title:L.auto_title,auto_title_source:L.auto_title_source??null,has_alias:L.alias!=null&&F==="manual"});return x.has(W)});return rt(i,async L=>{let F=k.length,W=[],K=[],J=[],H=0,q=async(ee,V)=>{H+=1;try{await L.writeSSE({id:String(H),event:ee,data:JSON.stringify(V)})}catch{}};await q("start",{total:F,model:y});let Y=0;for(let ee of k){if(i.req.raw.signal.aborted)break;Y+=1;try{let V=await Ki(ee.id,{model:y,budget:O,signal:i.req.raw.signal});V.written?(W.push(ee.id),await q("progress",{sessionId:ee.id,title:V.title,evidence:V.evidence,confidence:V.confidence,current:Y,total:F})):(K.push({sessionId:ee.id,reason:V.skipped??"unknown"}),await q("skipped",{sessionId:ee.id,reason:V.skipped??"unknown",current:Y,total:F}))}catch(V){let G=V instanceof Error?V.message:String(V),ie=V instanceof Jt?"no-context-available":"failed";J.push({sessionId:ee.id,error:G}),await q("error",{sessionId:ee.id,error:G,code:ie,current:Y,total:F})}}await q("done",{generated:W,skipped:K,failed:J,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),p=ur(l);return p?i.json(p):i.body(null,204)}),t.put("/api/sessions/:id/notes",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.content!="string")return i.json({error:"content required (string)"},400);try{let g=dp(l,p.content);return i.json(g)}catch(g){return console.error("[notes] failed to save note for session",l,g),i.json({error:"failed to save note"},500)}}),t.post("/api/sessions/:id/generate-note",async i=>{let l=i.req.param("id");if(!_t().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await pp(l),_=mp(l,g);return i.json(_)}catch(g){let _=g.message,E=/no messages available/i.test(_)?404:500;return i.json({error:_},E)}}),t.get("/api/semantic/status",i=>i.json(yo())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),p=Ss.partial().safeParse(l);return p.success?(Ts(p.data,"api"),i.json(yo())):i.json({error:"invalid semantic config",issues:p.error.issues},400)}),t.get("/api/semantic/config",i=>i.json(ae())),t.post("/api/semantic/backfill",Fe,async i=>{if(Pa)return i.json({error:"a scan is already running"},409);if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let p=await i.req.json().catch(()=>({})),g=Math.max(1,Math.min(5e3,Number(p.limit??200)));return Pa=!0,Rs({limit:g,force:!!p.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{Pa=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",Fe,async i=>{if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let p=i.req.param("id");if(!p)return i.json({error:"session id required"},400);let g=await ws(p);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=le(),p=Me(),g=Ge(),_=(i.req.query("project")??"").trim(),E=_.length>512?"":_,y=null,R=0,O=h();if(E){let q=O.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(E,E);q?y=O.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(q.id).n:y=0}let N=O.prepare(`SELECT COALESCE(SUM(s.message_count), 0) AS n
2088
+ LIMIT @limit`).all({proj:`%${Qn(p)}%`,limit:k}),x=new Set(E),w=L.filter(C=>{let F=C.alias==null?null:M.isSessionAutoLinked(C.id)?"auto":"manual",W=Rn({auto_title:C.auto_title,auto_title_source:C.auto_title_source??null,has_alias:C.alias!=null&&F==="manual"});return x.has(W)});return rt(i,async C=>{let F=w.length,W=[],K=[],J=[],B=0,q=async(ee,V)=>{B+=1;try{await C.writeSSE({id:String(B),event:ee,data:JSON.stringify(V)})}catch{}};await q("start",{total:F,model:y});let Y=0;for(let ee of w){if(i.req.raw.signal.aborted)break;Y+=1;try{let V=await ta(ee.id,{model:y,budget:O,signal:i.req.raw.signal});V.written?(W.push(ee.id),await q("progress",{sessionId:ee.id,title:V.title,evidence:V.evidence,confidence:V.confidence,current:Y,total:F})):(K.push({sessionId:ee.id,reason:V.skipped??"unknown"}),await q("skipped",{sessionId:ee.id,reason:V.skipped??"unknown",current:Y,total:F}))}catch(V){let G=V instanceof Error?V.message:String(V),ie=V instanceof Jt?"no-context-available":"failed";J.push({sessionId:ee.id,error:G}),await q("error",{sessionId:ee.id,error:G,code:ie,current:Y,total:F})}}await q("done",{generated:W,skipped:K,failed:J,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),p=pr(l);return p?i.json(p):i.body(null,204)}),t.put("/api/sessions/:id/notes",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.content!="string")return i.json({error:"content required (string)"},400);try{let g=Ep(l,p.content);return i.json(g)}catch(g){return console.error("[notes] failed to save note for session",l,g),i.json({error:"failed to save note"},500)}}),t.post("/api/sessions/:id/generate-note",async i=>{let l=i.req.param("id");if(!_t().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await bp(l),_=Sp(l,g);return i.json(_)}catch(g){let _=g.message,E=/no messages available/i.test(_)?404:500;return i.json({error:_},E)}}),t.get("/api/semantic/status",i=>i.json(Ro())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),p=Ss.partial().safeParse(l);return p.success?(Ts(p.data,"api"),i.json(Ro())):i.json({error:"invalid semantic config",issues:p.error.issues},400)}),t.get("/api/semantic/config",i=>i.json(ae())),t.post("/api/semantic/backfill",Fe,async i=>{if(Ha)return i.json({error:"a scan is already running"},409);if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let p=await i.req.json().catch(()=>({})),g=Math.max(1,Math.min(5e3,Number(p.limit??200)));return Ha=!0,Rs({limit:g,force:!!p.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{Ha=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",Fe,async i=>{if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let p=i.req.param("id");if(!p)return i.json({error:"session id required"},400);let g=await ws(p);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=le(),p=Me(),g=Ge(),_=(i.req.query("project")??"").trim(),E=_.length>512?"":_,y=null,k=0,O=h();if(E){let q=O.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(E,E);q?y=O.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(q.id).n:y=0}let N=O.prepare(`SELECT COALESCE(SUM(s.message_count), 0) AS n
2089
2089
  FROM chunk_queue cq
2090
- JOIN sessions s ON s.id = cq.session_id`).get(),C=Number(N.n)||0,x=hi(),k=x?.chunksPerSec??0,L=x?.samples??0,F=PA(C),K=k>0?Math.ceil(C/k):0,J=null;if(L>=5&&k>0){let q=k-F.deltaPerSec;q>0&&F.deltaPerSec>0?J=Math.ceil(C/q):(F.deltaPerSec<=0,J=null)}let H="no-samples";return L>=5&&k>0&&(H=F.deltaPerSec>0?"live":"stable"),k>0&&y!==null&&(R=Math.ceil(y/k)),i.json({embedder:l,worker:p,modelInstalled:g,project:E||null,queueDepthForProject:y,etaForProject:R,remainingChunks:C,throughput:{chunksPerSec:k,samples:L,source:x?"local-measured":"no-samples-yet"},etaSeconds:K,etaSecondsDetail:{currentQueue:K,withGrowth:J,source:H},queueGrowthChunksPerSec:F.deltaPerSec})}),t.post("/api/semantic/install",Fe,async i=>{if(Ge())return i.json({ok:!0,status:"already_installed"});if(ja)return i.json({error:"a scan is already running"},409);ja=!0;try{return await of(),await Be(),In(),i.json({ok:!0,status:"installed"})}catch(l){let p=l instanceof Error?l.message:"unknown error";return i.json({ok:!1,error:p},500)}finally{ja=!1}}),t.get("/api/semantic/reindex-preview",Fe,async i=>{let l=(i.req.query("project")??"").trim();if(!l)return i.json({error:"project name required"},400);let p=h(),g=p.prepare("SELECT id, name FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(l,l);if(!g)return i.json({error:`project not found: ${l}`},404);let _=p.prepare(`SELECT
2090
+ JOIN sessions s ON s.id = cq.session_id`).get(),L=Number(N.n)||0,x=Si(),w=x?.chunksPerSec??0,C=x?.samples??0,F=JA(L),K=w>0?Math.ceil(L/w):0,J=null;if(C>=5&&w>0){let q=w-F.deltaPerSec;q>0&&F.deltaPerSec>0?J=Math.ceil(L/q):(F.deltaPerSec<=0,J=null)}let B="no-samples";return C>=5&&w>0&&(B=F.deltaPerSec>0?"live":"stable"),w>0&&y!==null&&(k=Math.ceil(y/w)),i.json({embedder:l,worker:p,modelInstalled:g,project:E||null,queueDepthForProject:y,etaForProject:k,remainingChunks:L,throughput:{chunksPerSec:w,samples:C,source:x?"local-measured":"no-samples-yet"},etaSeconds:K,etaSecondsDetail:{currentQueue:K,withGrowth:J,source:B},queueGrowthChunksPerSec:F.deltaPerSec})}),t.post("/api/semantic/install",Fe,async i=>{if(Ge())return i.json({ok:!0,status:"already_installed"});if(Ba)return i.json({error:"a scan is already running"},409);Ba=!0;try{return await mf(),await He(),In(),i.json({ok:!0,status:"installed"})}catch(l){let p=l instanceof Error?l.message:"unknown error";return i.json({ok:!1,error:p},500)}finally{Ba=!1}}),t.get("/api/semantic/reindex-preview",Fe,async i=>{let l=(i.req.query("project")??"").trim();if(!l)return i.json({error:"project name required"},400);let p=h(),g=p.prepare("SELECT id, name FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(l,l);if(!g)return i.json({error:`project not found: ${l}`},404);let _=p.prepare(`SELECT
2091
2091
  COUNT(*) AS total,
2092
2092
  SUM(CASE WHEN s.message_count >= 3 THEN 1 ELSE 0 END) AS eligible,
2093
2093
  SUM(CASE WHEN s.message_count >= 3 AND EXISTS
2094
2094
  (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id) THEN 1 ELSE 0 END)
2095
2095
  AS indexed
2096
- FROM sessions s WHERE s.project_id = ?`).get(g.id),E=_.eligible??0,y=_.indexed??0,R=Math.max(0,E-y),O=p.prepare(`SELECT COUNT(*) AS n FROM chunk_queue
2097
- WHERE session_id NOT IN (SELECT id FROM sessions WHERE project_id = ?)`).get(g.id).n,N=hi(),C=2,x=30,k=C,L=x,F=C*x,W="fallback-baseline",K=0;if(N)k=N.sessionsPerSec,L=N.avgChunksPerSession,F=N.chunksPerSec,W="local-measured",K=N.samples;else if(Ge()){if(!le().loaded)try{await Be()}catch{}try{let Y=["[user] benchmark probe one \u2014 typical session opening turn","[assistant] benchmark probe two \u2014 typical assistant response with code reference","[user] benchmark probe three \u2014 typical follow-up clarification"],ee=Date.now();await Ve(Y);let V=Date.now()-ee;V>0&&(F=Y.length*1e3/V,k=F/x,W="live-benchmark")}catch{}}let J=Y=>p.prepare(`SELECT
2096
+ FROM sessions s WHERE s.project_id = ?`).get(g.id),E=_.eligible??0,y=_.indexed??0,k=Math.max(0,E-y),O=p.prepare(`SELECT COUNT(*) AS n FROM chunk_queue
2097
+ WHERE session_id NOT IN (SELECT id FROM sessions WHERE project_id = ?)`).get(g.id).n,N=Si(),L=2,x=30,w=L,C=x,F=L*x,W="fallback-baseline",K=0;if(N)w=N.sessionsPerSec,C=N.avgChunksPerSession,F=N.chunksPerSec,W="local-measured",K=N.samples;else if(Ge()){if(!le().loaded)try{await He()}catch{}try{let Y=["[user] benchmark probe one \u2014 typical session opening turn","[assistant] benchmark probe two \u2014 typical assistant response with code reference","[user] benchmark probe three \u2014 typical follow-up clarification"],ee=Date.now();await Ve(Y);let V=Date.now()-ee;V>0&&(F=Y.length*1e3/V,w=F/x,W="live-benchmark")}catch{}}let J=Y=>p.prepare(`SELECT
2098
2098
  COALESCE(SUM(CASE WHEN ec > 5 THEN 5 ELSE ec END), 0) AS total_quick,
2099
2099
  COALESCE(SUM(CASE WHEN ec > 80 THEN 80 ELSE ec END), 0) AS total_standard,
2100
2100
  COALESCE(SUM(CASE WHEN ec > 200 THEN 200 ELSE ec END), 0) AS total_full,
@@ -2107,9 +2107,9 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2107
2107
  END AS ec
2108
2108
  FROM sessions s
2109
2109
  WHERE s.project_id = ? AND s.message_count >= 3 ${Y}
2110
- )`).get(g.id),H=J("AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)"),q=J("");return i.json({project:g.name,total:_.total??0,eligible:E,indexed:y,pendingNew:R,pendingForce:E,modelInstalled:Ge(),modelName:"BAAI/bge-base-en-v1.5",embedderLoaded:le().loaded,workerRunning:Me().running,queueDepthOther:O,sessionsPerSec:k,avgChunksPerSession:L,chunksPerSec:F,throughputSource:W,throughputSamples:K,estimatedChunksByDepth:{new:{quick:H.total_quick,standard:H.total_standard,full:H.total_full,uncapped:H.total_uncapped},force:{quick:q.total_quick,standard:q.total_standard,full:q.total_full,uncapped:q.total_uncapped}}})}),t.post("/api/semantic/cancel-reindex",Fe,async i=>{let l={};try{l=await i.req.json()}catch{}let p=(l.project??"").trim(),g=h(),_=0,E=null;if(p){let y=g.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(p,p);if(!y)return i.json({error:`project not found: ${p}`},404);let R=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(y.id).n;g.prepare("DELETE FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").run(y.id),_=R,E=y.id}else{let y=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n;g.prepare("DELETE FROM chunk_queue").run(),_=y}return E!==null?pd(E):md(),Me().queueDepth===0&&fd(),i.json({cleared:_,project:p||null,queueDepth:Me().queueDepth})}),t.post("/api/semantic/reindex-project",Fe,async i=>{if(!Ge())return i.json({error:"embedder not installed \u2014 run `recall semantic install` first"},503);let l={};try{l=await i.req.json()}catch{}let p=(l.project??"").trim();if(!p)return i.json({error:"project name required"},400);let g=!!l.force,_=Math.max(0,Math.floor(Number(l.maxChunks??0)));ud(_);let E=h(),y=E.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(p,p);if(!y)return i.json({error:`project not found: ${p}`},404);let R=g?"SELECT id FROM sessions WHERE project_id = ? AND message_count >= 3":`SELECT s.id FROM sessions s
2110
+ )`).get(g.id),B=J("AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)"),q=J("");return i.json({project:g.name,total:_.total??0,eligible:E,indexed:y,pendingNew:k,pendingForce:E,modelInstalled:Ge(),modelName:"BAAI/bge-base-en-v1.5",embedderLoaded:le().loaded,workerRunning:Me().running,queueDepthOther:O,sessionsPerSec:w,avgChunksPerSession:C,chunksPerSec:F,throughputSource:W,throughputSamples:K,estimatedChunksByDepth:{new:{quick:B.total_quick,standard:B.total_standard,full:B.total_full,uncapped:B.total_uncapped},force:{quick:q.total_quick,standard:q.total_standard,full:q.total_full,uncapped:q.total_uncapped}}})}),t.post("/api/semantic/cancel-reindex",Fe,async i=>{let l={};try{l=await i.req.json()}catch{}let p=(l.project??"").trim(),g=h(),_=0,E=null;if(p){let y=g.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(p,p);if(!y)return i.json({error:`project not found: ${p}`},404);let k=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(y.id).n;g.prepare("DELETE FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").run(y.id),_=k,E=y.id}else{let y=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n;g.prepare("DELETE FROM chunk_queue").run(),_=y}return E!==null?hd(E):Ed(),Me().queueDepth===0&&Sd(),i.json({cleared:_,project:p||null,queueDepth:Me().queueDepth})}),t.post("/api/semantic/reindex-project",Fe,async i=>{if(!Ge())return i.json({error:"embedder not installed \u2014 run `recall semantic install` first"},503);let l={};try{l=await i.req.json()}catch{}let p=(l.project??"").trim();if(!p)return i.json({error:"project name required"},400);let g=!!l.force,_=Math.max(0,Math.floor(Number(l.maxChunks??0)));fd(_);let E=h(),y=E.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(p,p);if(!y)return i.json({error:`project not found: ${p}`},404);let k=g?"SELECT id FROM sessions WHERE project_id = ? AND message_count >= 3":`SELECT s.id FROM sessions s
2111
2111
  WHERE s.project_id = ? AND s.message_count >= 3
2112
- AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)`,O=E.prepare(R).all(y.id);if(O.length===0)return i.json({enqueued:0,queueDepth:Me().queueDepth,message:"nothing to do \u2014 every session in this project is already vectorized (use force:true to re-embed)"});let N=E.prepare("INSERT INTO chunk_queue(session_id, action) VALUES (?, 'embed')");if(E.transaction(()=>{for(let x of O)N.run(x.id)})(),!le().loaded)try{await Be()}catch(x){let k=x instanceof Error?x.message:"unknown error";return i.json({error:`embedder load failed: ${k}`},500)}return Me().running||In(),i.json({enqueued:O.length,queueDepth:Me().queueDepth,project:p,appliedMaxChunks:_})}),t.get("/api/sessions/:id/similar",Fe,async i=>{if(!le().loaded)return i.json({error:"vector model not loaded"},503);let l=i.req.param("id"),p=Math.max(1,Math.min(50,Number(i.req.query("limit")??10)));try{let g=await Qg(l,p);return i.json({sessionId:l,similar:g})}catch(g){let _=g instanceof Error?g.message:"unknown error";return i.json({error:_},500)}}),t.get("/api/search",Fe,async i=>{let l=h(),p=i.req.query("q")?.trim();if(!p)return i.json({query:"",hits:[],tags:[]});if(p.length>500)return i.json({error:"query too long (max 500 chars)"},400);let g=i.req.query("project"),_=p.split(/\s+/).filter(G=>G.length>0),E=_.filter(G=>G.startsWith("#")).map(G=>ut(G)).filter(Boolean),y=_.filter(G=>!G.startsWith("#")),R=y.length>20,N=(R?y.slice(0,20):y).map(G=>`"${G.replace(/"/g,"")}"`),C=N.join(" "),x=Math.max(1,Math.min(200,Number(i.req.query("limit")??30))),k=i.req.query("system")==="1"||i.req.query("system")==="true",L=Br("s",k);if(N.length===0&&E.length>0){let G=`
2112
+ AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)`,O=E.prepare(k).all(y.id);if(O.length===0)return i.json({enqueued:0,queueDepth:Me().queueDepth,message:"nothing to do \u2014 every session in this project is already vectorized (use force:true to re-embed)"});let N=E.prepare("INSERT INTO chunk_queue(session_id, action) VALUES (?, 'embed')");if(E.transaction(()=>{for(let x of O)N.run(x.id)})(),!le().loaded)try{await He()}catch(x){let w=x instanceof Error?x.message:"unknown error";return i.json({error:`embedder load failed: ${w}`},500)}return Me().running||In(),i.json({enqueued:O.length,queueDepth:Me().queueDepth,project:p,appliedMaxChunks:_})}),t.get("/api/sessions/:id/similar",Fe,async i=>{if(!le().loaded)return i.json({error:"vector model not loaded"},503);let l=i.req.param("id"),p=Math.max(1,Math.min(50,Number(i.req.query("limit")??10)));try{let g=await of(l,p);return i.json({sessionId:l,similar:g})}catch(g){let _=g instanceof Error?g.message:"unknown error";return i.json({error:_},500)}}),t.get("/api/search",Fe,async i=>{let l=h(),p=i.req.query("q")?.trim();if(!p)return i.json({query:"",hits:[],tags:[]});if(p.length>500)return i.json({error:"query too long (max 500 chars)"},400);let g=i.req.query("project"),_=p.split(/\s+/).filter(G=>G.length>0),E=_.filter(G=>G.startsWith("#")).map(G=>ut(G)).filter(Boolean),y=_.filter(G=>!G.startsWith("#")),k=y.length>20,N=(k?y.slice(0,20):y).map(G=>`"${G.replace(/"/g,"")}"`),L=N.join(" "),x=Math.max(1,Math.min(200,Number(i.req.query("limit")??30))),w=i.req.query("system")==="1"||i.req.query("system")==="true",C=qr("s",w);if(N.length===0&&E.length>0){let G=`
2113
2113
  SELECT s.id AS session_id,
2114
2114
  s.id AS message_uuid,
2115
2115
  p.name AS project,
@@ -2121,8 +2121,8 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2121
2121
  FROM sessions s
2122
2122
  JOIN projects p ON p.id = s.project_id
2123
2123
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2124
- WHERE 1=1${L}
2125
- `,ie={limit:x};g&&(G+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Qn(g)}%`),E.forEach(($e,ge)=>{G+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ge})`,ie[`tag_${ge}`]=$e}),G+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let ot=l.prepare(G).all(ie);return i.json({query:p,hits:ot,tags:E,truncated:R})}let F=`
2124
+ WHERE 1=1${C}
2125
+ `,ie={limit:x};g&&(G+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Qn(g)}%`),E.forEach(($e,ge)=>{G+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ge})`,ie[`tag_${ge}`]=$e}),G+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let ot=l.prepare(G).all(ie);return i.json({query:p,hits:ot,tags:E,truncated:k})}let F=`
2126
2126
  SELECT m.session_id AS session_id,
2127
2127
  m.uuid AS message_uuid,
2128
2128
  p.name AS project,
@@ -2136,8 +2136,8 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2136
2136
  JOIN sessions s ON s.id = m.session_id
2137
2137
  JOIN projects p ON p.id = s.project_id
2138
2138
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2139
- WHERE messages_fts MATCH @fts${L}
2140
- `,W={fts:C,limit:x};g&&(F+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",W.proj=`%${Qn(g)}%`),E.forEach((G,ie)=>{F+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ie})`,W[`tag_${ie}`]=G}),F+=" ORDER BY bm25(messages_fts) LIMIT @limit";let J=l.prepare(F).all(W).map(G=>({...G,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:p,hits:J,tags:E,truncated:R});let q=[];try{let G=`
2139
+ WHERE messages_fts MATCH @fts${C}
2140
+ `,W={fts:L,limit:x};g&&(F+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",W.proj=`%${Qn(g)}%`),E.forEach((G,ie)=>{F+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ie})`,W[`tag_${ie}`]=G}),F+=" ORDER BY bm25(messages_fts) LIMIT @limit";let J=l.prepare(F).all(W).map(G=>({...G,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:p,hits:J,tags:E,truncated:k});let q=[];try{let G=`
2141
2141
  SELECT s.id AS session_id,
2142
2142
  s.id AS message_uuid,
2143
2143
  p.name AS project,
@@ -2152,16 +2152,16 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2152
2152
  JOIN sessions s ON s.id = ss.session_id
2153
2153
  JOIN projects p ON p.id = s.project_id
2154
2154
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2155
- WHERE sessions_fts MATCH @fts${L}
2156
- `,ie={fts:C,limit:x};g&&(G+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Qn(g)}%`),E.forEach((ot,$e)=>{G+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${$e})`,ie[`tag_${$e}`]=ot}),G+=" ORDER BY rank LIMIT @limit",q=l.prepare(G).all(ie)}catch(G){console.error("[search.semantic] failed:",G)}if(le().loaded)try{let G=await Vg(p,x),ie=J.map(de=>({id:String(de.session_id),data:de,lane:"bm25"})),ot=q.map(de=>({id:String(de.session_id),data:de,lane:"summary"})),$e=G.map(de=>({id:de.sessionId,data:{session_id:de.sessionId,snippet:de.text,matched_via:"vector"},lane:"vector"})),Cf=Zg([ie,ot,$e]).slice(0,x).map(de=>({...de.data,session_id:de.id,rrf_score:de.score,lanes:de.lanes,matched_via:de.lanes.length>1?"fused":de.lanes[0]}));return i.json({query:p,hits:Cf,tags:E,mode:"semantic",fusion:"rrf",truncated:R})}catch(G){console.error("[search.vector] failed, falling back:",G)}let Y=new Set(J.map(G=>String(G.session_id))),ee=q.filter(G=>!Y.has(String(G.session_id))).map(({rank:G,...ie})=>({...ie,matched_via:"semantic"})),V=[...J,...ee].slice(0,x);return i.json({query:p,hits:V,tags:E,mode:"semantic",truncated:R})}),t.get("/api/sessions/:id/context",Fe,i=>{let l=h(),p=i.req.param("id"),g=i.req.query("mode")==="full"?"full":"condensed",_=i.req.query("subagents")==="1",E=i.req.query("prelude")??null,y=l.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
2155
+ WHERE sessions_fts MATCH @fts${C}
2156
+ `,ie={fts:L,limit:x};g&&(G+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Qn(g)}%`),E.forEach((ot,$e)=>{G+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${$e})`,ie[`tag_${$e}`]=ot}),G+=" ORDER BY rank LIMIT @limit",q=l.prepare(G).all(ie)}catch(G){console.error("[search.semantic] failed:",G)}if(le().loaded)try{let G=await rf(p,x),ie=J.map(de=>({id:String(de.session_id),data:de,lane:"bm25"})),ot=q.map(de=>({id:String(de.session_id),data:de,lane:"summary"})),$e=G.map(de=>({id:de.sessionId,data:{session_id:de.sessionId,snippet:de.text,matched_via:"vector"},lane:"vector"})),Ff=af([ie,ot,$e]).slice(0,x).map(de=>({...de.data,session_id:de.id,rrf_score:de.score,lanes:de.lanes,matched_via:de.lanes.length>1?"fused":de.lanes[0]}));return i.json({query:p,hits:Ff,tags:E,mode:"semantic",fusion:"rrf",truncated:k})}catch(G){console.error("[search.vector] failed, falling back:",G)}let Y=new Set(J.map(G=>String(G.session_id))),ee=q.filter(G=>!Y.has(String(G.session_id))).map(({rank:G,...ie})=>({...ie,matched_via:"semantic"})),V=[...J,...ee].slice(0,x);return i.json({query:p,hits:V,tags:E,mode:"semantic",truncated:k})}),t.get("/api/sessions/:id/context",Fe,i=>{let l=h(),p=i.req.param("id"),g=i.req.query("mode")==="full"?"full":"condensed",_=i.req.query("subagents")==="1",E=i.req.query("prelude")??null,y=l.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
2157
2157
  s.started_at, s.ended_at, s.message_count, s.git_branch
2158
2158
  FROM sessions s JOIN projects p ON p.id = s.project_id
2159
- WHERE s.id = ?`).get(p);if(!y)return i.json({error:"not found"},404);let R=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2159
+ WHERE s.id = ?`).get(p);if(!y)return i.json({error:"not found"},404);let k=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2160
2160
  FROM messages
2161
2161
  WHERE session_id = ?
2162
- ORDER BY COALESCE(timestamp, ''), rowid`).all(p),O=ip(y,R,{mode:g,includeSidechain:_,prelude:E});return i.text(O)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:Mu(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),p=Qe(l);if(!p)return i.json({error:"not found"},404);let g=Du(l,!0);return i.json({collection:p,members:g})}),t.post("/api/collections",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=An({name:l.name,description:l.description??null,icon:l.icon??null,color:l.color??null,parent_id:l.parent_id??null,sort_key:l.sort_key});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/collections/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let g=Pu(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.post("/api/collections/:id/archive",i=>{let l=i.req.param("id");try{let p=Fu(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/restore",i=>{let l=i.req.param("id");try{let p=$u(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/members",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.session_id!="string")return i.json({error:"session_id required"},400);try{let g=xn(l,p.session_id,p.note??null);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/collections/:id/members/:sid",i=>{let l=i.req.param("id"),p=i.req.param("sid");try{let g=Uu(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/collections",i=>{let l=i.req.param("id");return i.json({collections:ju(l)})});let A=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:Xu()})),t.post("/api/auto-collections/rules",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string"||typeof l.pattern!="string"||!l.type||!A.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let p=ui({name:l.name,type:l.type,pattern:l.pattern,collection_id:l.collection_id,parent_collection_id:l.parent_collection_id,priority:l.priority,enabled:l.enabled});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/auto-collections/rules/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let g=Ju(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/auto-collections/rules/:id",i=>{let l=i.req.param("id");try{let p=Gu(l);return i.json(p)}catch(p){return i.json({error:p.message},400)}}),t.get("/api/auto-collections/suggestions",i=>{let l=i.req.query("dismissed")==="1";return i.json({suggestions:Vs({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let p=zu(l);return i.json({rule:p})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/suggestions/:id/dismiss",i=>{let l=i.req.param("id");try{return Yu(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=Qs();return i.json({suggestions:l})}),t.get("/api/auto-collections/suggestions/:id/preview",i=>{let l=i.req.param("id"),p=Math.max(1,Math.min(20,Number(i.req.query("limit"))||3)),_=Vs({includeDismissed:!1}).find(y=>y.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=Wu(_.type,_.pattern,p);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(Ku());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Wi({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),p=ce(l);if(!p)return i.json({error:"thread not found"},404);let g=p.edges.map(_=>({..._,alias_source:_.alias==null?null:M.isSessionAutoLinked(_.session_id)?"auto":"manual"}));return i.json({thread:{...p,edges:g}})}),t.post("/api/threads",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name)return i.json({error:"name required"},400);try{let p=pr({name:l.name,summary:l.summary??null,originSessionId:l.originSessionId});return i.json({thread:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/threads/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{p.name&&wp(l,p.name),p.close&&Rp(l),p.reopen&&kp(l),p.archive&&Ap(l),"folder_id"in p&&Jp(l,p.folder_id??null);let g=ce(l);return g?i.json({thread:g}):i.json({error:"thread not found"},404)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/thread-folders",i=>i.json({folders:Gi()})),t.post("/api/thread-folders",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=Up({name:l.name,parentFolderId:l.parent_folder_id??null,projectScope:l.project_scope??null});return i.json({folder:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/thread-folders/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{let g;return p.name&&(g=Bp(l,p.name)),"parent_folder_id"in p&&(g=Wp(l,p.parent_folder_id??null)),g?i.json({folder:g}):i.json({error:"no patch fields"},400)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/thread-folders/:id",i=>{let l=i.req.param("id");try{return Xp(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/thread-folders/reorder",async i=>{let l=await i.req.json().catch(()=>({})),p=l.ordered_ids;if(!Array.isArray(p))return i.json({error:"ordered_ids must be an array"},400);try{return qp(l.parent_folder_id??null,l.project_scope??null,p),i.json({ok:!0})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/sessions",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionId)return i.json({error:"sessionId required"},400);try{let g=mr({threadId:l,sessionId:p.sessionId,parentSessionId:p.parentSessionId??null,role:p.role});return i.json({edge:g})}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/threads/:id/sessions/:sessionId",i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),g=yp(l,p);return i.json(g)}),t.patch("/api/threads/:id/sessions/:sessionId",async i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),g=await i.req.json().catch(()=>({}));try{let _=jn(l,p,g.parentSessionId??null);return i.json({edge:_})}catch(_){return i.json({error:_.message},400)}}),t.post("/api/threads/:id/merge",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sourceId)return i.json({error:"sourceId required"},400);try{let g=xp(p.sourceId,l);return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/split",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionIds?.length||!p.newThreadName)return i.json({error:"sessionIds and newThreadName required"},400);try{let g=Np({threadId:l,sessionIds:p.sessionIds,newThreadName:p.newThreadName});return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/threads",i=>{let l=i.req.param("id");return i.json({threads:Tp(l)})});let v=j.object({enabled:j.boolean(),band_lo:j.number().min(0).max(1).optional(),band_hi:j.number().min(0).max(1).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),D=j.object({project:j.string().min(1),threshold:j.number().min(0).max(1).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:v});t.post("/api/threads/scan/preflight",async i=>{let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=D.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=mg({project:g.data.project,threshold:g.data.threshold,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json(_)});let X=j.object({project:j.string().min(1),threshold:j.number().min(0).max(1).optional(),llm_names:j.boolean().optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:v});t.post("/api/threads/scan/apply",async i=>{let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=X.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=fg({project:g.data.project,threshold:g.data.threshold,llm_names:g.data.llm_names,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json({jobId:_.jobId,reused:_.reused},_.reused?409:200)}),t.get("/api/threads/scan/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!_a(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of _g(l,g))if(E||(await _.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=_a(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/threads/scan/jobs/:jobId",i=>{let l=je(i);return l||(hg(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let I=j.object({project_id:j.number().int().positive(),mode:j.enum(["preflight","apply"]),window_hours:j.number().min(.5).max(168).optional(),score_threshold:j.number().min(0).max(1).optional(),use_live_pids:j.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),p=I.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=await Pp(p.data.project_id,{windowHours:p.data.window_hours,scoreThreshold:p.data.score_threshold,useLivePids:p.data.use_live_pids});if(p.data.mode==="preflight")return i.json({plan:g});let _=Fp(g);return i.json({plan:g,result:_})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/threads/:id/titles/preflight",i=>{let l=i.req.param("id"),p=ce(l);if(!p)return i.json({error:"thread not found"},404);let g=h(),_=0;for(let E of p.edges)g.prepare("SELECT auto_title_source FROM sessions WHERE id = ?").get(E.session_id)?.auto_title_source==="agent"&&(_+=1);return i.json({total:p.edges.length,alreadyTitled:_,untitled:p.edges.length-_})}),t.post("/api/threads/:id/titles/generate",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),g=ce(l);if(!g)return i.json({error:"thread not found"},404);if(g.edges.length===0)return i.json({error:"thread has no sessions"},400);let _=qe(),E=p.model??_.model,y=om({threadId:l,force:p.force??!1,model:E});return i.json({jobId:y})}),t.get("/api/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Yi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of im(l,g))if(E||(await _.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/jobs/:jobId",i=>{let l=Yi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>am(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404)),t.post("/api/terminal/opened",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=M.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance",count:M.size()});let g=M.upsert({shell_pid:l.shell_pid,tab_name:l.tab_name,cwd:l.cwd??null,opened_at:l.opened_at??new Date().toISOString()});return i.json({ok:!0,ownership:p,count:M.size(),entry:g})}),t.post("/api/terminal/renamed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=M.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance"});let g=M.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let _=gf(l.shell_pid,l.tab_name);return i.json({ok:!0,ownership:p,entry:g,propagated:_})}),t.post("/api/terminal/closed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number")return i.json({error:"shell_pid required"},400);let p=M.remove(l.shell_pid);return i.json({ok:!0,removed:p,count:M.size()})}),t.post("/api/terminal/claude-started",async i=>{let l=await i.req.json().catch(()=>null);return!l||typeof l.shell_pid!="number"?i.json({error:"shell_pid required"},400):(M.pushPending({shell_pid:l.shell_pid,tab_name:typeof l.tab_name=="string"?l.tab_name:"",cwd:typeof l.cwd=="string"?l.cwd:null,started_at:typeof l.started_at=="string"?l.started_at:new Date().toISOString()}),i.json({ok:!0,pending:M.pendingSize()}))}),t.post("/api/terminal/output",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.text!="string")return i.json({error:"shell_pid and text required"},400);let p=l.text.length>8192?l.text.slice(-8192):l.text,g=typeof l.captured_at=="string"?l.captured_at:new Date().toISOString();return M.setOutputTail(l.shell_pid,p,g),i.json({ok:!0})}),t.post("/api/terminal/sync",async i=>{let l=await i.req.json().catch(()=>null);if(!l||!Array.isArray(l.terminals))return i.json({error:"terminals array required"},400);let p=new Map;for(let L of M.all())p.set(L.shell_pid,L.tab_name);let g=l.terminals.filter(L=>!!L&&typeof L.shell_pid=="number"&&typeof L.tab_name=="string").map(L=>({shell_pid:L.shell_pid,tab_name:L.tab_name,cwd:L.cwd??null,opened_at:L.opened_at??new Date().toISOString()})),_=l.extension_instance_id??null,E=[],y=g.filter(L=>{let F=M.claimPidOwnership(L.shell_pid,_);return E.push({shell_pid:L.shell_pid,ownership:F}),F!=="rejected"}),R=M.sync(y),O=0;for(let L of y){let F=p.get(L.shell_pid),W=M.get(L.shell_pid)?.tab_name??L.tab_name,J=!!W&&!pe(W)&&!ue(W)?W:L.tab_name;F!==void 0&&F!==J&&(O+=gf(L.shell_pid,J))}let N=E.filter(L=>L.ownership==="rejected").length;N>0&&console.log(`[terminal/sync] dropped ${N} tab_name update(s), pid(s) owned by a different extension instance`);let C=await yA(),x={resolved:0,expired:0};try{x=au()}catch{}let k={rebound:0,ghosts:0,ambiguous:0};try{k=iu()}catch{}return MA(),i.json({ok:!0,count:M.size(),diff:R,propagated:O,live_sweep:C,deferred_resolved:x,rebound:k})}),t.get("/api/terminal/registry",i=>i.json({terminals:M.all(),count:M.size()})),t.get("/api/terminal/sessions/:shellPid",i=>{let l=i.req.param("shellPid"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"shellPid must be a positive integer"},400);let g=M.sessionsFor(p);return i.json({shell_pid:p,sessions:g})}),t.get("/api/sessions/:id/linked-terminal",i=>{let l=i.req.param("id"),p=M.all().find(_=>M.sessionsFor(_.shell_pid).includes(l)),g=[];if(!p){let _=h().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l);if(_?.cwd&&_.started_at){let E=Date.parse(_.started_at);if(Number.isFinite(E)){let y=_.cwd.replace(/\/+$/,""),R=300*1e3;for(let O of M.all()){if(!O.cwd||O.cwd.replace(/\/+$/,"")!==y||pe(O.tab_name))continue;let N=Date.parse(O.opened_at),C=Date.parse(O.last_seen_at);!Number.isFinite(N)||!Number.isFinite(C)||N>E||C+R<E||g.push({shell_pid:O.shell_pid,tab_name:O.tab_name,cwd:O.cwd,opened_at:O.opened_at,last_seen_at:O.last_seen_at,reason:"time-overlap"})}g.sort((O,N)=>Date.parse(N.last_seen_at)-Date.parse(O.last_seen_at))}}}return p?i.json({linked:{shell_pid:p.shell_pid,tab_name:p.tab_name,cwd:p.cwd},suggested:[]}):i.json({linked:null,suggested:g})}),t.post("/api/sessions/:id/auto-relink",async i=>{let l=i.req.param("id");if(Se(l))return i.json({applied:!1,reason:"has-alias"});if(M.all().some(y=>M.sessionsFor(y.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=h().prepare("SELECT cwd, git_branch, started_at FROM sessions WHERE id = ?").get(l);if(!g?.cwd)return i.json({applied:!1,reason:"no-cwd"});let _=g.cwd.replace(/\/+$/,""),E=M.all().filter(y=>y.cwd&&y.cwd.replace(/\/+$/,"")===_&&!pe(y.tab_name));if(E.length===1){let y=E[0],R=Dn({sessionStartedAt:g.started_at??null,terminalOpenedAt:y.opened_at??null});if(!R.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:R.reason});let O=M.getOrigin(l),N=Mt({tabName:y.tab_name,origin:O??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return N?(_e(l,N),M.linkSession(l,y.shell_pid),i.json({applied:!0,alias:N,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,method:"cwd-singleton"})):i.json({applied:!1,reason:"no-usable-name"})}if(E.length>1){let y=await ou(l);if(y){let O=M.get(y.shell_pid),N=Dn({sessionStartedAt:g.started_at??null,terminalOpenedAt:O?.opened_at??null});if(!N.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:N.reason});let C=M.getOrigin(l),x=Mt({tabName:y.tab_name,origin:C??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(x)return _e(l,x),M.linkSession(l,y.shell_pid),i.json({applied:!0,alias:x,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,matched_fingerprints:y.matched_fingerprints,method:"content-match"})}let R=6e4;if(g.started_at){let O=Date.parse(g.started_at);if(Number.isFinite(O)){let N=E.filter(x=>Dn({sessionStartedAt:g.started_at,terminalOpenedAt:x.opened_at??null}).allowed).map(x=>({t:x,gap:O-Date.parse(x.opened_at??"")})).filter(x=>Number.isFinite(x.gap)&&x.gap>=0&&x.gap<=R);if(N.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:N.length});let C=N[0];if(C){let x=M.getOrigin(l),k=Mt({tabName:C.t.tab_name,origin:x??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(k)return _e(l,k),M.linkSession(l,C.t.shell_pid),i.json({applied:!0,alias:k,linked_pid:C.t.shell_pid,linked_tab_name:C.t.tab_name,method:"closest-before-temporal",gap_ms:C.gap})}}}return i.json({applied:!1,reason:"ambiguous",candidate_count:E.length})}return i.json({applied:!1,reason:"no-candidates"})}),t.post("/api/sessions/:id/relink",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(p?.clear)return M.unlinkSession(l),Is(l),i.json({ok:!0,alias:null,linked_pid:null});if(!p||typeof p.shell_pid!="number")return i.json({error:"shell_pid required"},400);let g=M.get(p.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let _=M.getOrigin(l),E=h().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),y=null,R=g.tab_name?.trim()??"";if(R&&!pe(R)&&!ue(R))y=R;else if(R&&ue(R)){let O=It(R);O&&!pe(O)&&(y=O)}return y?(M.unlinkSession(l),_e(l,y),i.json({ok:!0,alias:y,linked_pid:p.shell_pid,linked_tab_name:g.tab_name})):i.json({error:"terminal has no usable name, name the tab in your editor first, then retry the relink"},422)}),t.post("/api/sessions/:id/recorrelate",async i=>{let l=i.req.param("id"),p=h().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!p?.file_path)return i.json({error:"session not found"},404);M.unlinkSession(l),Is(l),await js(p.file_path);let g=Se(l);return i.json({ok:!0,alias:g,linked_pid:M.all().find(_=>M.sessionsFor(_.shell_pid).includes(l))?.shell_pid??null})}),t.get("/api/paste-expand",async i=>{let l=i.req.query("session"),p=i.req.query("message"),g=i.req.query("path");if(!l||!p||!g)return i.json({error:"session, message and path are required"},400);let _=h(),E=_.prepare("SELECT rowid, content_text FROM messages WHERE uuid = ? AND session_id = ?").get(p,l);if(!E)return i.json({error:"message not found in session"},404);let y=g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(!new RegExp(`\\[Pasted text #\\d+ \\+\\d+ lines\\]\\s*${y}`).test(E.content_text??""))return i.json({error:"path not referenced by this message"},403);let O=_.prepare(`SELECT content_text FROM messages
2162
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(p),O=mp(y,k,{mode:g,includeSidechain:_,prelude:E});return i.text(O)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:$u(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),p=Qe(l);if(!p)return i.json({error:"not found"},404);let g=Uu(l,!0);return i.json({collection:p,members:g})}),t.post("/api/collections",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=An({name:l.name,description:l.description??null,icon:l.icon??null,color:l.color??null,parent_id:l.parent_id??null,sort_key:l.sort_key});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/collections/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let g=Hu(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.post("/api/collections/:id/archive",i=>{let l=i.req.param("id");try{let p=Wu(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/restore",i=>{let l=i.req.param("id");try{let p=qu(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/members",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.session_id!="string")return i.json({error:"session_id required"},400);try{let g=xn(l,p.session_id,p.note??null);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/collections/:id/members/:sid",i=>{let l=i.req.param("id"),p=i.req.param("sid");try{let g=Xu(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/collections",i=>{let l=i.req.param("id");return i.json({collections:Bu(l)})});let A=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:Ku()})),t.post("/api/auto-collections/rules",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string"||typeof l.pattern!="string"||!l.type||!A.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let p=mi({name:l.name,type:l.type,pattern:l.pattern,collection_id:l.collection_id,parent_collection_id:l.parent_collection_id,priority:l.priority,enabled:l.enabled});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/auto-collections/rules/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let g=Vu(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/auto-collections/rules/:id",i=>{let l=i.req.param("id");try{let p=Qu(l);return i.json(p)}catch(p){return i.json({error:p.message},400)}}),t.get("/api/auto-collections/suggestions",i=>{let l=i.req.query("dismissed")==="1";return i.json({suggestions:Qs({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let p=ed(l);return i.json({rule:p})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/suggestions/:id/dismiss",i=>{let l=i.req.param("id");try{return Zu(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=Zs();return i.json({suggestions:l})}),t.get("/api/auto-collections/suggestions/:id/preview",i=>{let l=i.req.param("id"),p=Math.max(1,Math.min(20,Number(i.req.query("limit"))||3)),_=Qs({includeDismissed:!1}).find(y=>y.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=Yu(_.type,_.pattern,p);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(td());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Yi({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),p=ce(l);if(!p)return i.json({error:"thread not found"},404);let g=p.edges.map(_=>({..._,alias_source:_.alias==null?null:M.isSessionAutoLinked(_.session_id)?"auto":"manual"}));return i.json({thread:{...p,edges:g}})}),t.post("/api/threads",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name)return i.json({error:"name required"},400);try{let p=gr({name:l.name,summary:l.summary??null,originSessionId:l.originSessionId});return i.json({thread:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/threads/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{p.name&&Cp(l,p.name),p.close&&Lp(l),p.reopen&&vp(l),p.archive&&Ip(l),"folder_id"in p&&Zp(l,p.folder_id??null);let g=ce(l);return g?i.json({thread:g}):i.json({error:"thread not found"},404)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/thread-folders",i=>i.json({folders:Qi()})),t.post("/api/thread-folders",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=Gp({name:l.name,parentFolderId:l.parent_folder_id??null,projectScope:l.project_scope??null});return i.json({folder:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/thread-folders/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{let g;return p.name&&(g=zp(l,p.name)),"parent_folder_id"in p&&(g=Kp(l,p.parent_folder_id??null)),g?i.json({folder:g}):i.json({error:"no patch fields"},400)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/thread-folders/:id",i=>{let l=i.req.param("id");try{return Qp(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/thread-folders/reorder",async i=>{let l=await i.req.json().catch(()=>({})),p=l.ordered_ids;if(!Array.isArray(p))return i.json({error:"ordered_ids must be an array"},400);try{return Vp(l.parent_folder_id??null,l.project_scope??null,p),i.json({ok:!0})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/sessions",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionId)return i.json({error:"sessionId required"},400);try{let g=fr({threadId:l,sessionId:p.sessionId,parentSessionId:p.parentSessionId??null,role:p.role});return i.json({edge:g})}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/threads/:id/sessions/:sessionId",i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),g=Op(l,p);return i.json(g)}),t.patch("/api/threads/:id/sessions/:sessionId",async i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),g=await i.req.json().catch(()=>({}));try{let _=jn(l,p,g.parentSessionId??null);return i.json({edge:_})}catch(_){return i.json({error:_.message},400)}}),t.post("/api/threads/:id/merge",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sourceId)return i.json({error:"sourceId required"},400);try{let g=Mp(p.sourceId,l);return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/split",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionIds?.length||!p.newThreadName)return i.json({error:"sessionIds and newThreadName required"},400);try{let g=Dp({threadId:l,sessionIds:p.sessionIds,newThreadName:p.newThreadName});return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/threads",i=>{let l=i.req.param("id");return i.json({threads:Np(l)})});let v=j.object({enabled:j.boolean(),band_lo:j.number().min(0).max(1).optional(),band_hi:j.number().min(0).max(1).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),D=j.object({project:j.string().min(1),threshold:j.number().min(0).max(1).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:v});t.post("/api/threads/scan/preflight",async i=>{let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=D.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Sg({project:g.data.project,threshold:g.data.threshold,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json(_)});let X=j.object({project:j.string().min(1),threshold:j.number().min(0).max(1).optional(),llm_names:j.boolean().optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:v});t.post("/api/threads/scan/apply",async i=>{let l=je(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=X.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=yg({project:g.data.project,threshold:g.data.threshold,llm_names:g.data.llm_names,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json({jobId:_.jobId,reused:_.reused},_.reused?409:200)}),t.get("/api/threads/scan/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Ta(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let k of wg(l,g))if(E||(await _.writeSSE({id:String(k.id),event:k.kind,data:JSON.stringify(k.data)}),k.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=Ta(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/threads/scan/jobs/:jobId",i=>{let l=je(i);return l||(Rg(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let I=j.object({project_id:j.number().int().positive(),mode:j.enum(["preflight","apply"]),window_hours:j.number().min(.5).max(168).optional(),score_threshold:j.number().min(0).max(1).optional(),use_live_pids:j.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),p=I.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=await qp(p.data.project_id,{windowHours:p.data.window_hours,scoreThreshold:p.data.score_threshold,useLivePids:p.data.use_live_pids});if(p.data.mode==="preflight")return i.json({plan:g});let _=Xp(g);return i.json({plan:g,result:_})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/threads/:id/titles/preflight",i=>{let l=i.req.param("id"),p=ce(l);if(!p)return i.json({error:"thread not found"},404);let g=h(),_=0;for(let E of p.edges)g.prepare("SELECT auto_title_source FROM sessions WHERE id = ?").get(E.session_id)?.auto_title_source==="agent"&&(_+=1);return i.json({total:p.edges.length,alreadyTitled:_,untitled:p.edges.length-_})}),t.post("/api/threads/:id/titles/generate",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),g=ce(l);if(!g)return i.json({error:"thread not found"},404);if(g.edges.length===0)return i.json({error:"thread has no sessions"},400);let _=qe(),E=p.model??_.model,y=pm({threadId:l,force:p.force??!1,model:E});return i.json({jobId:y})}),t.get("/api/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Zi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let k of mm(l,g))if(E||(await _.writeSSE({id:String(k.id),event:k.kind,data:JSON.stringify(k.data)}),k.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/jobs/:jobId",i=>{let l=Zi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>gm(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404)),t.post("/api/terminal/opened",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=M.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance",count:M.size()});let g=M.upsert({shell_pid:l.shell_pid,tab_name:l.tab_name,cwd:l.cwd??null,opened_at:l.opened_at??new Date().toISOString()});return i.json({ok:!0,ownership:p,count:M.size(),entry:g})}),t.post("/api/terminal/renamed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=M.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance"});let g=M.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let _=Tf(l.shell_pid,l.tab_name);return i.json({ok:!0,ownership:p,entry:g,propagated:_})}),t.post("/api/terminal/closed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number")return i.json({error:"shell_pid required"},400);let p=M.remove(l.shell_pid);return i.json({ok:!0,removed:p,count:M.size()})}),t.post("/api/terminal/claude-started",async i=>{let l=await i.req.json().catch(()=>null);return!l||typeof l.shell_pid!="number"?i.json({error:"shell_pid required"},400):(M.pushPending({shell_pid:l.shell_pid,tab_name:typeof l.tab_name=="string"?l.tab_name:"",cwd:typeof l.cwd=="string"?l.cwd:null,started_at:typeof l.started_at=="string"?l.started_at:new Date().toISOString()}),i.json({ok:!0,pending:M.pendingSize()}))}),t.post("/api/terminal/output",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.text!="string")return i.json({error:"shell_pid and text required"},400);let p=l.text.length>8192?l.text.slice(-8192):l.text,g=typeof l.captured_at=="string"?l.captured_at:new Date().toISOString();return M.setOutputTail(l.shell_pid,p,g),i.json({ok:!0})}),t.post("/api/terminal/sync",async i=>{let l=await i.req.json().catch(()=>null);if(!l||!Array.isArray(l.terminals))return i.json({error:"terminals array required"},400);let p=new Map;for(let C of M.all())p.set(C.shell_pid,C.tab_name);let g=l.terminals.filter(C=>!!C&&typeof C.shell_pid=="number"&&typeof C.tab_name=="string").map(C=>({shell_pid:C.shell_pid,tab_name:C.tab_name,cwd:C.cwd??null,opened_at:C.opened_at??new Date().toISOString()})),_=l.extension_instance_id??null,E=[],y=g.filter(C=>{let F=M.claimPidOwnership(C.shell_pid,_);return E.push({shell_pid:C.shell_pid,ownership:F}),F!=="rejected"}),k=M.sync(y),O=0;for(let C of y){let F=p.get(C.shell_pid),W=M.get(C.shell_pid)?.tab_name??C.tab_name,J=!!W&&!pe(W)&&!ue(W)?W:C.tab_name;F!==void 0&&F!==J&&(O+=Tf(C.shell_pid,J))}let N=E.filter(C=>C.ownership==="rejected").length;N>0&&console.log(`[terminal/sync] dropped ${N} tab_name update(s), pid(s) owned by a different extension instance`);let L=await LA(),x={resolved:0,expired:0};try{x=pu()}catch{}let w={rebound:0,ghosts:0,ambiguous:0};try{w=du()}catch{}return WA(),i.json({ok:!0,count:M.size(),diff:k,propagated:O,live_sweep:L,deferred_resolved:x,rebound:w})}),t.get("/api/terminal/registry",i=>i.json({terminals:M.all(),count:M.size()})),t.get("/api/terminal/sessions/:shellPid",i=>{let l=i.req.param("shellPid"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"shellPid must be a positive integer"},400);let g=M.sessionsFor(p);return i.json({shell_pid:p,sessions:g})}),t.get("/api/sessions/:id/linked-terminal",i=>{let l=i.req.param("id"),p=M.all().find(_=>M.sessionsFor(_.shell_pid).includes(l)),g=[];if(!p){let _=h().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l);if(_?.cwd&&_.started_at){let E=Date.parse(_.started_at);if(Number.isFinite(E)){let y=_.cwd.replace(/\/+$/,""),k=300*1e3;for(let O of M.all()){if(!O.cwd||O.cwd.replace(/\/+$/,"")!==y||pe(O.tab_name))continue;let N=Date.parse(O.opened_at),L=Date.parse(O.last_seen_at);!Number.isFinite(N)||!Number.isFinite(L)||N>E||L+k<E||g.push({shell_pid:O.shell_pid,tab_name:O.tab_name,cwd:O.cwd,opened_at:O.opened_at,last_seen_at:O.last_seen_at,reason:"time-overlap"})}g.sort((O,N)=>Date.parse(N.last_seen_at)-Date.parse(O.last_seen_at))}}}return p?i.json({linked:{shell_pid:p.shell_pid,tab_name:p.tab_name,cwd:p.cwd},suggested:[]}):i.json({linked:null,suggested:g})}),t.post("/api/sessions/:id/auto-relink",async i=>{let l=i.req.param("id");if(Se(l))return i.json({applied:!1,reason:"has-alias"});if(M.all().some(y=>M.sessionsFor(y.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=h().prepare("SELECT cwd, git_branch, started_at FROM sessions WHERE id = ?").get(l);if(!g?.cwd)return i.json({applied:!1,reason:"no-cwd"});let _=g.cwd.replace(/\/+$/,""),E=M.all().filter(y=>y.cwd&&y.cwd.replace(/\/+$/,"")===_&&!pe(y.tab_name));if(E.length===1){let y=E[0],k=Dn({sessionStartedAt:g.started_at??null,terminalOpenedAt:y.opened_at??null});if(!k.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:k.reason});let O=M.getOrigin(l),N=Mt({tabName:y.tab_name,origin:O??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return N?(_e(l,N),M.linkSession(l,y.shell_pid),i.json({applied:!0,alias:N,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,method:"cwd-singleton"})):i.json({applied:!1,reason:"no-usable-name"})}if(E.length>1){let y=await uu(l);if(y){let O=M.get(y.shell_pid),N=Dn({sessionStartedAt:g.started_at??null,terminalOpenedAt:O?.opened_at??null});if(!N.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:N.reason});let L=M.getOrigin(l),x=Mt({tabName:y.tab_name,origin:L??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(x)return _e(l,x),M.linkSession(l,y.shell_pid),i.json({applied:!0,alias:x,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,matched_fingerprints:y.matched_fingerprints,method:"content-match"})}let k=6e4;if(g.started_at){let O=Date.parse(g.started_at);if(Number.isFinite(O)){let N=E.filter(x=>Dn({sessionStartedAt:g.started_at,terminalOpenedAt:x.opened_at??null}).allowed).map(x=>({t:x,gap:O-Date.parse(x.opened_at??"")})).filter(x=>Number.isFinite(x.gap)&&x.gap>=0&&x.gap<=k);if(N.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:N.length});let L=N[0];if(L){let x=M.getOrigin(l),w=Mt({tabName:L.t.tab_name,origin:x??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(w)return _e(l,w),M.linkSession(l,L.t.shell_pid),i.json({applied:!0,alias:w,linked_pid:L.t.shell_pid,linked_tab_name:L.t.tab_name,method:"closest-before-temporal",gap_ms:L.gap})}}}return i.json({applied:!1,reason:"ambiguous",candidate_count:E.length})}return i.json({applied:!1,reason:"no-candidates"})}),t.post("/api/sessions/:id/relink",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(p?.clear)return M.unlinkSession(l),Ms(l),i.json({ok:!0,alias:null,linked_pid:null});if(!p||typeof p.shell_pid!="number")return i.json({error:"shell_pid required"},400);let g=M.get(p.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let _=M.getOrigin(l),E=h().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),y=null,k=g.tab_name?.trim()??"";if(k&&!pe(k)&&!ue(k))y=k;else if(k&&ue(k)){let O=It(k);O&&!pe(O)&&(y=O)}return y?(M.unlinkSession(l),_e(l,y),i.json({ok:!0,alias:y,linked_pid:p.shell_pid,linked_tab_name:g.tab_name})):i.json({error:"terminal has no usable name, name the tab in your editor first, then retry the relink"},422)}),t.post("/api/sessions/:id/recorrelate",async i=>{let l=i.req.param("id"),p=h().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!p?.file_path)return i.json({error:"session not found"},404);M.unlinkSession(l),Ms(l),await Ps(p.file_path);let g=Se(l);return i.json({ok:!0,alias:g,linked_pid:M.all().find(_=>M.sessionsFor(_.shell_pid).includes(l))?.shell_pid??null})}),t.get("/api/paste-expand",async i=>{let l=i.req.query("session"),p=i.req.query("message"),g=i.req.query("path");if(!l||!p||!g)return i.json({error:"session, message and path are required"},400);let _=h(),E=_.prepare("SELECT rowid, content_text FROM messages WHERE uuid = ? AND session_id = ?").get(p,l);if(!E)return i.json({error:"message not found in session"},404);let y=g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(!new RegExp(`\\[Pasted text #\\d+ \\+\\d+ lines\\]\\s*${y}`).test(E.content_text??""))return i.json({error:"path not referenced by this message"},403);let O=_.prepare(`SELECT content_text FROM messages
2163
2163
  WHERE session_id = ? AND rowid > ?
2164
- ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let N of O){let C=N.content_text??"";if(C.includes("**Tool result**")&&C.includes(g))return i.json({source:"tool-result",content:C});if(/^\s*1\t/.test(C)&&C.length>200)return i.json({source:"tool-result",content:C})}try{let N=await EA(g),C=SA();if(!N.startsWith(C+"/")&&!N.startsWith(C+"\\"))return i.json({error:"path outside allowed root"},403);let x=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],L=N.slice(C.length+1).split("/")[0].split("\\")[0];if(x.includes(L))return i.json({error:"path inside sensitive directory"},403);let F=await _A(N),W=2*1024*1024;if(F.size>W)return i.json({error:"file too large",size:F.size,max:W},413);let K=await hA(N,"utf8");return i.json({source:"disk",content:K})}catch(N){return i.json({source:"missing",error:N.message})}}),t.get("/api/projects/:name/stats",i=>{let l=h(),p=i.req.param("name"),g=l.prepare(`SELECT
2164
+ ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let N of O){let L=N.content_text??"";if(L.includes("**Tool result**")&&L.includes(g))return i.json({source:"tool-result",content:L});if(/^\s*1\t/.test(L)&&L.length>200)return i.json({source:"tool-result",content:L})}try{let N=await xA(g),L=OA();if(!N.startsWith(L+"/")&&!N.startsWith(L+"\\"))return i.json({error:"path outside allowed root"},403);let x=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],C=N.slice(L.length+1).split("/")[0].split("\\")[0];if(x.includes(C))return i.json({error:"path inside sensitive directory"},403);let F=await kA(N),W=2*1024*1024;if(F.size>W)return i.json({error:"file too large",size:F.size,max:W},413);let K=await AA(N,"utf8");return i.json({source:"disk",content:K})}catch(N){return i.json({source:"missing",error:N.message})}}),t.get("/api/projects/:name/stats",i=>{let l=h(),p=i.req.param("name"),g=l.prepare(`SELECT
2165
2165
  (SELECT COUNT(*) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=? AND s.message_count > 2) AS sessions,
2166
2166
  (SELECT COALESCE(SUM(s.message_count), 0) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=?) AS messages,
2167
2167
  (SELECT MIN(s.started_at) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=? AND s.started_at IS NOT NULL) AS earliest,
@@ -2169,14 +2169,14 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2169
2169
  JOIN projects p ON p.id = s.project_id
2170
2170
  WHERE p.name = ? AND s.git_branch IS NOT NULL
2171
2171
  ORDER BY s.git_branch
2172
- LIMIT 20`).all(p).map(E=>E.git_branch);return i.json({...g,branches:_})});function P(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function te(i){if(!e)return i;let l=`<meta name="recall-token" content="${P(e)}" />`,p=i.indexOf("</head>");return p!==-1?i.slice(0,p)+l+i.slice(p):l+i}function $(i){let l=_f();return i.html(op({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:ff}))}function ne(){try{return Fa(Ef,"utf8")}catch{return null}}return OA?(t.use("/assets/*",mf({root:Ua})),t.get("/favicon.svg",mf({root:Ua})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=ne();return l===null?$(i):i.html(te(l))}),t.get("*",i=>{if(i.req.path.startsWith("/api/"))return i.notFound();i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=ne();return l===null?$(i):i.html(te(l))})):t.get("/",i=>$(i)),t}function UA(){if(Mr(),!!_t().heuristicEnabled){try{let{updated:e}=wu();e>0&&console.log(`[auto-title] backfilled heuristic title on ${e} sessions`)}catch(e){console.error("[auto-title] backfill failed:",e)}try{let{scanned:e,updated:t}=Ru();t>0&&console.log(`[auto-title] refreshed templated heuristic title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] templated-title refresh failed:",e)}try{let{scanned:e,updated:t}=ku();t>0&&console.log(`[auto-title] refreshed recursive_meta title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] recursive_meta refresh failed:",e)}try{let{scanned:e,updated:t}=Au();t>0&&console.log(`[auto-title] canonicalized brand on ${t}/${e} session titles`)}catch(e){console.error("[auto-title] brand canonicalization failed:",e)}}}async function Tf(e,t){let n=$A(t);return new Promise((s,r)=>{try{let o=gA({fetch:n.fetch,port:e,hostname:"127.0.0.1"},()=>{s(o),setImmediate(()=>{try{UA()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}import{createServer as wf}from"node:net";function yf(e){return new Promise(t=>{let n=wf();n.once("error",()=>t(!1)),n.once("listening",()=>{n.close(()=>t(!0))}),n.listen(e,"127.0.0.1")})}async function Rf(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await yf(t))return t;for(let n=0;n<50;n++){let s=49152+Math.floor(Math.random()*16383);if(!e.has(s)&&await yf(s))return s}return new Promise((n,s)=>{let r=wf();r.once("error",s),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>n(a))}else r.close(),s(new Error("could not determine a free port"))})})}function kf(e){let t=Date.now(),n=e.prepare("SELECT id, decoded_path FROM projects WHERE repo_root IS NULL AND is_repo = 0").all();if(n.length===0)return{total:0,ephemeral:0,walked:0,folded:0,deferred:0,durationMs:Date.now()-t};let s=new Set,r=e.prepare("SELECT DISTINCT repo_root FROM projects WHERE repo_root IS NOT NULL AND repo_root != ?").all(Ft);for(let T of r)s.add(T.repo_root);let o=[],a=e.prepare(`UPDATE projects
2172
+ LIMIT 20`).all(p).map(E=>E.git_branch);return i.json({...g,branches:_})});function P(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function te(i){if(!e)return i;let l=`<meta name="recall-token" content="${P(e)}" />`,p=i.indexOf("</head>");return p!==-1?i.slice(0,p)+l+i.slice(p):l+i}function $(i){let l=wf();return i.html(pp({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:yf}))}function ne(){try{return Wa(kf,"utf8")}catch{return null}}return FA?(t.use("/assets/*",Sf({root:Xa})),t.get("/favicon.svg",Sf({root:Xa})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=ne();return l===null?$(i):i.html(te(l))}),t.get("*",i=>{if(i.req.path.startsWith("/api/"))return i.notFound();i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=ne();return l===null?$(i):i.html(te(l))})):t.get("/",i=>$(i)),t}function zA(){if(jr(),!!_t().heuristicEnabled){try{let{updated:e}=Nu();e>0&&console.log(`[auto-title] backfilled heuristic title on ${e} sessions`)}catch(e){console.error("[auto-title] backfill failed:",e)}try{let{scanned:e,updated:t}=Ou();t>0&&console.log(`[auto-title] refreshed templated heuristic title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] templated-title refresh failed:",e)}try{let{scanned:e,updated:t}=Cu();t>0&&console.log(`[auto-title] refreshed recursive_meta title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] recursive_meta refresh failed:",e)}try{let{scanned:e,updated:t}=Lu();t>0&&console.log(`[auto-title] canonicalized brand on ${t}/${e} session titles`)}catch(e){console.error("[auto-title] brand canonicalization failed:",e)}}}async function Nf(e,t){let n=YA(t);return new Promise((s,r)=>{try{let o=wA({fetch:n.fetch,port:e,hostname:"127.0.0.1"},()=>{s(o),setImmediate(()=>{try{zA()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}import{createServer as Cf}from"node:net";function Of(e){return new Promise(t=>{let n=Cf();n.once("error",()=>t(!1)),n.once("listening",()=>{n.close(()=>t(!0))}),n.listen(e,"127.0.0.1")})}async function Lf(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await Of(t))return t;for(let n=0;n<50;n++){let s=49152+Math.floor(Math.random()*16383);if(!e.has(s)&&await Of(s))return s}return new Promise((n,s)=>{let r=Cf();r.once("error",s),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>n(a))}else r.close(),s(new Error("could not determine a free port"))})})}function vf(e){let t=Date.now(),n=e.prepare("SELECT id, decoded_path FROM projects WHERE repo_root IS NULL AND is_repo = 0").all();if(n.length===0)return{total:0,ephemeral:0,walked:0,folded:0,deferred:0,durationMs:Date.now()-t};let s=new Set,r=e.prepare("SELECT DISTINCT repo_root FROM projects WHERE repo_root IS NOT NULL AND repo_root != ?").all(Ft);for(let T of r)s.add(T.repo_root);let o=[],a=e.prepare(`UPDATE projects
2173
2173
  SET repo_root = ?, main_repo = NULL, is_repo = 0, is_worktree = 0
2174
2174
  WHERE id = ?`),c=e.prepare(`UPDATE projects
2175
2175
  SET repo_root = ?, main_repo = ?, is_repo = 1, is_worktree = ?
2176
- WHERE id = ?`),u=e.prepare("UPDATE projects SET repo_root = ?, is_repo = 1 WHERE id = ?"),d=0,m=0,f=0;e.transaction(()=>{for(let T of n)On(T.decoded_path)&&(a.run(Ft,T.id),d++);for(let T of n){if(On(T.decoded_path))continue;let S=er(T.decoded_path);S.isRepo?(c.run(S.root,S.mainRepo,S.isWorktree?1:0,T.id),s.add(S.root),m++):o.push(T)}for(let T of o){let S=Zu(T.decoded_path,s);S&&(u.run(S,T.id),f++)}})();let b=o.length-f;return{total:n.length,ephemeral:d,walked:m,folded:f,deferred:b,durationMs:Date.now()-t}}Lo();U();Q();We();function Wa(){let e=[];return on("stale-claude-json-mcp-paths",()=>{for(let t of HA())e.push(t)}),on("zombie-mcp",()=>{for(let t of WA())e.push(t)}),on("chunk-queue-growth",()=>{for(let t of qA())e.push(t)}),on("watcher-reflag-loop",()=>{for(let t of XA())e.push(t)}),on("disk-pressure-backups",()=>{for(let t of JA())e.push(t)}),on("daemon-state-files",()=>{for(let t of YA())e.push(t)}),e}function on(e,t){try{t()}catch(n){let s=n instanceof Error?n.message:String(n);process.stderr.write(`[doctor-tick] ${e} check failed: ${s}
2177
- `)}}function HA(){let e=rp();if(!e.configExists)return[];let t=[];for(let n of e.findings)t.push(BA(n));return t}function BA(e){return{check:"stale_claude_json_mcp_path",severity:e.severity==="HIGH"?"critical":"high",keyFacts:{name:e.name,stalePath:e.stalePath},message:`~/.claude.json mcpServers.${e.name} points at a deleted path: ${e.stalePath}`,remediation:e.remediation}}function WA(){let e=Ai();return!e.flagged||!e.message?[]:[{check:"zombie_mcp_threshold",severity:"critical",keyFacts:{countBucket:Math.floor(e.orphanCount/5)*5,aggregateGbBucket:Math.floor(e.orphanRssKb/(1024*1024))},message:e.message,remediation:"`recall mcp-prune --all` reaps. If that returns no children on a stale build, `pkill -f mcp-server.js` is the nuclear option."}]}function qA(){let e=Ii();return e.status==="ok"?[]:[{check:"chunk_queue_growth",severity:{critical:"critical",high:"high",medium:"medium"}[e.status],keyFacts:{sizeBucket:Math.floor(e.currentSize/1e3)*1e3,semanticEnabled:e.semanticEnabled},message:e.message,remediation:e.remediation??"Re-run `recall doctor` for context."}]}function XA(){let e=nr(20),t=[];for(let n of e){if(n.noProgressCount<=tp)continue;let s=n.path.replace(/'/g,"''");t.push({check:"watcher_reflag_loop",severity:"critical",keyFacts:{path:n.path},message:`Watcher reindexed ${n.path} ${n.count.toLocaleString()} times in the last hour (${n.noProgressCount.toLocaleString()} with no new content) \u2014 reflag loop.`,remediation:`Mark it skipped with a SQL one-liner:
2178
- sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${s}';"`})}return t}function JA(){let e=np();if(e.severity==="ok"||e.severity==="low")return[];let t=e.severity==="high"?"critical":"medium",n=e.severity==="high"?`Disk pressure HIGH \u2014 ${St(e.freeBytes)} free of ${St(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${St(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`:`Disk pressure MEDIUM \u2014 ${St(e.freeBytes)} free of ${St(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${St(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`;return[{check:"disk_pressure_backups",severity:t,keyFacts:{freePercentBucket:Math.floor(e.freePercent/5)*5,backupGbBucket:Math.floor(e.backupTotalBytes/(1024*1024*1024)),severity:e.severity},message:n,remediation:GA(e.oldFiles)}]}function GA(e){return e.length===0?"No snapshots older than 30 days \u2014 recent ones may still be rollback-critical, leave them alone unless you know what you are doing.":`Review snapshots older than 30 days then delete: ${e.slice(0,3).map(n=>`${n.name} (${St(n.sizeBytes)}, ${n.ageDays}d old)`).join("; ")}. See memory \`partial_corpus_swap_bug_20260519\` \u2014 keep most recent .pre-swap for rollback.`}function YA(){let e=sp();return!e.flagged||!e.message?[]:[{check:"daemon_state_files_missing",severity:"critical",keyFacts:{missing:[...e.missing].sort().join(",")},message:e.message,remediation:e.remediation??"recall stop && recall start"}]}function St(e){return!Number.isFinite(e)||e<=0?"0 B":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}import{createInterface as f$}from"node:readline/promises";U();We();qa();U();qa();U();We();Q();var KF=768*4;We();function Af(){let e=h();if(!e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get())return{outcome:"absent",message:"no vec_chunks_v1_backup present"};let n=e.prepare("SELECT completed_at FROM migration_state WHERE status = 'completed' ORDER BY id DESC LIMIT 1").get();return n?e.prepare("SELECT 1 AS x FROM migration_state WHERE status = 'completed' AND completed_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT 1").get()?{outcome:"kept",message:`backup retained \u2014 last migration (${n.completed_at}) is within the 30-day rollback window`}:(e.exec("DROP TABLE vec_chunks_v1_backup;"),{outcome:"pruned",message:`auto-pruned vec_chunks_v1_backup \u2014 last migration (${n.completed_at}) is older than 30 days`}):{outcome:"kept",message:"backup present but no completed migration on record \u2014 left for manual inspection"}}var xf=300*1e3;function zA(){let e=process.env.RECALL_DOCTOR_TICK_MS;if(!e)return xf;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<100?xf:t}function Nf(e=Wa,t=new Date){let n=[];try{n=e()}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] check pipeline failed: ${a}
2179
- `),n=[]}let s=Di(),r=Vd(s,n,t);ji(r);try{Gd().catch(o=>{let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] auto-prune failed: ${a}
2176
+ WHERE id = ?`),u=e.prepare("UPDATE projects SET repo_root = ?, is_repo = 1 WHERE id = ?"),d=0,m=0,f=0;e.transaction(()=>{for(let T of n)On(T.decoded_path)&&(a.run(Ft,T.id),d++);for(let T of n){if(On(T.decoded_path))continue;let S=tr(T.decoded_path);S.isRepo?(c.run(S.root,S.mainRepo,S.isWorktree?1:0,T.id),s.add(S.root),m++):o.push(T)}for(let T of o){let S=rd(T.decoded_path,s);S&&(u.run(S,T.id),f++)}})();let b=o.length-f;return{total:n.length,ephemeral:d,walked:m,folded:f,deferred:b,durationMs:Date.now()-t}}vo();U();Q();We();function Ya(){let e=[];return on("stale-claude-json-mcp-paths",()=>{for(let t of KA())e.push(t)}),on("zombie-mcp",()=>{for(let t of QA())e.push(t)}),on("chunk-queue-growth",()=>{for(let t of ZA())e.push(t)}),on("watcher-reflag-loop",()=>{for(let t of ex())e.push(t)}),on("disk-pressure-backups",()=>{for(let t of tx())e.push(t)}),on("daemon-state-files",()=>{for(let t of sx())e.push(t)}),e}function on(e,t){try{t()}catch(n){let s=n instanceof Error?n.message:String(n);process.stderr.write(`[doctor-tick] ${e} check failed: ${s}
2177
+ `)}}function KA(){let e=dp();if(!e.configExists)return[];let t=[];for(let n of e.findings)t.push(VA(n));return t}function VA(e){return{check:"stale_claude_json_mcp_path",severity:e.severity==="HIGH"?"critical":"high",keyFacts:{name:e.name,stalePath:e.stalePath},message:`~/.claude.json mcpServers.${e.name} points at a deleted path: ${e.stalePath}`,remediation:e.remediation}}function QA(){let e=Li();return!e.flagged||!e.message?[]:[{check:"zombie_mcp_threshold",severity:"critical",keyFacts:{countBucket:Math.floor(e.orphanCount/5)*5,aggregateGbBucket:Math.floor(e.orphanRssKb/(1024*1024))},message:e.message,remediation:"`recall mcp-prune --all` reaps. If that returns no children on a stale build, `pkill -f mcp-server.js` is the nuclear option."}]}function ZA(){let e=Fi();return e.status==="ok"?[]:[{check:"chunk_queue_growth",severity:{critical:"critical",high:"high",medium:"medium"}[e.status],keyFacts:{sizeBucket:Math.floor(e.currentSize/1e3)*1e3,semanticEnabled:e.semanticEnabled},message:e.message,remediation:e.remediation??"Re-run `recall doctor` for context."}]}function ex(){let e=sr(20),t=[];for(let n of e){if(n.noProgressCount<=cp)continue;let s=n.path.replace(/'/g,"''");t.push({check:"watcher_reflag_loop",severity:"critical",keyFacts:{path:n.path},message:`Watcher reindexed ${n.path} ${n.count.toLocaleString()} times in the last hour (${n.noProgressCount.toLocaleString()} with no new content) \u2014 reflag loop.`,remediation:`Mark it skipped with a SQL one-liner:
2178
+ sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${s}';"`})}return t}function tx(){let e=lp();if(e.severity==="ok"||e.severity==="low")return[];let t=e.severity==="high"?"critical":"medium",n=e.severity==="high"?`Disk pressure HIGH \u2014 ${St(e.freeBytes)} free of ${St(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${St(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`:`Disk pressure MEDIUM \u2014 ${St(e.freeBytes)} free of ${St(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${St(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`;return[{check:"disk_pressure_backups",severity:t,keyFacts:{freePercentBucket:Math.floor(e.freePercent/5)*5,backupGbBucket:Math.floor(e.backupTotalBytes/(1024*1024*1024)),severity:e.severity},message:n,remediation:nx(e.oldFiles)}]}function nx(e){return e.length===0?"No snapshots older than 30 days \u2014 recent ones may still be rollback-critical, leave them alone unless you know what you are doing.":`Review snapshots older than 30 days then delete: ${e.slice(0,3).map(n=>`${n.name} (${St(n.sizeBytes)}, ${n.ageDays}d old)`).join("; ")}. See memory \`partial_corpus_swap_bug_20260519\` \u2014 keep most recent .pre-swap for rollback.`}function sx(){let e=up();return!e.flagged||!e.message?[]:[{check:"daemon_state_files_missing",severity:"critical",keyFacts:{missing:[...e.missing].sort().join(",")},message:e.message,remediation:e.remediation??"recall stop && recall start"}]}function St(e){return!Number.isFinite(e)||e<=0?"0 B":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}import{createInterface as C$}from"node:readline/promises";U();We();za();U();za();U();We();Q();var d$=768*4;We();function If(){let e=h();if(!e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get())return{outcome:"absent",message:"no vec_chunks_v1_backup present"};let n=e.prepare("SELECT completed_at FROM migration_state WHERE status = 'completed' ORDER BY id DESC LIMIT 1").get();return n?e.prepare("SELECT 1 AS x FROM migration_state WHERE status = 'completed' AND completed_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT 1").get()?{outcome:"kept",message:`backup retained \u2014 last migration (${n.completed_at}) is within the 30-day rollback window`}:(e.exec("DROP TABLE vec_chunks_v1_backup;"),{outcome:"pruned",message:`auto-pruned vec_chunks_v1_backup \u2014 last migration (${n.completed_at}) is older than 30 days`}):{outcome:"kept",message:"backup present but no completed migration on record \u2014 left for manual inspection"}}var Mf=300*1e3;function rx(){let e=process.env.RECALL_DOCTOR_TICK_MS;if(!e)return Mf;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<100?Mf:t}function Df(e=Ya,t=new Date){let n=[];try{n=e()}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] check pipeline failed: ${a}
2179
+ `),n=[]}let s=Ui(),r=rp(s,n,t);Bi(r);try{ep().catch(o=>{let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] auto-prune failed: ${a}
2180
2180
  `)})}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] auto-prune failed (sync): ${a}
2181
- `)}try{let o=Af();o.outcome==="pruned"&&console.log(`[doctor-tick] ${o.message}`)}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] backup auto-prune failed: ${a}
2182
- `)}return r}function Of(e=Wa){let t=zA(),n=setInterval(()=>{Nf(e)},t);return typeof n.unref=="function"&&n.unref(),{stop:()=>clearInterval(n),runOnce:()=>{Nf(e)}}}var VA=Math.max(1,KA().length),Lf=String(Math.max(2,Math.floor(VA/2)));process.env.OMP_NUM_THREADS||(process.env.OMP_NUM_THREADS=Lf);process.env.ORT_NUM_THREADS||(process.env.ORT_NUM_THREADS=Lf);var QA=360*60*1e3,ZA=60*1e3,ex=1440*60*1e3,tx=300*1e3,nx=300*1e3,sx=10*1e3,rx=1440*60*1e3,ox=30*1e3,ix=500,ax=1500,cx=6e4;async function lx(){let e=await Rf(),t=vd();(!t||t.length<32)&&(console.error("[daemon] FATAL: daemon token mint returned empty or undersized \u2014 refusing to start"),process.exit(1));try{Og().repointed&&console.log("[daemon] mcp self-heal: ~/.claude.json repointed (was stale)")}catch(p){console.error("[daemon] mcp self-heal failed (non-fatal, daemon continues):",p)}let n=await Tf(e,t),s={pid:process.pid,port:e,startedAt:new Date().toISOString()};Id({...s,token:t}),So(ae().enabled,"boot");try{Tn();let p=kl();p>0&&console.log(`[daemon] archive: migrated ${p} hot row(s) into archive.sqlite`)}catch(p){let g=p instanceof Error?p.message:String(p);console.error(`[daemon] archive migration failed: ${g}`)}let r=Pd({db:h(),walPath:`${ze}-wal`}),o=wd(),a=setInterval(()=>{Rd()},cx),c=()=>{try{Qs()}catch(p){console.error("[daemon] suggestion scan failed:",p)}},u=setTimeout(c,ZA),d=setInterval(c,QA),m=()=>{try{let p=M.reapStaleLinks();(p.pruned_pids||p.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${p.pruned_pids} pid${p.pruned_pids===1?"":"s"}, ${p.pruned_sessions} session link${p.pruned_sessions===1?"":"s"}`)}catch(p){console.error("[daemon] stale-link reaper failed:",p)}},f=setTimeout(m,tx),b=setInterval(m,ex),T=()=>{try{let p=M.gcDeadPids();(p.pruned_pids||p.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${p.pruned_pids} pid${p.pruned_pids===1?"":"s"}, ${p.pruned_sessions} session link${p.pruned_sessions===1?"":"s"}`)}catch(p){console.error("[daemon] dead-pid gc failed:",p)}},S=setTimeout(T,sx),w=setInterval(T,nx),A=()=>{nc().then(p=>{p.ran&&p.revoked&&console.log(`[daemon] license check: REVOKED${p.reason?` (${p.reason})`:""}`)}).catch(p=>{console.error("[daemon] license check failed:",p)})},v=setTimeout(A,ox),D=setInterval(A,rx),X=Bl();(async()=>{try{if(!Ge())return;let p=h().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get();if(p.n===0)return;if(!ae().autoResumeWorker){console.log(`[daemon] vector-worker dormant: ${p.n} chunk(s) pending in queue. Click Start in the web UI to drain, or set semantic.autoResumeWorker=true in ~/.recall/config.json to resume automatically on boot.`);return}let _=await it();if(_.tier!=="pro"){console.log(`[daemon] vector-worker auto-resume skipped: ${p.n} chunk(s) pending but license tier is ${_.tier}`);return}le().loaded||await Be(),Me().running||(In(),console.log(`[daemon] vector-worker auto-resumed: ${p.n} chunk(s) pending`))}catch(p){let g=p instanceof Error?p.message:String(p);console.error(`[daemon] vector-worker auto-resume failed: ${g}`)}})();let I=Of(),P=setInterval(()=>{try{let{restored:p}=Dd({...s,token:t});p.length>0&&console.log(`[daemon] state-files heal: restored ${p.join(",")}`)}catch(p){let g=p instanceof Error?p.message:String(p);console.error(`[daemon] state-files heal failed: ${g}`)}},3e4);typeof P.unref=="function"&&P.unref();let te=lr();console.log(te==="enabled"?"[auto-prune] mode=enabled \u2014 orphans >10min and runaway-CPU MCPs will be killed":te==="off"?"[auto-prune] mode=off \u2014 auto-prune fully disabled":"[auto-prune] mode=dry-run (set RECALL_AUTO_PRUNE=enabled to enforce)");let $=!1,ne=!1,i=()=>{if(ne)return;ne=!0,setTimeout(()=>{console.error("[daemon] graceful shutdown exceeded 4s budget \u2014 forcing exit"),process.exit(0)},4e3).unref()},l=p=>{$||($=!0,console.log(`[daemon] received ${p}, shutting down`),X.stop(),clearTimeout(u),clearInterval(d),clearTimeout(f),clearInterval(b),clearTimeout(S),clearInterval(w),clearTimeout(v),clearInterval(D),clearInterval(a),clearInterval(P),r.stop(),I.stop(),Oi(),process.exit(0))};for(let p of["SIGTERM","SIGINT","SIGHUP"])process.on(p,()=>{i(),l(p)});try{let p=kf(h());p.total>0&&console.log(`[daemon] projects backfill: ephemeral=${p.ephemeral} walked=${p.walked} folded=${p.folded} deferred=${p.deferred} (total=${p.total}, ${p.durationMs}ms)`)}catch(p){console.error("[daemon] projects backfill failed:",p)}console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{Ps().then(p=>{console.log(`[daemon] boot sweep: scanned ${p.scanned} live claude(s), linked ${p.linked}, renamed ${p.renamed}, ambiguous_cwd ${p.ambiguous_cwd}`)}).catch(p=>{console.error("[daemon] boot sweep failed:",p)})},ix),setTimeout(()=>{Ri().then(p=>{console.log(`[daemon] ingestion sweep: scanned=${p.scanned} reindexed=${p.reindexed} up-to-date=${p.upToDate} skipped=${p.skipped} errors=${p.errors} (${p.durationMs}ms)`)}).catch(p=>{console.error("[daemon] ingestion sweep failed:",p)})},ax)}lx().catch(e=>{console.error("[daemon] fatal:",e),Oi(),process.exit(1)});
2181
+ `)}try{let o=If();o.outcome==="pruned"&&console.log(`[doctor-tick] ${o.message}`)}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] backup auto-prune failed: ${a}
2182
+ `)}return r}function jf(e=Ya){let t=rx(),n=setInterval(()=>{Df(e)},t);return typeof n.unref=="function"&&n.unref(),{stop:()=>clearInterval(n),runOnce:()=>{Df(e)}}}var ix=Math.max(1,ox().length),Pf=String(Math.max(2,Math.floor(ix/2)));process.env.OMP_NUM_THREADS||(process.env.OMP_NUM_THREADS=Pf);process.env.ORT_NUM_THREADS||(process.env.ORT_NUM_THREADS=Pf);var ax=360*60*1e3,cx=60*1e3,lx=1440*60*1e3,ux=300*1e3,dx=300*1e3,px=10*1e3,mx=1440*60*1e3,gx=30*1e3,fx=500,_x=1500,hx=6e4;async function Ex(){let e=await Lf(),t=$d();(!t||t.length<32)&&(console.error("[daemon] FATAL: daemon token mint returned empty or undersized \u2014 refusing to start"),process.exit(1));try{jg().repointed&&console.log("[daemon] mcp self-heal: ~/.claude.json repointed (was stale)")}catch(p){console.error("[daemon] mcp self-heal failed (non-fatal, daemon continues):",p)}let n=await Nf(e,t),s={pid:process.pid,port:e,startedAt:new Date().toISOString()};Ud({...s,token:t}),yo(ae().enabled,"boot");try{Tn();let p=Cl();p>0&&console.log(`[daemon] archive: migrated ${p} hot row(s) into archive.sqlite`)}catch(p){let g=p instanceof Error?p.message:String(p);console.error(`[daemon] archive migration failed: ${g}`)}let r=qd({db:h(),walPath:`${ze}-wal`}),o=Cd(),a=setInterval(()=>{Ld()},hx),c=()=>{try{Zs()}catch(p){console.error("[daemon] suggestion scan failed:",p)}},u=setTimeout(c,cx),d=setInterval(c,ax),m=()=>{try{let p=M.reapStaleLinks();(p.pruned_pids||p.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${p.pruned_pids} pid${p.pruned_pids===1?"":"s"}, ${p.pruned_sessions} session link${p.pruned_sessions===1?"":"s"}`)}catch(p){console.error("[daemon] stale-link reaper failed:",p)}},f=setTimeout(m,ux),b=setInterval(m,lx),T=()=>{try{let p=M.gcDeadPids();(p.pruned_pids||p.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${p.pruned_pids} pid${p.pruned_pids===1?"":"s"}, ${p.pruned_sessions} session link${p.pruned_sessions===1?"":"s"}`)}catch(p){console.error("[daemon] dead-pid gc failed:",p)}},S=setTimeout(T,px),R=setInterval(T,dx),A=()=>{ac().then(p=>{p.ran&&p.revoked&&console.log(`[daemon] license check: REVOKED${p.reason?` (${p.reason})`:""}`)}).catch(p=>{console.error("[daemon] license check failed:",p)})},v=setTimeout(A,gx),D=setInterval(A,mx),X=Gl();(async()=>{try{if(!Ge())return;let p=h().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get();if(p.n===0)return;if(!ae().autoResumeWorker){console.log(`[daemon] vector-worker dormant: ${p.n} chunk(s) pending in queue. Click Start in the web UI to drain, or set semantic.autoResumeWorker=true in ~/.recall/config.json to resume automatically on boot.`);return}let _=await it();if(_.tier!=="pro"){console.log(`[daemon] vector-worker auto-resume skipped: ${p.n} chunk(s) pending but license tier is ${_.tier}`);return}le().loaded||await He(),Me().running||(In(),console.log(`[daemon] vector-worker auto-resumed: ${p.n} chunk(s) pending`))}catch(p){let g=p instanceof Error?p.message:String(p);console.error(`[daemon] vector-worker auto-resume failed: ${g}`)}})();let I=jf(),P=setInterval(()=>{try{let{restored:p}=Hd({...s,token:t});p.length>0&&console.log(`[daemon] state-files heal: restored ${p.join(",")}`)}catch(p){let g=p instanceof Error?p.message:String(p);console.error(`[daemon] state-files heal failed: ${g}`)}},3e4);typeof P.unref=="function"&&P.unref();let te=dr();console.log(te==="enabled"?"[auto-prune] mode=enabled \u2014 orphans >10min and runaway-CPU MCPs will be killed":te==="off"?"[auto-prune] mode=off \u2014 auto-prune fully disabled":"[auto-prune] mode=dry-run (set RECALL_AUTO_PRUNE=enabled to enforce)");let $=!1,ne=!1,i=()=>{if(ne)return;ne=!0,setTimeout(()=>{console.error("[daemon] graceful shutdown exceeded 4s budget \u2014 forcing exit"),process.exit(0)},4e3).unref()},l=p=>{$||($=!0,console.log(`[daemon] received ${p}, shutting down`),X.stop(),clearTimeout(u),clearInterval(d),clearTimeout(f),clearInterval(b),clearTimeout(S),clearInterval(R),clearTimeout(v),clearInterval(D),clearInterval(a),clearInterval(P),r.stop(),I.stop(),Mi(),process.exit(0))};for(let p of["SIGTERM","SIGINT","SIGHUP"])process.on(p,()=>{i(),l(p)});try{let p=vf(h());p.total>0&&console.log(`[daemon] projects backfill: ephemeral=${p.ephemeral} walked=${p.walked} folded=${p.folded} deferred=${p.deferred} (total=${p.total}, ${p.durationMs}ms)`)}catch(p){console.error("[daemon] projects backfill failed:",p)}console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{Fs().then(p=>{console.log(`[daemon] boot sweep: scanned ${p.scanned} live claude(s), linked ${p.linked}, renamed ${p.renamed}, ambiguous_cwd ${p.ambiguous_cwd}`)}).catch(p=>{console.error("[daemon] boot sweep failed:",p)})},fx),setTimeout(()=>{Oi().then(p=>{console.log(`[daemon] ingestion sweep: scanned=${p.scanned} reindexed=${p.reindexed} up-to-date=${p.upToDate} skipped=${p.skipped} errors=${p.errors} (${p.durationMs}ms)`)}).catch(p=>{console.error("[daemon] ingestion sweep failed:",p)})},_x)}Ex().catch(e=>{console.error("[daemon] fatal:",e),Mi(),process.exit(1)});