@clauderecallhq/cli 0.68.3 → 0.72.0

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 xp=Object.defineProperty;var ue=(e,t)=>()=>(e&&(t=e(e=0)),t);var ds=(e,t)=>{for(var s in t)xp(e,s,{get:t[s],enumerable:!0})};import{homedir as ni}from"node:os";import{join as Dn,basename as $y}from"node:path";import{existsSync as Np,mkdirSync as Op,chmodSync as Lp,readdirSync as Hy,statSync as Wy}from"node:fs";function J(){Np(U)||Op(U,{recursive:!0,mode:448}),process.platform!=="win32"&&Lp(U,448)}var Fn,U,Pn,Q=ue(()=>{"use strict";Fn=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Dn(ni(),".claude","projects"),U=process.env.RECALL_HOME?process.env.RECALL_HOME:Dn(ni(),".recall"),Pn=Dn(U,"db.sqlite")});import{createRequire as em}from"node:module";var tm,sm,nm,Bn,Hn,fi,hi=ue(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...s){let n=s[0];return t==="warning"&&n instanceof Error&&n.name==="ExperimentalWarning"&&/SQLite/i.test(n.message)?!1:e(t,...s)}}tm=em(import.meta.url),sm=["node","sqlite"].join(":"),nm=tm(sm),Bn=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 s=t.length===0?this.inner.run():this.inner.run(...t);return{changes:s.changes,lastInsertRowid:s.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},Hn=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new nm.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new Bn(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,s={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(s.simple){let n=this.inner.prepare(`PRAGMA ${t}`).get();return n&&typeof n=="object"?Object.values(n)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...n)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...n);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,s){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),s===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,s)}},fi=Hn});function bi(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),s=new Set(t.map(T=>T.name)),n=[["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"]];for(let[T,S]of n)s.has(T)||e.exec(`ALTER TABLE sessions ADD COLUMN ${T} ${S}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(T=>T.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[T,S]of a)o.has(T)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${T} ${S}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(T=>T.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[T,S]of d)u.has(T)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${T} ${S}`);let m=e.prepare("PRAGMA table_info(threads)").all();new Set(m.map(T=>T.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 h=e.prepare("PRAGMA table_info(thread_folders)").all(),b=new Set(h.map(T=>T.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 nm=Object.defineProperty;var ue=(e,t)=>()=>(e&&(t=e(e=0)),t);var ys=(e,t)=>{for(var s in t)nm(e,s,{get:t[s],enumerable:!0})};import{homedir as Si}from"node:os";import{join as zn,basename as Lw}from"node:path";import{existsSync as rm,mkdirSync as om,chmodSync as im,readdirSync as Iw,statSync as vw}from"node:fs";function z(){rm(B)||om(B,{recursive:!0,mode:448}),process.platform!=="win32"&&im(B,448)}var Kn,B,Vn,ee=ue(()=>{"use strict";Kn=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:zn(Si(),".claude","projects"),B=process.env.RECALL_HOME?process.env.RECALL_HOME:zn(Si(),".recall"),Vn=zn(B,"db.sqlite")});import{createRequire as Lm}from"node:module";var Cm,Im,vm,er,tr,vi,ji=ue(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...s){let n=s[0];return t==="warning"&&n instanceof Error&&n.name==="ExperimentalWarning"&&/SQLite/i.test(n.message)?!1:e(t,...s)}}Cm=Lm(import.meta.url),Im=["node","sqlite"].join(":"),vm=Cm(Im),er=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 s=t.length===0?this.inner.run():this.inner.run(...t);return{changes:s.changes,lastInsertRowid:s.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},tr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new vm.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new er(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,s={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(s.simple){let n=this.inner.prepare(`PRAGMA ${t}`).get();return n&&typeof n=="object"?Object.values(n)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...n)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...n);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,s){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),s===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,s)}},vi=tr});function Di(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),s=new Set(t.map(T=>T.name)),n=[["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"]];for(let[T,S]of n)s.has(T)||e.exec(`ALTER TABLE sessions ADD COLUMN ${T} ${S}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(T=>T.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[T,S]of a)o.has(T)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${T} ${S}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(T=>T.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[T,S]of d)u.has(T)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${T} ${S}`);let m=e.prepare("PRAGMA table_info(threads)").all();new Set(m.map(T=>T.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 h=e.prepare("PRAGMA table_info(thread_folders)").all(),b=new Set(h.map(T=>T.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 xp=Object.defineProperty;var ue=(e,t)=>()=>(e&&(t=e(e=0)),t);var ds=(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
- `)}var Ei,Si=ue(()=>{"use strict";Ei=`
15
+ `)}var Mi,Fi=ue(()=>{"use strict";Mi=`
16
16
  PRAGMA journal_mode = WAL;
17
17
  PRAGMA synchronous = NORMAL;
18
18
  PRAGMA foreign_keys = ON;
@@ -668,9 +668,9 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_target
668
668
  ON bug_synthesis_results(scope, target_id, created_at DESC);
669
669
  CREATE INDEX IF NOT EXISTS idx_synth_results_created
670
670
  ON bug_synthesis_results(created_at DESC);
671
- `});import*as Ti from"sqlite-vec";function f(){if(de)return de;J(),de=new fi(Pn),Ti.load(de),de.pragma("cache_size = -64000"),de.pragma("mmap_size = 268435456"),de.pragma("temp_store = MEMORY"),de.pragma("busy_timeout = 5000"),de.pragma("journal_size_limit = 67108864"),de.pragma("wal_autocheckpoint = 1000"),de.exec(Ei),bi(de);try{de.exec("PRAGMA optimize")}catch{}return de}var de,H=ue(()=>{"use strict";hi();Q();Si();de=null});var Fi={};ds(Fi,{EmbedderUnavailableError:()=>Lt,embed:()=>Ct,embedQuery:()=>qn,getEmbedderStatus:()=>Ne,loadEmbedder:()=>gs,unloadEmbedder:()=>pm});import{join as Mi}from"node:path";function dm(){return Mi(U,"models","bge-base-en-v1.5")}async function gs(){if(ms&&Ot)return;let e;try{e=await import("@huggingface/transformers")}catch(n){throw new Lt(n)}let{pipeline:t,env:s}=e;s.localModelPath=Mi(U,"models"),s.allowRemoteModels=!1;try{Ot=await t("feature-extraction",Di,{local_files_only:!0,cache_dir:dm()}),ms=!0}catch(n){throw n instanceof Error&&/Cannot find module|onnxruntime_binding/.test(n.message)?new Lt(n):n}}function Ne(){return{loaded:ms,modelId:Di,dim:lm}}async function Ct(e){if(!Ot)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=[];for(let s=0;s<e.length;s+=ji){let n=e.slice(s,s+ji),o=(await Ot(n,{pooling:"cls",normalize:!0})).tolist();for(let a=0;a<n.length;a++){let c=o[a],u=Array.isArray(c[0])?c[0]:c;t.push(new Float32Array(u))}}return t}async function qn(e){let t=um+e,[s]=await Ct([t]);return s}function pm(){Ot=null,ms=!1}var Di,lm,ji,um,Ot,ms,Lt,ze=ue(()=>{"use strict";Q();Di="BAAI/bge-base-en-v1.5",lm=768,ji=64,um="Represent this sentence for searching relevant passages: ",Ot=null,ms=!1;Lt=class extends Error{cause;constructor(t){let s=t instanceof Error?t.message:String(t);super(["Semantic search is unavailable on this platform.","",`Reason: ${s}`,"",`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) \u2014 or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
672
- `)),this.name="EmbedderUnavailableError",this.cause=t}}});import{writeFileSync as mg}from"node:fs";import{join as gg}from"node:path";function Be(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Ve(e,t){let s=Be(t);if(!s)throw new Error("tag must contain at least one alphanumeric character");let n=f(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?{tag:s,added:!1}:(n.transaction(()=>{n.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,s,r),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,s,r)})(),ia(),{tag:s,added:!0})}function oa(e,t){let s=Be(t);if(!s)return{tag:"",removed:!1};let n=f(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?(n.transaction(()=>{n.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,s),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,s,r)})(),ia(),{tag:s,removed:!0}):{tag:s,removed:!1}}function Mt(e){return f().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function Ze(){return f().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
673
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function ia(){try{J();let e=f(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),s=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),n={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:s};mg(_g,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var _g,Qe=ue(()=>{"use strict";H();Q();_g=gg(U,"tags.json")});function fg(e,t){let s=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(s.length<=t)return s;let n=new Set;n.add(0),n.add(s.length-1);let r=(s.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)n.add(Math.floor(o*r));return Array.from(n).sort((o,a)=>o-a).slice(0,t).map(o=>s[o])}function et(e){let t=f(),s={limit:e.limit??500},n=e.sessionIds&&e.sessionIds.length>0,r=n?"1=1":"s.message_count > 2";if(n){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{s[`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",s.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",s.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
671
+ `});import*as Pi from"sqlite-vec";function f(){if(ge)return ge;z(),ge=new vi(Vn),Pi.load(ge),ge.pragma("cache_size = -64000"),ge.pragma("mmap_size = 268435456"),ge.pragma("temp_store = MEMORY"),ge.pragma("busy_timeout = 5000"),ge.pragma("journal_size_limit = 67108864"),ge.pragma("wal_autocheckpoint = 1000"),ge.exec(Mi),Di(ge);try{ge.exec("PRAGMA optimize")}catch{}return ge}var ge,U=ue(()=>{"use strict";ji();ee();Fi();ge=null});import{existsSync as Um}from"node:fs";import{dirname as Vi}from"node:path";import{fileURLToPath as $m}from"node:url";function Zi(){if(Rs)return Rs;let e=Vi($m(import.meta.url));for(;!Um(`${e}/package.json`);){let t=Vi(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return Rs=e,Rs}var Rs,Qi=ue(()=>{"use strict";Rs=null});var ea,ta=ue(()=>{"use strict";ea="BAAI/bge-base-en-v1.5"});var na={};ys(na,{EmbedderUnavailableError:()=>Ht,embed:()=>ct,embedQuery:()=>or,getEmbedderStatus:()=>_e,loadEmbedder:()=>$e,unloadEmbedder:()=>Gm});import{Worker as Bm}from"node:worker_threads";import{join as Wm}from"node:path";import{existsSync as qm}from"node:fs";function Xm(){return Wm(Zi(),"dist","daemon","embedder-worker.js")}function sa(e){for(let t of $t.values())t.reject(e);$t.clear()}function Jm(){if(De)return De;let e=Xm();if(!qm(e))throw new Ht(new Error(`embedder-worker bundle not found at ${e}. Run \`npm run build:cli\` to emit it.`));let t=new Bm(e);return t.on("message",s=>{let n=$t.get(s.id);n&&($t.delete(s.id),n.resolve(s))}),t.on("error",s=>{console.error("[embedder-worker] thread error:",s);let n=s instanceof Error?s:new Error(String(s));sa(n),De=null,xe=!1}),t.on("exit",s=>{s!==0&&(console.error(`[embedder-worker] exited with code ${s}`),sa(new Error(`embedder worker exited with code ${s}`))),De=null,xe=!1}),De=t,t}function ks(e){return new Promise((t,s)=>{let n;try{n=Jm()}catch(r){s(r instanceof Error?r:new Error(String(r)));return}$t.set(e.id,{resolve:t,reject:s}),n.postMessage(e)})}function As(){return nr=nr+1>>>0,String(nr)}function rr(e){if(!e.ok)throw e.unavailable?new Ht(new Error(e.error)):new Error(e.error);return e}async function $e(){if(!(xe&&De))try{rr(await ks({id:As(),type:"load"})),xe=!0}catch(e){throw xe=!1,e}}function _e(){return{loaded:xe,modelId:ea,dim:768}}async function ct(e){if(!xe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return rr(await ks({id:As(),type:"embed",texts:e})).embeddings.map(s=>new Float32Array(s))}async function or(e){if(!xe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=rr(await ks({id:As(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function Gm(){if(!De){xe=!1;return}try{await ks({id:As(),type:"unload"})}catch{}xe=!1;let e=De;De=null;try{await e.terminate()}catch{}}var De,$t,nr,xe,Ht,Ve=ue(()=>{"use strict";Qi();ta();De=null,$t=new Map,nr=0,xe=!1,Ht=class extends Error{cause;constructor(t){let s=t instanceof Error?t.message:String(t);super(["Semantic search is unavailable on this platform.","",`Reason: ${s}`,"",`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) \u2014 or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
672
+ `)),this.name="EmbedderUnavailableError",this.cause=t}}});import{writeFileSync as Yg}from"node:fs";import{join as zg}from"node:path";function Qe(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function ut(e,t){let s=Qe(t);if(!s)throw new Error("tag must contain at least one alphanumeric character");let n=f(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?{tag:s,added:!1}:(n.transaction(()=>{n.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,s,r),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,s,r)})(),Aa(),{tag:s,added:!0})}function ka(e,t){let s=Qe(t);if(!s)return{tag:"",removed:!1};let n=f(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?(n.transaction(()=>{n.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,s),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,s,r)})(),Aa(),{tag:s,removed:!0}):{tag:s,removed:!1}}function Xt(e){return f().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function dt(){return f().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
673
+ GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function Aa(){try{z();let e=f(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),s=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),n={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:s};Yg(Kg,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var Kg,pt=ue(()=>{"use strict";U();ee();Kg=zg(B,"tags.json")});function Vg(e,t){let s=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(s.length<=t)return s;let n=new Set;n.add(0),n.add(s.length-1);let r=(s.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)n.add(Math.floor(o*r));return Array.from(n).sort((o,a)=>o-a).slice(0,t).map(o=>s[o])}function mt(e){let t=f(),s={limit:e.limit??500},n=e.sessionIds&&e.sessionIds.length>0,r=n?"1=1":"s.message_count > 2";if(n){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{s[`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",s.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",s.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
674
674
  NULLIF(sa.alias, '') AS alias,
675
675
  COALESCE(s.first_user_message, '') AS first_user_message
676
676
  FROM sessions s
@@ -680,15 +680,15 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
680
680
  ORDER BY COALESCE(s.started_at, '') DESC
681
681
  LIMIT @limit`).all(s).map(a=>{let c=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
682
682
  FROM messages WHERE session_id = ?
683
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=fg(c,5).map(m=>`${m.role}: ${m.content_text.slice(0,400)}`).join(`
683
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=Vg(c,5).map(m=>`${m.role}: ${m.content_text.slice(0,400)}`).join(`
684
684
  ---
685
- `);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:Mt(a.id)}})}var bs=ue(()=>{"use strict";H();Qe()});import{z as ge}from"zod";function er(e){let t=e.minTags??2,s=e.maxTags??4,n=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.`)):(n&&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}-${s} 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(`
686
- `)}function bg(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(`
687
- `)}function Tg(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(`
688
- `)}function wg(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(`
689
- `)}function aa(e){return tr.find(t=>t.name===e)}var hg,Eg,Sg,yg,Rg,Ag,kg,xg,tr,sr=ue(()=>{"use strict";hg={project:ge.string().optional().describe("Exact project name match (optional)."),collectionId:ge.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:ge.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:ge.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:ge.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:ge.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:ge.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};Eg={sessionId:ge.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:ge.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};Sg={sessionId:ge.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};yg={sessionId:ge.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:ge.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};Rg={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:hg,build:er,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Ag={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:Eg,build:bg,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},kg={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:Sg,build:Tg,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},xg={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:yg,build:wg,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},tr=[Rg,Ag,kg,xg]});function Ss(e,t){let s=Dt.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}function ca(e,t){let s=Dt.get(e);return s||(s=new Set,Dt.set(e,s)),s.add(t),()=>{let n=Dt.get(e);n&&(n.delete(t),n.size===0&&Dt.delete(e))}}var Dt,nr=ue(()=>{"use strict";Dt=new Map});var We={};ds(We,{buildScanPrompt:()=>la,isClaudeCliAvailable:()=>le,runClaudeCliScan:()=>rr,spawnClaudePrompt:()=>He});import{execFileSync as Ng,execSync as Og,spawn as Lg}from"node:child_process";function vg(){if(Ft)return Ft;try{Ft=Og("which claude",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{Ft="claude"}return Ft}function le(){try{return Ng("command",["-v","claude"],{stdio:"ignore"}),!0}catch{return!1}}function la(e){return er({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 Ig(e,t){let s=t.get(e);return s||e.slice(0,8)}function jg(e){try{return et(e).map(s=>({id:s.id,label:s.alias&&s.alias.trim().length>0?s.alias:s.first_user_message&&s.first_user_message.trim().length>0?s.first_user_message.slice(0,60):s.id.slice(0,8)}))}catch{return[]}}function Mg(e){let{scanId:t,total:s,labelTable:n}=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,h=typeof m?.sessionId=="string"?m.sessionId:null;!h||r.has(h)||(r.add(h),Ss(t,{type:"progress",current:r.size,total:s,sessionId:h,sessionLabel:Ig(h,n)}))}}}function Dg(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
685
+ `);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:Xt(a.id)}})}var Cs=ue(()=>{"use strict";U();pt()});import{z as be}from"zod";function _r(e){let t=e.minTags??2,s=e.maxTags??4,n=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.`)):(n&&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}-${s} 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(`
686
+ `)}function e_(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(`
687
+ `)}function s_(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(`
688
+ `)}function r_(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(`
689
+ `)}function xa(e){return fr.find(t=>t.name===e)}var Zg,Qg,t_,n_,o_,i_,a_,c_,fr,hr=ue(()=>{"use strict";Zg={project:be.string().optional().describe("Exact project name match (optional)."),collectionId:be.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:be.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:be.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:be.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:be.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:be.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};Qg={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:be.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};t_={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};n_={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:be.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};o_={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:Zg,build:_r,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},i_={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:Qg,build:e_,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},a_={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:t_,build:s_,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},c_={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:n_,build:r_,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},fr=[o_,i_,a_,c_]});function Is(e,t){let s=Jt.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}function Na(e,t){let s=Jt.get(e);return s||(s=new Set,Jt.set(e,s)),s.add(t),()=>{let n=Jt.get(e);n&&(n.delete(t),n.size===0&&Jt.delete(e))}}var Jt,Er=ue(()=>{"use strict";Jt=new Map});var tt={};ys(tt,{buildScanPrompt:()=>Oa,isClaudeCliAvailable:()=>pe,runClaudeCliScan:()=>br,spawnClaudePrompt:()=>et});import{execFileSync as l_,execSync as u_,spawn as d_}from"node:child_process";function m_(){if(Gt)return Gt;try{Gt=u_("which claude",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{Gt="claude"}return Gt}function pe(){try{return l_("command",["-v","claude"],{stdio:"ignore"}),!0}catch{return!1}}function Oa(e){return _r({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 g_(e,t){let s=t.get(e);return s||e.slice(0,8)}function __(e){try{return mt(e).map(s=>({id:s.id,label:s.alias&&s.alias.trim().length>0?s.alias:s.first_user_message&&s.first_user_message.trim().length>0?s.first_user_message.slice(0,60):s.id.slice(0,8)}))}catch{return[]}}function f_(e){let{scanId:t,total:s,labelTable:n}=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,h=typeof m?.sessionId=="string"?m.sessionId:null;!h||r.has(h)||(r.add(h),Is(t,{type:"progress",current:r.size,total:s,sessionId:h,sessionLabel:g_(h,n)}))}}}function h_(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
690
690
  `);for(;n!==-1;){let r=t.slice(0,n);t=t.slice(n+1),r.length>0&&e(r),n=t.indexOf(`
691
- `)}}}async function rr(e,t={},s){let n=!!t.scanId,r=n?jg(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return n&&t.scanId&&(c=Mg({scanId:t.scanId,total:a,labelTable:o})),ua({prompt:la(e),allowedTools:Cg.split(","),opts:t,onProgress:s,onStdoutLine:c,outputFormat:n?"stream-json":"json"})}async function He(e,t,s={},n){return ua({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function ua(e){let{prompt:t,allowedTools:s,opts:n,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",s.join(","),"--permission-mode","bypassPermissions"];return a==="stream-json"&&c.push("--verbose"),n.model&&c.push("--model",n.model),new Promise(u=>{let d=Lg(vg(),c,{stdio:["ignore","pipe","pipe"]}),m=[],h=[],b=o?Dg(o):void 0;d.stdout.on("data",S=>{m.push(S),b&&b(S)}),d.stderr.on("data",S=>{if(h.push(S),r){let w=S.toString("utf8").trim();w&&r(w)}});let T=setTimeout(()=>{d.kill("SIGKILL")},1800*1e3);d.on("close",S=>{clearTimeout(T),u({success:S===0,stdout:Buffer.concat(m).toString("utf8"),stderr:Buffer.concat(h).toString("utf8"),exitCode:S})}),d.on("error",S=>{clearTimeout(T),u({success:!1,stdout:"",stderr:String(S),exitCode:null})})})}var Cg,Ft,he=ue(()=>{"use strict";bs();sr();nr();Cg=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var xa={};ds(xa,{RetentionConfigSchema:()=>pr,readRetentionConfig:()=>mr,writeRetentionConfig:()=>Os});import{existsSync as ya,mkdirSync as w_,readFileSync as R_,writeFileSync as A_}from"node:fs";import{homedir as k_}from"node:os";import{join as wa}from"node:path";import{z as xs}from"zod";function Ra(){return process.env.RECALL_HOME??wa(k_(),".recall")}function x_(){let e=Ra();ya(e)||w_(e,{recursive:!0})}function Aa(){return wa(Ra(),"config.json")}function ka(){let e=Aa();if(!ya(e))return{};try{return JSON.parse(R_(e,"utf8"))}catch(t){let s=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${s}`),{}}}function mr(){let e=ka().retention;if(!e)return{...Ns};let t=pr.safeParse({...Ns,...e});return t.success?t.data:{...Ns}}function Os(e){x_();let t=ka(),s=pr.parse({...Ns,...t.retention??{},...e}),n={...t,retention:s};return A_(Aa(),JSON.stringify(n,null,2)),s}var pr,Ns,gr=ue(()=>{"use strict";pr=xs.object({autoArchiveEnabled:xs.boolean().default(!1),autoArchiveAfterDays:xs.number().int().min(7).max(3650).default(90),lastRunAt:xs.string().nullable().default(null)}),Ns={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Re from"chalk";import{formatDistanceToNowStrict as eR,parseISO as tR}from"date-fns";var Ee,Ls=ue(()=>{"use strict";Ee={dim:Re.gray,bold:Re.bold,project:Re.cyan,user:Re.blue,assistant:Re.green,tool:Re.magenta,warn:Re.yellow,err:Re.red,ok:Re.green,accent:Re.hex("#f97316")}});import{existsSync as N_}from"node:fs";import{join as O_}from"node:path";function Ut(){if(Na&&N_(Pt))return;J();let e=f(),t=Pt.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
691
+ `)}}}async function br(e,t={},s){let n=!!t.scanId,r=n?__(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return n&&t.scanId&&(c=f_({scanId:t.scanId,total:a,labelTable:o})),La({prompt:Oa(e),allowedTools:p_.split(","),opts:t,onProgress:s,onStdoutLine:c,outputFormat:n?"stream-json":"json"})}async function et(e,t,s={},n){return La({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function La(e){let{prompt:t,allowedTools:s,opts:n,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",s.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return a==="stream-json"&&c.push("--verbose"),n.model&&c.push("--model",n.model),new Promise(u=>{let d=d_(m_(),c,{stdio:["ignore","pipe","pipe"]}),m=[],h=[],b=o?h_(o):void 0;d.stdout.on("data",S=>{m.push(S),b&&b(S)}),d.stderr.on("data",S=>{if(h.push(S),r){let w=S.toString("utf8").trim();w&&r(w)}});let T=setTimeout(()=>{d.kill("SIGKILL")},1800*1e3);d.on("close",S=>{clearTimeout(T),u({success:S===0,stdout:Buffer.concat(m).toString("utf8"),stderr:Buffer.concat(h).toString("utf8"),exitCode:S})}),d.on("error",S=>{clearTimeout(T),u({success:!1,stdout:"",stderr:String(S),exitCode:null})})})}var p_,Gt,ye=ue(()=>{"use strict";Cs();hr();Er();p_=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var Ga={};ys(Ga,{RetentionConfigSchema:()=>xr,readRetentionConfig:()=>Nr,writeRetentionConfig:()=>Hs});import{existsSync as Ba,mkdirSync as rf,readFileSync as of,writeFileSync as af}from"node:fs";import{homedir as cf}from"node:os";import{join as Wa}from"node:path";import{z as Us}from"zod";function qa(){return process.env.RECALL_HOME??Wa(cf(),".recall")}function lf(){let e=qa();Ba(e)||rf(e,{recursive:!0})}function Xa(){return Wa(qa(),"config.json")}function Ja(){let e=Xa();if(!Ba(e))return{};try{return JSON.parse(of(e,"utf8"))}catch(t){let s=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${s}`),{}}}function Nr(){let e=Ja().retention;if(!e)return{...$s};let t=xr.safeParse({...$s,...e});return t.success?t.data:{...$s}}function Hs(e){lf();let t=Ja(),s=xr.parse({...$s,...t.retention??{},...e}),n={...t,retention:s};return af(Xa(),JSON.stringify(n,null,2)),s}var xr,$s,Or=ue(()=>{"use strict";xr=Us.object({autoArchiveEnabled:Us.boolean().default(!1),autoArchiveAfterDays:Us.number().int().min(7).max(3650).default(90),lastRunAt:Us.string().nullable().default(null)}),$s={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Oe from"chalk";import{formatDistanceToNowStrict as ZR,parseISO as QR}from"date-fns";var we,Bs=ue(()=>{"use strict";we={dim:Oe.gray,bold:Oe.bold,project:Oe.cyan,user:Oe.blue,assistant:Oe.green,tool:Oe.magenta,warn:Oe.yellow,err:Oe.red,ok:Oe.green,accent:Oe.hex("#f97316")}});import{existsSync as uf}from"node:fs";import{join as df}from"node:path";function zt(){if(Ya&&uf(Yt))return;z();let e=f(),t=Yt.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
692
692
  CREATE TABLE IF NOT EXISTS archive.messages_archive (
693
693
  uuid TEXT PRIMARY KEY,
694
694
  session_id TEXT NOT NULL,
@@ -703,12 +703,12 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
703
703
  archived_at TEXT NOT NULL DEFAULT (datetime('now'))
704
704
  );
705
705
  CREATE INDEX IF NOT EXISTS archive.idx_messages_archive_session ON messages_archive(session_id);
706
- `)}finally{e.exec("DETACH DATABASE archive")}Na=!0}function Oa(){let e=f();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;Ut();let s=Pt.replace(/'/g,"''"),n=0;e.exec(`ATTACH DATABASE '${s}' AS archive`);try{e.transaction(()=>{n=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
706
+ `)}finally{e.exec("DETACH DATABASE archive")}Ya=!0}function za(){let e=f();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;zt();let s=Yt.replace(/'/g,"''"),n=0;e.exec(`ATTACH DATABASE '${s}' AS archive`);try{e.transaction(()=>{n=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
707
707
  (uuid, session_id, parent_uuid, type, role, timestamp,
708
708
  is_sidechain, content_text, tool_names, raw_json, archived_at)
709
709
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
710
710
  is_sidechain, content_text, tool_names, raw_json, archived_at
711
- FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return n}var Pt,Na,_r=ue(()=>{"use strict";Q();H();Pt=O_(U,"archive.sqlite"),Na=!1});var Ca={};ds(Ca,{runArchive:()=>F_});function fr(e){Ut();let t=f(),s=Pt.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${s}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function L_(){return fr(e=>{let t=e.prepare(`SELECT
711
+ FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return n}var Yt,Ya,Lr=ue(()=>{"use strict";ee();U();Yt=df(B,"archive.sqlite"),Ya=!1});var Va={};ys(Va,{runArchive:()=>bf});function Cr(e){zt();let t=f(),s=Yt.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${s}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function pf(){return Cr(e=>{let t=e.prepare(`SELECT
712
712
  SUM(CASE WHEN archive_status = 'archived' THEN 1 ELSE 0 END) AS archived,
713
713
  SUM(CASE WHEN archive_status != 'archived' THEN 1 ELSE 0 END) AS live
714
714
  FROM sessions`).get(),s=e.prepare("SELECT COUNT(*) AS n FROM messages").get().n,n=e.prepare(`SELECT
@@ -717,15 +717,15 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
717
717
  SELECT MAX(timestamp) AS t FROM main.messages_archive WHERE timestamp IS NOT NULL
718
718
  UNION ALL
719
719
  SELECT MAX(timestamp) AS t FROM archive.messages_archive WHERE timestamp IS NOT NULL
720
- )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:s,archivedMessages:n,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function La(e){return e?e.slice(0,10):"\u2014"}function C_(){let e=L_();return console.log(Ee.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 ${La(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${La(e.newestArchivedTimestamp)}`),console.log(""),console.log(Ee.dim(" recall archive run --before YYYY-MM-DD")),console.log(Ee.dim(" recall archive restore <session-id>")),0}function v_(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let s=f().prepare(`SELECT s.id, s.ended_at, s.message_count
720
+ )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:s,archivedMessages:n,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function Ka(e){return e?e.slice(0,10):"\u2014"}function mf(){let e=pf();return console.log(we.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 ${Ka(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${Ka(e.newestArchivedTimestamp)}`),console.log(""),console.log(we.dim(" recall archive run --before YYYY-MM-DD")),console.log(we.dim(" recall archive restore <session-id>")),0}function gf(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let s=f().prepare(`SELECT s.id, s.ended_at, s.message_count
721
721
  FROM sessions s
722
722
  WHERE s.archive_status != 'archived'
723
- AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(s.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let n=s.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${s.length.toLocaleString()} session(s), ${n.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(Ee.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=s.map(c=>c.id),o=Date.now(),a=fr(c=>c.transaction(d=>{let m=0,h=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
723
+ AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(s.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let n=s.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${s.length.toLocaleString()} session(s), ${n.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(we.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=s.map(c=>c.id),o=Date.now(),a=Cr(c=>c.transaction(d=>{let m=0,h=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
724
724
  (uuid, session_id, parent_uuid, type, role, timestamp,
725
725
  is_sidechain, content_text, tool_names, raw_json, archived_at)
726
726
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
727
727
  is_sidechain, content_text, tool_names, raw_json, datetime('now')
728
- 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){h.run(S);let w=b.run(S);m+=Number(w.changes??0),T.run(S)}return m})(r));return console.log(`Archived ${s.length.toLocaleString()} session(s), moved ${a.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(Ee.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function I_(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let s=f().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!s)return console.error(`Session ${e} not found.`),1;if(s.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${s.archive_status}).`),1;let n=fr(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
728
+ 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){h.run(S);let w=b.run(S);m+=Number(w.changes??0),T.run(S)}return m})(r));return console.log(`Archived ${s.length.toLocaleString()} session(s), moved ${a.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(we.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function _f(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let s=f().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!s)return console.error(`Session ${e} not found.`),1;if(s.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${s.archive_status}).`),1;let n=Cr(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
729
729
  (uuid, session_id, parent_uuid, type, role, timestamp,
730
730
  is_sidechain, content_text, tool_names, raw_json)
731
731
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
@@ -735,32 +735,32 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
735
735
  is_sidechain, content_text, tool_names, raw_json)
736
736
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
737
737
  is_sidechain, content_text, tool_names, raw_json
738
- 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),h=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+h})());return console.log(`Restored ${n.toLocaleString()} message(s) for session ${e}.`),0}function j_(){let e=mr();return console.log(Ee.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?Ee.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function M_(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 s=Os({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${s.autoArchiveAfterDays} days).`),console.log(Ee.dim(" The daemon will run a daily archive pass on the next tick.")),0}function D_(){return Os({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function F_(e){let t=e._action??"list";if(t==="list"||t==="stats")return C_();if(t==="run")return e.before?v_({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return I_(e._sessionId??"");if(t==="auto"){let s=e._subAction??"status";return s==="status"?j_():s==="on"||s==="enable"?M_(e.after):s==="off"||s==="disable"?D_():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
738
+ 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),h=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+h})());return console.log(`Restored ${n.toLocaleString()} message(s) for session ${e}.`),0}function ff(){let e=Nr();return console.log(we.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?we.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function hf(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 s=Hs({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${s.autoArchiveAfterDays} days).`),console.log(we.dim(" The daemon will run a daily archive pass on the next tick.")),0}function Ef(){return Hs({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function bf(e){let t=e._action??"list";if(t==="list"||t==="stats")return mf();if(t==="run")return e.before?gf({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return _f(e._sessionId??"");if(t==="auto"){let s=e._subAction??"status";return s==="status"?ff():s==="on"||s==="enable"?hf(e.after):s==="off"||s==="disable"?Ef():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
739
739
  list \u2014 show archive counts
740
740
  run --before YYYY-MM-DD [--dry-run] \u2014 move sessions older than DATE
741
741
  restore <session-id> \u2014 pull a session back from archive
742
- auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var va=ue(()=>{"use strict";Ls();H();_r();gr()});import{Hono as HT}from"hono";import{serve as WT}from"@hono/node-server";Q();import{existsSync as Cp,readFileSync as vp,writeFileSync as Jy,unlinkSync as Yy}from"node:fs";import{join as Ip}from"node:path";var ri=Ip(U,"license.json");function kt(){if(!Cp(ri))return null;try{let e=vp(ri,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as jp,importSPKI as Mp}from"jose";var oi=`-----BEGIN PUBLIC KEY-----
742
+ auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var Za=ue(()=>{"use strict";Bs();U();Lr();Or()});import{cpus as gw}from"node:os";import{Hono as Oy}from"hono";import{serve as Ly}from"@hono/node-server";ee();import{existsSync as am,readFileSync as cm,writeFileSync as Dw,unlinkSync as Fw}from"node:fs";import{join as lm}from"node:path";var Ti=lm(B,"license.json");function Pt(){if(!am(Ti))return null;try{let e=cm(Ti,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as um,importSPKI as dm}from"jose";var yi=`-----BEGIN PUBLIC KEY-----
743
743
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
744
744
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
745
745
  -----END PUBLIC KEY-----
746
- `,Un="ES256",ii="clauderecall.com",ai="clauderecall-cli";var ps=null;async function Dp(){return ps||(ps=await Mp(oi,Un),ps)}async function ci(e){try{let t=await Dp(),{payload:s}=await jp(e,t,{issuer:ii,audience:ai,algorithms:[Un]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Fp}from"node:crypto";import{hostname as Pp,userInfo as Up,platform as $p,arch as Bp}from"node:os";function li(){let e="unknown";try{e=Up().username}catch{}let t=[Pp(),e,$p(),Bp()];return Fp("sha256").update(t.join("\0")).digest("hex")}Q();import{existsSync as Hp,readFileSync as Wp,writeFileSync as qp}from"node:fs";import{join as Xp}from"node:path";function ui(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),s;try{s=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let n=s.hostname==="127.0.0.1"||s.hostname==="localhost"||s.hostname==="::1";if(s.protocol==="https:"||s.protocol==="http:"&&n)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var $n=Xp(U,"license-check.json"),Jp=1440*60*1e3,Yp=720*60*60*1e3,Gp=1e4;function di(){if(!Hp($n))return null;try{let e=JSON.parse(Wp($n,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function zp(e){J(),qp($n,JSON.stringify(e,null,2)+`
747
- `,{mode:384})}async function Kp(e,t){let s=null,n=null;try{s=new AbortController,n=setTimeout(()=>s?.abort(),Gp);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:s.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{n&&clearTimeout(n)}}async function pi(e,t={}){let s=di(),n=t.apiUrl??`${ui()}/api/license/check`,r=s?.license_key===e,o=!s||!r||Date.now()-new Date(s.last_checked_at).getTime()>=Jp;if(!t.force&&!o)return s;let a=await Kp(e,n);if(!a)return r?s:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return zp(c),c}function mi(e){let t=di();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()>Yp?{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 Vp=Date.UTC(2026,5,1,7,0,0);var Zp=1440*60*1e3,pw=60*Zp;async function xt(){let e=kt();if(!e)return{tier:"free"};let t=await ci(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==li())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=mi(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:Qp(e,t.claims)}async function gi(e){let t=kt();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let s=await pi(t.license_key,{force:e?.force??!1});return s?{ran:!0,revoked:s.revoked,reason:s.reason,last_checked_at:s.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function Qp(e,t){let s=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:s?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...s?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function _i(){return(await xt()).tier==="pro"}H();H();import{createHash as mm}from"node:crypto";H();Q();import{writeFileSync as rm,readFileSync as Cw,existsSync as om,mkdirSync as im,readdirSync as vw,unlinkSync as Iw}from"node:fs";import{join as yi}from"node:path";import{randomUUID as wi}from"node:crypto";var Wn=yi(U,"bug-patterns");function am(){J(),om(Wn)||im(Wn,{recursive:!0})}function ve(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 Ri(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function Ai(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=f(),s=new Date().toISOString(),n=e.id??wi(),r=e.first_seen_at??s,o=e.last_seen_at??s,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
746
+ `,Zn="ES256",wi="clauderecall.com",Ri="clauderecall-cli";var ws=null;async function pm(){return ws||(ws=await dm(yi,Zn),ws)}async function ki(e){try{let t=await pm(),{payload:s}=await um(e,t,{issuer:wi,audience:Ri,algorithms:[Zn]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as mm}from"node:crypto";import{hostname as gm,userInfo as _m,platform as fm,arch as hm}from"node:os";function Ai(){let e="unknown";try{e=_m().username}catch{}let t=[gm(),e,fm(),hm()];return mm("sha256").update(t.join("\0")).digest("hex")}ee();import{existsSync as Em,readFileSync as bm,writeFileSync as Sm}from"node:fs";import{join as Tm}from"node:path";function xi(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),s;try{s=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let n=s.hostname==="127.0.0.1"||s.hostname==="localhost"||s.hostname==="::1";if(s.protocol==="https:"||s.protocol==="http:"&&n)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var Qn=Tm(B,"license-check.json"),ym=1440*60*1e3,wm=720*60*60*1e3,Rm=1e4;function Ni(){if(!Em(Qn))return null;try{let e=JSON.parse(bm(Qn,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function km(e){z(),Sm(Qn,JSON.stringify(e,null,2)+`
747
+ `,{mode:384})}async function Am(e,t){let s=null,n=null;try{s=new AbortController,n=setTimeout(()=>s?.abort(),Rm);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:s.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{n&&clearTimeout(n)}}async function Oi(e,t={}){let s=Ni(),n=t.apiUrl??`${xi()}/api/license/check`,r=s?.license_key===e,o=!s||!r||Date.now()-new Date(s.last_checked_at).getTime()>=ym;if(!t.force&&!o)return s;let a=await Am(e,n);if(!a)return r?s:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return km(c),c}function Li(e){let t=Ni();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()>wm?{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 xm=Date.UTC(2026,5,1,7,0,0);var Nm=1440*60*1e3,s0=60*Nm;async function Ke(){let e=Pt();if(!e)return{tier:"free"};let t=await ki(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Ai())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=Li(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:Om(e,t.claims)}async function Ci(e){let t=Pt();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let s=await Oi(t.license_key,{force:e?.force??!1});return s?{ran:!0,revoked:s.revoked,reason:s.reason,last_checked_at:s.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function Om(e,t){let s=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:s?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...s?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function Ii(){return(await Ke()).tier==="pro"}U();U();import{createHash as Ym}from"node:crypto";U();ee();import{writeFileSync as jm,readFileSync as T0,existsSync as Mm,mkdirSync as Dm,readdirSync as y0,unlinkSync as w0}from"node:fs";import{join as Ui}from"node:path";import{randomUUID as $i}from"node:crypto";var sr=Ui(B,"bug-patterns");function Fm(){z(),Mm(sr)||Dm(sr,{recursive:!0})}function Ue(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 Hi(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function Bi(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=f(),s=new Date().toISOString(),n=e.id??$i(),r=e.first_seen_at??s,o=e.last_seen_at??s,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
748
748
  (id, signature_hash, example_message, occurrence_count,
749
749
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
750
750
  VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(n,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)
751
751
  VALUES (?, ?, ?)
752
- ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(n,d,s)})();let c=ki(n);if(!c)throw new Error("createCluster succeeded but read-back failed");return Nt(n),c}function ki(e){let t=f(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:ve(s),members:n.map(Ri)}}function xi(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 s=f();if(!s.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;s.transaction(()=>{let c=s.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
752
+ ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(n,d,s)})();let c=Wi(n);if(!c)throw new Error("createCluster succeeded but read-back failed");return Ut(n),c}function Wi(e){let t=f(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:Ue(s),members:n.map(Hi)}}function qi(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 s=f();if(!s.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;s.transaction(()=>{let c=s.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
753
753
  VALUES (?, ?, ?)
754
754
  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=s.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;s.prepare(`UPDATE bug_pattern_clusters
755
755
  SET occurrence_count = ?, last_seen_at = ?
756
- WHERE id = ?`).run(u,r,e)}})(),o>0&&Nt(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:ve(a),added:o}}function Ni(e={}){let t=f(),s=[],n=[];typeof e.minOccurrenceCount=="number"&&(s.push("c.occurrence_count >= ?"),n.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&s.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(s.push(`EXISTS (
756
+ WHERE id = ?`).run(u,r,e)}})(),o>0&&Ut(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Ue(a),added:o}}function Xi(e={}){let t=f(),s=[],n=[];typeof e.minOccurrenceCount=="number"&&(s.push("c.occurrence_count >= ?"),n.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&s.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(s.push(`EXISTS (
757
757
  SELECT 1 FROM bug_pattern_members m
758
758
  JOIN sessions s ON s.id = m.session_id
759
759
  JOIN projects p ON p.id = s.project_id
760
760
  WHERE m.cluster_id = c.id AND p.name = ?
761
761
  )`),n.push(e.project));let r=s.length>0?`WHERE ${s.join(" AND ")}`:"",o=t.prepare(`SELECT COUNT(*) AS n FROM bug_pattern_clusters c ${r}`).get(...n),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}
762
762
  ORDER BY c.occurrence_count DESC, c.last_seen_at DESC
763
- LIMIT ? OFFSET ?`).all(...n,a,c).map(ve),total:o.n}}function cm(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,s=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:s,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function Oi(e){let t=f(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
763
+ LIMIT ? OFFSET ?`).all(...n,a,c).map(Ue),total:o.n}}function Pm(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,s=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:s,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function Ji(e){let t=f(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
764
764
  NULLIF(sa.alias, '') AS alias,
765
765
  s.auto_title,
766
766
  s.first_user_message,
@@ -771,31 +771,31 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
771
771
  LEFT JOIN session_aliases sa ON sa.session_id = m.session_id
772
772
  LEFT JOIN projects p ON p.id = s.project_id
773
773
  WHERE m.cluster_id = ?
774
- ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:ve(s),members:n.map(cm)}}function Li(e,t,s){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof s!="string"||!s.trim())throw new Error("fixSummary is required");let n=f();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!n.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}`);n.prepare(`UPDATE bug_pattern_clusters
774
+ ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:Ue(s),members:n.map(Pm)}}function Gi(e,t,s){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof s!="string"||!s.trim())throw new Error("fixSummary is required");let n=f();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!n.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}`);n.prepare(`UPDATE bug_pattern_clusters
775
775
  SET resolved_in_session_id = ?, fix_summary = ?
776
- WHERE id = ?`).run(t,s.trim(),e),Nt(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:ve(a)}}function Ci(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 s=f(),n=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=s.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
777
- 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=s.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=wi(),d=new Date().toISOString(),m=[];s.transaction(()=>{s.prepare(`INSERT INTO bug_pattern_clusters
776
+ WHERE id = ?`).run(t,s.trim(),e),Ut(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Ue(a)}}function Yi(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 s=f(),n=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=s.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
777
+ 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=s.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=$i(),d=new Date().toISOString(),m=[];s.transaction(()=>{s.prepare(`INSERT INTO bug_pattern_clusters
778
778
  (id, signature_hash, example_message, occurrence_count,
779
779
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
780
780
  VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(u,n.signature_hash,n.example_message,a.length,n.first_seen_at,d);let T=s.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
781
- VALUES (?, ?, ?)`),S=s.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let R of a)T.run(u,R.session_id,R.matched_at),S.run(e,R.session_id);let w=c-a.length;s.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(w,e),m=s.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),Nt(e),Nt(u);let h=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),b=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:ve(h),split:ve(b),movedMembers:m.map(Ri)}}function Nt(e){try{am();let t=ki(e);if(!t)return;let s=yi(Wn,`${e}.json`),n={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};rm(s,JSON.stringify(n,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function vi(e){return e?f().prepare(`SELECT * FROM bug_pattern_clusters
781
+ VALUES (?, ?, ?)`),S=s.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let R of a)T.run(u,R.session_id,R.matched_at),S.run(e,R.session_id);let w=c-a.length;s.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(w,e),m=s.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),Ut(e),Ut(u);let h=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),b=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:Ue(h),split:Ue(b),movedMembers:m.map(Hi)}}function Ut(e){try{Fm();let t=Wi(e);if(!t)return;let s=Ui(sr,`${e}.json`),n={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};jm(s,JSON.stringify(n,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function zi(e){return e?f().prepare(`SELECT * FROM bug_pattern_clusters
782
782
  WHERE signature_hash = ?
783
- ORDER BY last_seen_at DESC, id ASC`).all(e).map(ve):[]}function Ii(e){if(!e)return new Set;let s=f().prepare(`SELECT DISTINCT m.session_id
783
+ ORDER BY last_seen_at DESC, id ASC`).all(e).map(Ue):[]}function Ki(e){if(!e)return new Set;let s=f().prepare(`SELECT DISTINCT m.session_id
784
784
  FROM bug_pattern_members m
785
785
  JOIN bug_pattern_clusters c ON c.id = m.cluster_id
786
- WHERE c.signature_hash = ?`).all(e);return new Set(s.map(n=>n.session_id))}var gm=/\b0x[0-9a-fA-F]+\b/g,_m=/\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,fm=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,hm=/:\d+:\d+/g,Em=/\bline\s+\d+\b/gi,bm=/\bcolumn\s+\d+\b/gi,Sm=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,Tm=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,ym=/\b\d{4,}\b/g,wm=/(['"`])[^'"`\n]{1,128}\1/g;function Rm(e){if(!e)return"";let t=String(e);return t=t.replace(gm,"<hex>"),t=t.replace(_m,"<uuid>"),t=t.replace(fm,"<ts>"),t=t.replace(hm,":<line>:<col>"),t=t.replace(Em,"line <n>"),t=t.replace(bm,"column <n>"),t=t.replace(Sm,"pid <n>"),t=t.replace(Tm,"port <n>"),t=t.replace(ym,"<num>"),t=t.replace(wm,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function Am(e){let t=(e.error_type??"unknown").toLowerCase().trim(),s=Rm(e.snippet??e.message_hash??""),n=`${t}|${s}`;return mm("sha256").update(n).digest("hex").slice(0,16)}function km(e){let t=f(),s=["oi.bug_signatures IS NOT NULL"],n=[];e&&(s.push("p.name = ?"),n.push(e));let r=`WHERE ${s.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
786
+ WHERE c.signature_hash = ?`).all(e);return new Set(s.map(n=>n.session_id))}var zm=/\b0x[0-9a-fA-F]+\b/g,Km=/\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,Vm=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,Zm=/:\d+:\d+/g,Qm=/\bline\s+\d+\b/gi,eg=/\bcolumn\s+\d+\b/gi,tg=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,sg=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,ng=/\b\d{4,}\b/g,rg=/(['"`])[^'"`\n]{1,128}\1/g;function og(e){if(!e)return"";let t=String(e);return t=t.replace(zm,"<hex>"),t=t.replace(Km,"<uuid>"),t=t.replace(Vm,"<ts>"),t=t.replace(Zm,":<line>:<col>"),t=t.replace(Qm,"line <n>"),t=t.replace(eg,"column <n>"),t=t.replace(tg,"pid <n>"),t=t.replace(sg,"port <n>"),t=t.replace(ng,"<num>"),t=t.replace(rg,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function ig(e){let t=(e.error_type??"unknown").toLowerCase().trim(),s=og(e.snippet??e.message_hash??""),n=`${t}|${s}`;return Ym("sha256").update(n).digest("hex").slice(0,16)}function ag(e){let t=f(),s=["oi.bug_signatures IS NOT NULL"],n=[];e&&(s.push("p.name = ?"),n.push(e));let r=`WHERE ${s.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
787
787
  p.name AS project,
788
788
  s.started_at AS started_at,
789
789
  oi.bug_signatures AS bug_signatures
790
790
  FROM session_output_index oi
791
791
  LEFT JOIN sessions s ON s.id = oi.session_id
792
792
  LEFT JOIN projects p ON p.id = s.project_id
793
- ${r}`).all(...n),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 h=Am(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:h})}}return a}function xm(e){let t=new Map;for(let n of e){let r=t.get(n.fingerprint);r||(r=[],t.set(n.fingerprint,r)),r.push(n)}let s=[];for(let[n,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,h)=>{let b=m.started_at??"",T=h.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);s.push({fingerprint:n,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 s}function Nm(e,t){if(e.length!==t.length)return 0;let s=0,n=0,r=0;for(let a=0;a<e.length;a++)s+=e[a]*t[a],n+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(n)*Math.sqrt(r);return o>0?s/o:0}function Om(e){let{records:t,vectors:s,epsilon:n,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let m=0;m<o;m++){let h=[];for(let b=0;b<o;b++){if(m===b)continue;1-Nm(s[m],s[b])<=n&&h.push(b)}a.push(h)}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 h=a[m];if(h.length<r)continue;let b=d.length;d.push({members:[t[m]]}),u[m]=b;let T=[...h];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 Lm(e,t,s,n){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=Om({records:e,vectors:o,epsilon:s,minPts:n}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,m=[];for(let R of u.members)d.has(R.session_id)||(d.add(R.session_id),m.push(R));if(m.length===0)continue;let b=`sem:${[...m.map(R=>R.fingerprint)].sort()[0]}`,T=[...m].sort((R,D)=>{let O=R.started_at??"",Y=D.started_at??"";return O<Y?-1:O>Y?1:0}),S=T.find(R=>R.started_at),w=[...T].reverse().find(R=>R.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 Pi(e,t){let s={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let n of e){if(n.members.length<t)continue;let r=vi(n.fingerprint),o=Ii(n.fingerprint),a=n.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=Ai({signature_hash:n.fingerprint,example_message:n.example_message.slice(0,256),member_session_ids:n.members.map(m=>m.session_id),first_seen_at:n.first_seen_at,last_seen_at:n.last_seen_at});s.clusters_created+=1,s.members_added+=d.members.length,s.cluster_ids.push(d.cluster.id);continue}if(a.length===0){s.cluster_ids.push(r[0].id);continue}let c=r[0],u=xi(c.id,a);u.added>0&&(s.clusters_merged+=1,s.members_added+=u.added),s.cluster_ids.push(c.id)}return s}async function Ui(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),s=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),n=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=km(e.project),a=new Set(o.map(O=>O.session_id)),c=xm(o),u=[],d=!1;if(e.semantic){let O=e.embedder??null;if(!O)try{O=await Im()}catch(Y){let W=(Y instanceof Error?Y.message:String(Y)).split(`
794
- `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${W}
795
- Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(O){let Y=[];for(let M of c)M.members.length===1&&Y.push(M.members[0]);Y.length>=2&&(u=await Lm(Y,O,n,r))}}let m=c.filter(O=>O.members.length>=t),h=u.filter(O=>O.members.length>=t),b=Pi(m,t),T=Pi(h,t),S=[...b.cluster_ids,...T.cluster_ids],w=Array.from(new Set(S)),R=[];if(w.length>0){let O=f(),Y=w.map(()=>"?").join(","),M=O.prepare(`SELECT * FROM bug_pattern_clusters
796
- WHERE id IN (${Y})
793
+ ${r}`).all(...n),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 h=ig(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:h})}}return a}function cg(e){let t=new Map;for(let n of e){let r=t.get(n.fingerprint);r||(r=[],t.set(n.fingerprint,r)),r.push(n)}let s=[];for(let[n,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,h)=>{let b=m.started_at??"",T=h.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);s.push({fingerprint:n,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 s}function lg(e,t){if(e.length!==t.length)return 0;let s=0,n=0,r=0;for(let a=0;a<e.length;a++)s+=e[a]*t[a],n+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(n)*Math.sqrt(r);return o>0?s/o:0}function ug(e){let{records:t,vectors:s,epsilon:n,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let m=0;m<o;m++){let h=[];for(let b=0;b<o;b++){if(m===b)continue;1-lg(s[m],s[b])<=n&&h.push(b)}a.push(h)}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 h=a[m];if(h.length<r)continue;let b=d.length;d.push({members:[t[m]]}),u[m]=b;let T=[...h];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 dg(e,t,s,n){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=ug({records:e,vectors:o,epsilon:s,minPts:n}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,m=[];for(let R of u.members)d.has(R.session_id)||(d.add(R.session_id),m.push(R));if(m.length===0)continue;let b=`sem:${[...m.map(R=>R.fingerprint)].sort()[0]}`,T=[...m].sort((R,j)=>{let x=R.started_at??"",q=j.started_at??"";return x<q?-1:x>q?1:0}),S=T.find(R=>R.started_at),w=[...T].reverse().find(R=>R.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 ra(e,t){let s={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let n of e){if(n.members.length<t)continue;let r=zi(n.fingerprint),o=Ki(n.fingerprint),a=n.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=Bi({signature_hash:n.fingerprint,example_message:n.example_message.slice(0,256),member_session_ids:n.members.map(m=>m.session_id),first_seen_at:n.first_seen_at,last_seen_at:n.last_seen_at});s.clusters_created+=1,s.members_added+=d.members.length,s.cluster_ids.push(d.cluster.id);continue}if(a.length===0){s.cluster_ids.push(r[0].id);continue}let c=r[0],u=qi(c.id,a);u.added>0&&(s.clusters_merged+=1,s.members_added+=u.added),s.cluster_ids.push(c.id)}return s}async function oa(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),s=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),n=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=ag(e.project),a=new Set(o.map(x=>x.session_id)),c=cg(o),u=[],d=!1;if(e.semantic){let x=e.embedder??null;if(!x)try{x=await gg()}catch(q){let H=(q instanceof Error?q.message:String(q)).split(`
794
+ `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${H}
795
+ Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(x){let q=[];for(let D of c)D.members.length===1&&q.push(D.members[0]);q.length>=2&&(u=await dg(q,x,n,r))}}let m=c.filter(x=>x.members.length>=t),h=u.filter(x=>x.members.length>=t),b=ra(m,t),T=ra(h,t),S=[...b.cluster_ids,...T.cluster_ids],w=Array.from(new Set(S)),R=[];if(w.length>0){let x=f(),q=w.map(()=>"?").join(","),D=x.prepare(`SELECT * FROM bug_pattern_clusters
796
+ WHERE id IN (${q})
797
797
  ORDER BY occurrence_count DESC, last_seen_at DESC
798
- LIMIT ?`).all(...w,s);for(let W of M)R.push({id:W.id,signature_hash:W.signature_hash,example_message:W.example_message,occurrence_count:W.occurrence_count,first_seen_at:W.first_seen_at,last_seen_at:W.last_seen_at,resolved_in_session_id:W.resolved_in_session_id,fix_summary:W.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:m.length,semantic_groups:h.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:R}}var Cm=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:s}=await Promise.resolve().then(()=>(ze(),Fi));return s().loaded||await t(),e},vm=Cm;async function Im(){return vm()}H();H();Q();import{writeFileSync as $i,readFileSync as Jw,existsSync as Bi,mkdirSync as Hi,readdirSync as Yw}from"node:fs";import{join as _s}from"node:path";var jm=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),Wi=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),Mm=new Set(["pending","approved","rejected"]),Dm=new Set(["L1","L2","L3","L4","user"]),Xn=_s(U,"links"),Jn=_s(U,"suggestions"),Fm=_s(Jn,"index.json");function Pm(){J(),Bi(Xn)||Hi(Xn,{recursive:!0})}function Um(){J(),Bi(Jn)||Hi(Jn,{recursive:!0})}function qi(e){try{return JSON.parse(e)}catch{return e}}function fs(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:qi(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Yn(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:qi(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function Xi(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function Gn(e){if(!jm.has(e))throw new Error(`invalid link_type: ${e}`)}function $m(e){if(!Wi.has(e))throw new Error(`invalid source: ${e}`)}function Ji(e){if(!Dm.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Yi(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 Gi(e){Yi(e.source_session_id,e.target_session_id),Gn(e.link_type),$m(e.source),Xi(e.confidence);let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
798
+ LIMIT ?`).all(...w,s);for(let H of D)R.push({id:H.id,signature_hash:H.signature_hash,example_message:H.example_message,occurrence_count:H.occurrence_count,first_seen_at:H.first_seen_at,last_seen_at:H.last_seen_at,resolved_in_session_id:H.resolved_in_session_id,fix_summary:H.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:m.length,semantic_groups:h.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:R}}var pg=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:s}=await Promise.resolve().then(()=>(Ve(),na));return s().loaded||await t(),e},mg=pg;async function gg(){return mg()}U();U();ee();import{writeFileSync as ia,readFileSync as q0,existsSync as aa,mkdirSync as ca,readdirSync as X0}from"node:fs";import{join as xs}from"node:path";var _g=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),la=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),fg=new Set(["pending","approved","rejected"]),hg=new Set(["L1","L2","L3","L4","user"]),ir=xs(B,"links"),ar=xs(B,"suggestions"),Eg=xs(ar,"index.json");function bg(){z(),aa(ir)||ca(ir,{recursive:!0})}function Sg(){z(),aa(ar)||ca(ar,{recursive:!0})}function ua(e){try{return JSON.parse(e)}catch{return e}}function Ns(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:ua(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function cr(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:ua(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function da(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function lr(e){if(!_g.has(e))throw new Error(`invalid link_type: ${e}`)}function Tg(e){if(!la.has(e))throw new Error(`invalid source: ${e}`)}function pa(e){if(!hg.has(e))throw new Error(`invalid inferred_by: ${e}`)}function ma(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 ga(e){ma(e.source_session_id,e.target_session_id),lr(e.link_type),Tg(e.source),da(e.confidence);let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
799
799
  (source_session_id, target_session_id, link_type,
800
800
  confidence, source, evidence, approved,
801
801
  created_at, updated_at)
@@ -808,11 +808,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
808
808
  updated_at = excluded.updated_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,e.source,n,r,s,s);let o=t.prepare(`SELECT * FROM session_links
809
809
  WHERE source_session_id = ?
810
810
  AND target_session_id = ?
811
- 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 Kn(e.source_session_id),fs(o)}function hs(e={}){let t=f(),s=[],n=[];e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.linkType&&(Gn(e.linkType),s.push("link_type = ?"),n.push(e.linkType)),e.approvedOnly&&s.push("approved = 1");let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
811
+ 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 dr(e.source_session_id),Ns(o)}function Os(e={}){let t=f(),s=[],n=[];e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.linkType&&(lr(e.linkType),s.push("link_type = ?"),n.push(e.linkType)),e.approvedOnly&&s.push("approved = 1");let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
812
812
  ORDER BY confidence DESC, updated_at DESC
813
- LIMIT ?`).all(...n,o).map(fs)}function vt(e){return f().prepare(`SELECT * FROM session_links
813
+ LIMIT ?`).all(...n,o).map(Ns)}function Bt(e){return f().prepare(`SELECT * FROM session_links
814
814
  WHERE source_session_id = ? OR target_session_id = ?
815
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(fs)}function zi(e){let t=f(),s=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!s)return{removed:0,sourceSessionId:null};let n=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return n.changes>0&&Kn(s.source_session_id),{removed:n.changes,sourceSessionId:s.source_session_id}}function It(e){Yi(e.source_session_id,e.target_session_id),Gn(e.link_type),Xi(e.confidence),Ji(e.inferred_by);let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
815
+ ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(Ns)}function _a(e){let t=f(),s=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!s)return{removed:0,sourceSessionId:null};let n=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return n.changes>0&&dr(s.source_session_id),{removed:n.changes,sourceSessionId:s.source_session_id}}function Wt(e){ma(e.source_session_id,e.target_session_id),lr(e.link_type),da(e.confidence),pa(e.inferred_by);let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
816
816
  (source_session_id, target_session_id, link_type,
817
817
  confidence, evidence, status, inferred_by,
818
818
  created_at, decided_at)
@@ -832,9 +832,9 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
832
832
  WHERE source_session_id = ?
833
833
  AND target_session_id = ?
834
834
  AND link_type = ?
835
- AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!r)throw new Error("createSuggestion succeeded but read-back failed");return Ki(),Yn(r)}function Ke(e={}){let t=f(),s=[],n=[];if(e.status){if(!Mm.has(e.status))throw new Error(`invalid status: ${e.status}`);s.push("status = ?"),n.push(e.status)}e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.inferredBy&&(Ji(e.inferredBy),s.push("inferred_by = ?"),n.push(e.inferredBy));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
835
+ AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!r)throw new Error("createSuggestion succeeded but read-back failed");return fa(),cr(r)}function lt(e={}){let t=f(),s=[],n=[];if(e.status){if(!fg.has(e.status))throw new Error(`invalid status: ${e.status}`);s.push("status = ?"),n.push(e.status)}e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.inferredBy&&(pa(e.inferredBy),s.push("inferred_by = ?"),n.push(e.inferredBy));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
836
836
  ORDER BY confidence DESC, created_at DESC
837
- LIMIT ?`).all(...n,o).map(Yn)}function zn(e,t,s={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let n=s.source??"manual";if(!Wi.has(n))throw new Error(`invalid source: ${n}`);let r=f(),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
837
+ LIMIT ?`).all(...n,o).map(cr)}function ur(e,t,s={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let n=s.source??"manual";if(!la.has(n))throw new Error(`invalid source: ${n}`);let r=f(),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
838
838
  SET status = ?, decided_at = ?
839
839
  WHERE id = ?`).run(t,a,e),t==="approved"&&(r.prepare(`INSERT INTO session_links
840
840
  (source_session_id, target_session_id, link_type,
@@ -849,7 +849,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
849
849
  updated_at = excluded.updated_at`).run(o.source_session_id,o.target_session_id,o.link_type,o.confidence,n,o.evidence,a,a),c=r.prepare(`SELECT * FROM session_links
850
850
  WHERE source_session_id = ?
851
851
  AND target_session_id = ?
852
- AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),Ki(),t==="approved"&&Kn(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:Yn(u),link:c?fs(c):null}}function Kn(e){try{Pm();let t=hs({sourceSessionId:e}),s=_s(Xn,`${e}.json`);if(t.length===0)return;let n={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};$i(s,JSON.stringify(n,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function Ki(){try{Um();let e=Ke({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};$i(Fm,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}H();Q();import{writeFileSync as Bm,readFileSync as Qw,existsSync as Hm,mkdirSync as Wm,readdirSync as e0}from"node:fs";import{join as Vi}from"node:path";var Vn=Vi(U,"output-index");function qm(){J(),Hm(Vn)||Wm(Vn,{recursive:!0})}function jt(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Xm(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function Zi(e){return{session_id:e.session_id,files_written:jt(e.files_written),brands_mentioned:jt(e.brands_mentioned),terms_introduced:jt(e.terms_introduced),plan_ids_referenced:jt(e.plan_ids_referenced),bug_signatures:jt(e.bug_signatures),raw_extraction:Xm(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function Qi(e){if(!e.session_id)throw new Error("session_id is required");let t=f(),s=new Date().toISOString(),n=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
852
+ AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),fa(),t==="approved"&&dr(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:cr(u),link:c?Ns(c):null}}function dr(e){try{bg();let t=Os({sourceSessionId:e}),s=xs(ir,`${e}.json`);if(t.length===0)return;let n={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};ia(s,JSON.stringify(n,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function fa(){try{Sg();let e=lt({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};ia(Eg,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}U();ee();import{writeFileSync as yg,readFileSync as V0,existsSync as wg,mkdirSync as Rg,readdirSync as Z0}from"node:fs";import{join as ha}from"node:path";var pr=ha(B,"output-index");function kg(){z(),wg(pr)||Rg(pr,{recursive:!0})}function qt(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Ag(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function Ea(e){return{session_id:e.session_id,files_written:qt(e.files_written),brands_mentioned:qt(e.brands_mentioned),terms_introduced:qt(e.terms_introduced),plan_ids_referenced:qt(e.plan_ids_referenced),bug_signatures:qt(e.bug_signatures),raw_extraction:Ag(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function ba(e){if(!e.session_id)throw new Error("session_id is required");let t=f(),s=new Date().toISOString(),n=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
853
853
  (session_id, files_written, brands_mentioned, terms_introduced,
854
854
  plan_ids_referenced, bug_signatures, raw_extraction,
855
855
  extracted_at, extractor_version)
@@ -862,11 +862,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
862
862
  bug_signatures = excluded.bug_signatures,
863
863
  raw_extraction = excluded.raw_extraction,
864
864
  extracted_at = excluded.extracted_at,
865
- extractor_version = excluded.extractor_version`).run(e.session_id,n,r,o,a,c,u,s,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 h=Zi(m);return Jm(e.session_id),h}function $e(e){let s=f().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return s?Zi(s):null}function Jm(e){try{qm();let t=$e(e);if(!t)return;let s=Vi(Vn,`${e}.json`),n={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};Bm(s,JSON.stringify(n,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var Zn={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},Ym=2,Gm=.25,zm=5,Km=60,Vm=25;function Es(e){return e.trim().toLowerCase()}function Zm(e){let t=new Set;for(let s of e.files_written)t.add(`file:${Es(s)}`);for(let s of e.brands_mentioned)t.add(`brand:${Es(s)}`);for(let s of e.terms_introduced)t.add(`term:${Es(s)}`);for(let s of e.plan_ids_referenced)t.add(`plan:${Es(s)}`);return t}function Qm(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Km);return Math.max(.2,t)}function eg(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function tg(e){let t=$e(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(s=>s.term),plan_ids_referenced:t.plan_ids_referenced}:null}function sg(e){return f().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
865
+ extractor_version = excluded.extractor_version`).run(e.session_id,n,r,o,a,c,u,s,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 h=Ea(m);return xg(e.session_id),h}function Ze(e){let s=f().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return s?Ea(s):null}function xg(e){try{kg();let t=Ze(e);if(!t)return;let s=ha(pr,`${e}.json`),n={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};yg(s,JSON.stringify(n,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var mr={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},Ng=2,Og=.25,Lg=5,Cg=60,Ig=25;function Ls(e){return e.trim().toLowerCase()}function vg(e){let t=new Set;for(let s of e.files_written)t.add(`file:${Ls(s)}`);for(let s of e.brands_mentioned)t.add(`brand:${Ls(s)}`);for(let s of e.terms_introduced)t.add(`term:${Ls(s)}`);for(let s of e.plan_ids_referenced)t.add(`plan:${Ls(s)}`);return t}function jg(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Cg);return Math.max(.2,t)}function Mg(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function Dg(e){let t=Ze(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(s=>s.term),plan_ids_referenced:t.plan_ids_referenced}:null}function Fg(e){return f().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
866
866
  FROM sessions s
867
867
  JOIN session_output_index oi ON oi.session_id = s.id
868
868
  WHERE s.project_id = ?
869
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function ng(e,t){let s=new Map,n=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=Zm(a);if(c.size!==0){n.set(o.id,c);for(let u of c){let d=s.get(u);d?d.push(o.id):s.set(u,[o.id])}}}return{posting:s,vocab:n,startedAt:r}}function rg(e,t){let s=t.vocab.get(e),n=t.startedAt.get(e)??null;if(!s||!n)return[];let r=new Map;for(let a of s){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>=n)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<Ym)continue;let d=t.startedAt.get(a)??null,m=eg(n,d),h=Qm(m),b=Math.min(1,u/zm*h);if(b<Gm)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(h*1e3)/1e3,confidence:Math.round(b*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,Vm)}async function ea(e){if(Zn.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=sg(e.projectId),s=new Map;for(let a of t){let c=tg(a.id);c&&s.set(a.id,c)}let n=ng(t,s),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 a of t){if(e.signal?.aborted)break;r.current_session_id=a.id,e.onProgress?.({...r});let c=rg(a.id,n);for(let u of c)try{let d=It({source_session_id:a.id,target_session_id:u.target_session_id,link_type:"citation",confidence:u.confidence,evidence:{matched_terms:u.matched_terms,overlap_count:u.overlap,recency:u.recency,days_apart:u.days_apart},inferred_by:"L2"});o.push(d.id),r.suggestions_created+=1}catch(d){console.error("[citation-inference] createSuggestion failed:",d)}r.processed_sessions+=1,e.onProgress?.({...r})}return r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:o}}H();var og=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,ig=[/\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],ag=.95,cg=.85;var Qn=50;function lg(e){if(!e)return[];let t=new Set,s=[],n=e.match(og);if(!n)return s;for(let r of n){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),s.push(o),s.length>=Qn))break}return s}function ug(e){if(!e)return[];let t=new Set,s=[];for(let n of ig){n.lastIndex=0;let r=e.match(n);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),s.push(a),s.length>=Qn))break}if(s.length>=Qn)break}}return s}function ta(e){let t=f();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 sa(e){let t=f();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
869
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function Pg(e,t){let s=new Map,n=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=vg(a);if(c.size!==0){n.set(o.id,c);for(let u of c){let d=s.get(u);d?d.push(o.id):s.set(u,[o.id])}}}return{posting:s,vocab:n,startedAt:r}}function Ug(e,t){let s=t.vocab.get(e),n=t.startedAt.get(e)??null;if(!s||!n)return[];let r=new Map;for(let a of s){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>=n)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<Ng)continue;let d=t.startedAt.get(a)??null,m=Mg(n,d),h=jg(m),b=Math.min(1,u/Lg*h);if(b<Og)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(h*1e3)/1e3,confidence:Math.round(b*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,Ig)}async function Sa(e){if(mr.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=Fg(e.projectId),s=new Map;for(let a of t){let c=Dg(a.id);c&&s.set(a.id,c)}let n=Pg(t,s),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 a of t){if(e.signal?.aborted)break;r.current_session_id=a.id,e.onProgress?.({...r});let c=Ug(a.id,n);for(let u of c)try{let d=Wt({source_session_id:a.id,target_session_id:u.target_session_id,link_type:"citation",confidence:u.confidence,evidence:{matched_terms:u.matched_terms,overlap_count:u.overlap,recency:u.recency,days_apart:u.days_apart},inferred_by:"L2"});o.push(d.id),r.suggestions_created+=1}catch(d){console.error("[citation-inference] createSuggestion failed:",d)}r.processed_sessions+=1,e.onProgress?.({...r})}return r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:o}}U();var $g=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,Hg=[/\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],Bg=.95,Wg=.85;var gr=50;function qg(e){if(!e)return[];let t=new Set,s=[],n=e.match($g);if(!n)return s;for(let r of n){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),s.push(o),s.length>=gr))break}return s}function Xg(e){if(!e)return[];let t=new Set,s=[];for(let n of Hg){n.lastIndex=0;let r=e.match(n);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),s.push(a),s.length>=gr))break}if(s.length>=gr)break}}return s}function Ta(e){let t=f();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 ya(e){let t=f();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
870
870
  s.project_id
871
871
  FROM messages m
872
872
  JOIN sessions s ON s.id = m.session_id
@@ -879,13 +879,13 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
879
879
  JOIN sessions s ON s.id = m.session_id
880
880
  WHERE m.is_sidechain = 0
881
881
  AND m.content_text IS NOT NULL
882
- AND length(m.content_text) > 0`).all()}function dg(e){let t=f(),s=typeof e=="number"?"WHERE s.project_id = ?":"",n=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
882
+ AND length(m.content_text) > 0`).all()}function Jg(e){let t=f(),s=typeof e=="number"?"WHERE s.project_id = ?":"",n=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
883
883
  s.project_id AS project_id,
884
884
  oi.plan_ids_referenced AS plan_ids_json
885
885
  FROM session_output_index oi
886
886
  JOIN sessions s ON s.id = oi.session_id
887
- ${s}`).all(...n)}function pg(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>typeof s=="string")}catch{}return[]}function na(e={}){if(Zn.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=ta(e.projectId),s=new Set(t.map(o=>o.id)),n=new Map;for(let o of t)n.set(o.id,o.project_id);let r=0;for(let o of sa(e.projectId)){if(e.signal?.aborted)break;let a=lg(o.content_text);if(a.length===0)continue;let c=n.get(o.session_id);if(c!==void 0)for(let u of a){if(u===o.session_id)continue;let d=n.get(u);if(!(d===void 0&&!s.has(u))&&!(d!==void 0&&c!==void 0&&d!==c))try{It({source_session_id:o.session_id,target_session_id:u,link_type:"citation",confidence:ag,evidence:{matched_uuid:u,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function ra(e={}){let t=dg(e.projectId);if(t.length===0)return{created:0};let s=new Map;for(let c of t){let u=pg(c.plan_ids_json);for(let d of u){let m=d.trim().toLowerCase();if(!m)continue;let h=s.get(m);h?h.push({id:c.session_id,project_id:c.project_id}):s.set(m,[{id:c.session_id,project_id:c.project_id}])}}if(s.size===0)return{created:0};let n=ta(e.projectId),r=new Map;for(let c of n)r.set(c.id,c.project_id);let o=0,a=new Map;for(let c of sa(e.projectId)){if(e.signal?.aborted)break;let u=ug(c.content_text);if(u.length===0)continue;let d=r.get(c.session_id);if(d!==void 0)for(let m of u){let h=s.get(m);if(h)for(let b of h){if(b.id===c.session_id||b.project_id!==d)continue;let T=a.get(c.session_id);T||(T=new Map,a.set(c.session_id,T));let S=T.get(b.id);S||(S=new Set,T.set(b.id,S)),S.add(m)}}}for(let[c,u]of a)for(let[d,m]of u)try{It({source_session_id:c,target_session_id:d,link_type:"citation",confidence:cg,evidence:{matched_plan_ids:Array.from(m).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}H();he();import{existsSync as Hg,mkdirSync as Wg,writeFileSync as qg}from"node:fs";import{homedir as Xg}from"node:os";import{join as ir}from"node:path";H();import{existsSync as da,mkdirSync as Fg,readFileSync as Pg,writeFileSync as Ug}from"node:fs";import{homedir as $g}from"node:os";import{join as pa}from"node:path";import{z as Oe}from"zod";function ma(){return process.env.RECALL_HOME??pa($g(),".recall")}function Bg(){let e=ma();da(e)||Fg(e,{recursive:!0})}function ga(){return pa(ma(),"config.json")}var ys=Oe.object({enabled:Oe.boolean().default(!1),model:Oe.string().optional(),ratePerMinute:Oe.number().int().min(1).max(600).default(30),lastProcessedSessionId:Oe.string().nullable().default(null),backfillPaused:Oe.boolean().default(!1),autoExtractEnabled:Oe.boolean().default(!1),autoExtractIntervalMinutes:Oe.number().int().min(5).max(720).default(60),autoExtractBatchSize:Oe.number().int().min(1).max(20).default(1)}),Ts={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1};function _a(){let e=ga();if(!da(e))return{};try{return JSON.parse(Pg(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ae(){let e=_a().semantic;if(!e)return{...Ts};let t=ys.safeParse({...Ts,...e});return t.success?t.data:{...Ts}}function ws(e){Bg();let t=_a(),s=ys.parse({...Ts,...t.semantic??{},...e}),n={...t,semantic:s};return Ug(ga(),JSON.stringify(n,null,2)),or(s.enabled),s}function or(e){try{f().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
888
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(e?"1":"0")}catch(t){let s=t instanceof Error?t.message:String(t);console.error(`[semantic-config] failed to sync semantic_enabled: ${s}`)}}var Jg=1,Yg=12e3,fa=3,Gg=[];function zg(){return process.env.RECALL_HOME??ir(Xg(),".recall")}function ha(){return ir(zg(),"semantic")}function Kg(){let e=ha();Hg(e)||Wg(e,{recursive:!0})}function Vg(e){let t=f(),s=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
887
+ ${s}`).all(...n)}function Gg(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>typeof s=="string")}catch{}return[]}function wa(e={}){if(mr.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Ta(e.projectId),s=new Set(t.map(o=>o.id)),n=new Map;for(let o of t)n.set(o.id,o.project_id);let r=0;for(let o of ya(e.projectId)){if(e.signal?.aborted)break;let a=qg(o.content_text);if(a.length===0)continue;let c=n.get(o.session_id);if(c!==void 0)for(let u of a){if(u===o.session_id)continue;let d=n.get(u);if(!(d===void 0&&!s.has(u))&&!(d!==void 0&&c!==void 0&&d!==c))try{Wt({source_session_id:o.session_id,target_session_id:u,link_type:"citation",confidence:Bg,evidence:{matched_uuid:u,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function Ra(e={}){let t=Jg(e.projectId);if(t.length===0)return{created:0};let s=new Map;for(let c of t){let u=Gg(c.plan_ids_json);for(let d of u){let m=d.trim().toLowerCase();if(!m)continue;let h=s.get(m);h?h.push({id:c.session_id,project_id:c.project_id}):s.set(m,[{id:c.session_id,project_id:c.project_id}])}}if(s.size===0)return{created:0};let n=Ta(e.projectId),r=new Map;for(let c of n)r.set(c.id,c.project_id);let o=0,a=new Map;for(let c of ya(e.projectId)){if(e.signal?.aborted)break;let u=Xg(c.content_text);if(u.length===0)continue;let d=r.get(c.session_id);if(d!==void 0)for(let m of u){let h=s.get(m);if(h)for(let b of h){if(b.id===c.session_id||b.project_id!==d)continue;let T=a.get(c.session_id);T||(T=new Map,a.set(c.session_id,T));let S=T.get(b.id);S||(S=new Set,T.set(b.id,S)),S.add(m)}}}for(let[c,u]of a)for(let[d,m]of u)try{Wt({source_session_id:c,target_session_id:d,link_type:"citation",confidence:Wg,evidence:{matched_plan_ids:Array.from(m).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}U();ye();import{existsSync as w_,mkdirSync as R_,writeFileSync as k_}from"node:fs";import{homedir as A_}from"node:os";import{join as Tr}from"node:path";U();import{existsSync as Ca,mkdirSync as E_,readFileSync as b_,writeFileSync as S_}from"node:fs";import{homedir as T_}from"node:os";import{join as Ia}from"node:path";import{z as Ne}from"zod";function va(){return process.env.RECALL_HOME??Ia(T_(),".recall")}function y_(){let e=va();Ca(e)||E_(e,{recursive:!0})}function ja(){return Ia(va(),"config.json")}var js=Ne.object({enabled:Ne.boolean().default(!1),model:Ne.string().optional(),ratePerMinute:Ne.number().int().min(1).max(600).default(30),lastProcessedSessionId:Ne.string().nullable().default(null),backfillPaused:Ne.boolean().default(!1),autoExtractEnabled:Ne.boolean().default(!1),autoExtractIntervalMinutes:Ne.number().int().min(5).max(720).default(60),autoExtractBatchSize:Ne.number().int().min(1).max(20).default(1),autoResumeWorker:Ne.boolean().default(!1)}),vs={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function Ma(){let e=ja();if(!Ca(e))return{};try{return JSON.parse(b_(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ae(){let e=Ma().semantic;if(!e)return{...vs};let t=js.safeParse({...vs,...e});return t.success?t.data:{...vs}}function Ms(e){y_();let t=Ma(),s=js.parse({...vs,...t.semantic??{},...e}),n={...t,semantic:s};return S_(ja(),JSON.stringify(n,null,2)),Sr(s.enabled),s}function Sr(e){try{f().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
888
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(e?"1":"0")}catch(t){let s=t instanceof Error?t.message:String(t);console.error(`[semantic-config] failed to sync semantic_enabled: ${s}`)}}var x_=1,N_=12e3,Da=3,O_=[];function L_(){return process.env.RECALL_HOME??Tr(A_(),".recall")}function Fa(){return Tr(L_(),"semantic")}function C_(){let e=Fa();w_(e)||R_(e,{recursive:!0})}function I_(e){let t=f(),s=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
889
889
  p.name AS project,
890
890
  NULLIF(sa.alias, '') AS alias
891
891
  FROM sessions s
@@ -894,10 +894,10 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
894
894
  WHERE s.id = ?`).get(e);if(!s)return null;let n=t.prepare(`SELECT role, content_text
895
895
  FROM messages
896
896
  WHERE session_id = ? AND is_sidechain = 0
897
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of n){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>Yg)break;r.push(m),o+=m.length}return{id:s.id,alias:s.alias,project:s.project,firstUserMessage:s.first_user_message,excerpt:r.join(`
897
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of n){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>N_)break;r.push(m),o+=m.length}return{id:s.id,alias:s.alias,project:s.project,firstUserMessage:s.first_user_message,excerpt:r.join(`
898
898
 
899
- `),messageCount:s.message_count}}function Zg(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(`
900
- `)}function Qg(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 s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+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 e_(e){let t=f(),s=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
899
+ `),messageCount:s.message_count}}function v_(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(`
900
+ `)}function j_(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 s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+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 M_(e){let t=f(),s=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
901
901
  (session_id, summary, keywords, model, source_message_count, generated_at)
902
902
  VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
903
903
  ON CONFLICT(session_id) DO UPDATE SET
@@ -905,16 +905,16 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
905
905
  keywords = excluded.keywords,
906
906
  model = excluded.model,
907
907
  source_message_count = excluded.source_message_count,
908
- generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:s,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),Kg();let n=ir(ha(),`${e.sessionId}.json`);qg(n,JSON.stringify({version:Jg,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 Rs=null;function t_(){let t=ae().ratePerMinute,s=t/6e4;return(!Rs||Rs.capacity!==t)&&(Rs={tokens:t,capacity:t,refillPerMs:s,lastRefill:Date.now()}),Rs}function s_(e){let t=Date.now(),s=t-e.lastRefill;s>0&&(e.tokens=Math.min(e.capacity,e.tokens+s*e.refillPerMs),e.lastRefill=t)}async function n_(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=t_();if(s_(t),t.tokens>=1){t.tokens-=1;return}let s=1-t.tokens,n=Math.max(50,Math.ceil(s/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(n,5e3)))}}async function As(e,t={}){let s=ae();if(!s.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!le())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let n=Vg(e);if(!n)return{sessionId:e,ok:!1,reason:"session-not-found"};if(n.messageCount<fa)return{sessionId:e,ok:!1,reason:"too-short"};if(!n.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await n_(t.signal);let r=Zg(n),o=await He(r,Gg,{model:s.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:s.model??null};let a=Qg(o.stdout);return a?(e_({sessionId:n.id,summary:a.summary,keywords:a.keywords,model:s.model??null,sourceMessageCount:n.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:s.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:s.model??null}}async function ks(e={}){let t=ae();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let s=f(),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=s.prepare(`SELECT s.id
908
+ generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:s,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),C_();let n=Tr(Fa(),`${e.sessionId}.json`);k_(n,JSON.stringify({version:x_,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 Ds=null;function D_(){let t=ae().ratePerMinute,s=t/6e4;return(!Ds||Ds.capacity!==t)&&(Ds={tokens:t,capacity:t,refillPerMs:s,lastRefill:Date.now()}),Ds}function F_(e){let t=Date.now(),s=t-e.lastRefill;s>0&&(e.tokens=Math.min(e.capacity,e.tokens+s*e.refillPerMs),e.lastRefill=t)}async function P_(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=D_();if(F_(t),t.tokens>=1){t.tokens-=1;return}let s=1-t.tokens,n=Math.max(50,Math.ceil(s/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(n,5e3)))}}async function Fs(e,t={}){let s=ae();if(!s.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!pe())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let n=I_(e);if(!n)return{sessionId:e,ok:!1,reason:"session-not-found"};if(n.messageCount<Da)return{sessionId:e,ok:!1,reason:"too-short"};if(!n.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await P_(t.signal);let r=v_(n),o=await et(r,O_,{model:s.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:s.model??null};let a=j_(o.stdout);return a?(M_({sessionId:n.id,summary:a.summary,keywords:a.keywords,model:s.model??null,sourceMessageCount:n.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:s.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:s.model??null}}async function Ps(e={}){let t=ae();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let s=f(),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=s.prepare(`SELECT s.id
909
909
  FROM sessions s
910
910
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
911
911
  WHERE ${o}
912
912
  ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
913
- 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 As(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,ws({lastProcessedSessionId:u}),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function Ea(e){if(!ae().enabled)return;let n=f().prepare(`SELECT s.message_count, s.ended_at,
913
+ 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 Fs(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,Ms({lastProcessedSessionId:u}),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function Pa(e){if(!ae().enabled)return;let n=f().prepare(`SELECT s.message_count, s.ended_at,
914
914
  ss.generated_at, ss.source_message_count
915
915
  FROM sessions s
916
916
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
917
- WHERE s.id = ?`).get(e);if(n&&!(n.message_count<fa)&&!(n.generated_at&&n.source_message_count!=null&&n.source_message_count>=n.message_count))try{await As(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function ar(){let e=ae(),t=f(),s=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,n=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:le(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:s,processedSessions:n,pendingSessions:Math.max(0,s-n),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}H();import{createHash as a_}from"node:crypto";var r_=[{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"}];function o_(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))),s=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-s.length))}${s}`}function i_(e){let t=5381;for(let s=0;s<e.length;s++)t=(t<<5)+t+e.charCodeAt(s)|0;return(t>>>0).toString(36)}function _e(e){if(!e)return{redacted:e,count:0};let t=e,s=0,n=new Set;for(let r of r_)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${i_(o)}`;return n.has(a)||(n.add(a),s+=1),`[REDACTED ${r.name}: ${o_(o)}]`});return{redacted:t,count:s}}he();var ur=1,qe="claude-haiku-4-5-20251001",c_=3,l_=32e3,ba=2e3,u_=30,d_=30,p_=30,m_=30;function g_(e){let s=f().prepare(`SELECT s.id,
917
+ WHERE s.id = ?`).get(e);if(n&&!(n.message_count<Da)&&!(n.generated_at&&n.source_message_count!=null&&n.source_message_count>=n.message_count))try{await Fs(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function yr(){let e=ae(),t=f(),s=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,n=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:pe(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:s,processedSessions:n,pendingSessions:Math.max(0,s-n),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}U();import{createHash as B_}from"node:crypto";var U_=[{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"}];function $_(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))),s=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-s.length))}${s}`}function H_(e){let t=5381;for(let s=0;s<e.length;s++)t=(t<<5)+t+e.charCodeAt(s)|0;return(t>>>0).toString(36)}function Se(e){if(!e)return{redacted:e,count:0};let t=e,s=0,n=new Set;for(let r of U_)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${H_(o)}`;return n.has(a)||(n.add(a),s+=1),`[REDACTED ${r.name}: ${$_(o)}]`});return{redacted:t,count:s}}ye();var kr=1,st="claude-haiku-4-5-20251001",W_=3,q_=32e3,Ua=2e3,X_=30,J_=30,G_=30,Y_=30;function z_(e){let s=f().prepare(`SELECT s.id,
918
918
  NULLIF(sa.alias, '') AS alias,
919
919
  s.auto_title,
920
920
  s.auto_title_source,
@@ -925,15 +925,15 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
925
925
  FROM sessions s
926
926
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
927
927
  LEFT JOIN projects p ON p.id = s.project_id
928
- WHERE s.id = ?`).get(e);return s?{...s,alias_source:s.alias?"manual":null}:null}function Sa(e,t={}){if(e.message_count<c_)return{eligible:!1,reason:"too-short"};let s=e.title_quality;if(s==="programmatic"||s==="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 n=$e(e.id);if(n&&n.extractor_version>=ur)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function __(e){let t=g_(e);if(!t)return null;let n=f().prepare(`SELECT role, content_text
928
+ WHERE s.id = ?`).get(e);return s?{...s,alias_source:s.alias?"manual":null}:null}function $a(e,t={}){if(e.message_count<W_)return{eligible:!1,reason:"too-short"};let s=e.title_quality;if(s==="programmatic"||s==="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 n=Ze(e.id);if(n&&n.extractor_version>=kr)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function K_(e){let t=z_(e);if(!t)return null;let n=f().prepare(`SELECT role, content_text
929
929
  FROM messages
930
930
  WHERE session_id = ?
931
931
  AND is_sidechain = 0
932
932
  AND content_text IS NOT NULL
933
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of n){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>ba?u.slice(0,ba)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>l_)break;r.push(m),o+=m.length}return{meta:t,excerpt:r.join(`
933
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of n){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>Ua?u.slice(0,Ua)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>q_)break;r.push(m),o+=m.length}return{meta:t,excerpt:r.join(`
934
934
 
935
- `)}}function f_(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: "Glaser 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(`
936
- `)}function h_(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function cr(e,t,s=!1){if(!Array.isArray(e))return[];let n=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=s?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!n.has(a)&&(n.add(a),r.push(a),r.length>=t))break}return r}function E_(e,t){if(!Array.isArray(e))return[];let s=new Set,n=[];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||s.has(c))continue;s.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(n.push({term:c,frequency:u}),n.length>=t)break}return n}function b_(e,t){if(!Array.isArray(e))return[];let s=[];for(let n of e){if(!n||typeof n!="object")continue;let r=n.error_type,o=n.snippet,a=n.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=a_("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(s.push({error_type:c,message_hash:m,snippet:u,file:d}),s.length>=t)break}return s}function S_(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 s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+1),o;try{o=JSON.parse(r)}catch{return null}let a=cr(o.files_written,200),c=cr(o.brands_mentioned,d_),u=E_(o.terms_introduced,u_),d=cr(o.plan_ids_referenced,p_),m=b_(o.bug_signatures,m_);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&m.length===0&&!h_(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:m}}var lr=null;async function T_(e,t){return lr?lr(e,t):He(e,[],{model:t})}async function dr(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let s=__(e);if(!s)return{session_id:e,ok:!1,skipped:"session-not-found"};let n=Sa(s.meta,{force:t.force});if(!n.eligible)return{session_id:e,ok:!1,skipped:n.reason};if(!lr&&!le())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=f_(s),o=t.model??qe,a=await T_(r,o);if(!a.success){let m=(a.stderr||a.stdout||"").slice(0,400),h=m?_e(m).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:h}}let c=S_(a.stdout);if(!c){let m=a.stdout.slice(0,400),h=m?_e(m).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:h}}let u=y_(a.stdout),d=Qi({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:ur});return{session_id:e,ok:!0,index:d,usage:u}}function y_(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let s=t.usage,n={};return typeof s.input_tokens=="number"&&(n.input_tokens=s.input_tokens),typeof s.output_tokens=="number"&&(n.output_tokens=s.output_tokens),n}}catch{}return null}function tt(e={}){let t=f(),s=[],n=[];typeof e.projectId=="number"&&(s.push("s.project_id = ?"),n.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=s.length?`WHERE ${s.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
935
+ `)}}function V_(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: "Glaser 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(`
936
+ `)}function Z_(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function wr(e,t,s=!1){if(!Array.isArray(e))return[];let n=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=s?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!n.has(a)&&(n.add(a),r.push(a),r.length>=t))break}return r}function Q_(e,t){if(!Array.isArray(e))return[];let s=new Set,n=[];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||s.has(c))continue;s.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(n.push({term:c,frequency:u}),n.length>=t)break}return n}function ef(e,t){if(!Array.isArray(e))return[];let s=[];for(let n of e){if(!n||typeof n!="object")continue;let r=n.error_type,o=n.snippet,a=n.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=B_("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(s.push({error_type:c,message_hash:m,snippet:u,file:d}),s.length>=t)break}return s}function tf(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 s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+1),o;try{o=JSON.parse(r)}catch{return null}let a=wr(o.files_written,200),c=wr(o.brands_mentioned,J_),u=Q_(o.terms_introduced,X_),d=wr(o.plan_ids_referenced,G_),m=ef(o.bug_signatures,Y_);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&m.length===0&&!Z_(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:m}}var Rr=null;async function sf(e,t){return Rr?Rr(e,t):et(e,[],{model:t})}async function Ar(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let s=K_(e);if(!s)return{session_id:e,ok:!1,skipped:"session-not-found"};let n=$a(s.meta,{force:t.force});if(!n.eligible)return{session_id:e,ok:!1,skipped:n.reason};if(!Rr&&!pe())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=V_(s),o=t.model??st,a=await sf(r,o);if(!a.success){let m=(a.stderr||a.stdout||"").slice(0,400),h=m?Se(m).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:h}}let c=tf(a.stdout);if(!c){let m=a.stdout.slice(0,400),h=m?Se(m).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:h}}let u=nf(a.stdout),d=ba({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:kr});return{session_id:e,ok:!0,index:d,usage:u}}function nf(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let s=t.usage,n={};return typeof s.input_tokens=="number"&&(n.input_tokens=s.input_tokens),typeof s.output_tokens=="number"&&(n.output_tokens=s.output_tokens),n}}catch{}return null}function gt(e={}){let t=f(),s=[],n=[];typeof e.projectId=="number"&&(s.push("s.project_id = ?"),n.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=s.length?`WHERE ${s.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
937
937
  NULLIF(sa.alias, '') AS alias,
938
938
  s.auto_title,
939
939
  s.auto_title_source,
@@ -945,7 +945,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
945
945
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
946
946
  LEFT JOIN projects p ON p.id = s.project_id
947
947
  ${o}
948
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(...n),c=[],u=new Map;for(let d of a){let m={...d,alias_source:d.alias?"manual":null},h=Sa(m,{force:e.force});if(!h.eligible){let b=h.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 Ta(e={}){let t=tt({projectId:e.projectId,limit:e.limit,force:e.force}),s={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())s.skipped+=r;e.onProgress?.({...s});let n=[];for(let r of t.eligible){if(e.signal?.aborted)break;s.current_session_id=r.id,e.onProgress?.({...s});let o=await dr(r.id,{model:e.model,force:e.force,signal:e.signal});n.push(o),e.onResult?.(o),o.ok?s.ok+=1:o.skipped?s.skipped+=1:s.failed+=1,o.usage?.input_tokens&&(s.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(s.total_output_tokens+=o.usage.output_tokens),s.processed+=1,e.onProgress?.({...s})}return s.current_session_id=null,e.onProgress?.({...s}),{progress:s,results:n}}he();var te="[daemon:inference]",hr=!1,Er=!1,br=!1,Sr=!1,Tr=!1,Ia=0,yr=!1,wr=!1,Cs=3,Xe=0,st=!1,vs=null,nt=null,ja=!1;function P_(){return f().prepare("SELECT id, name FROM projects").all()}async function Ma(){if(hr)return;hr=!0;let e=Date.now();try{let s=(await Ui({minClusterSize:2})).progress;(s.clusters_created||s.clusters_merged||s.members_added)&&console.log(`${te} bug-patterns: created=${s.clusters_created} merged=${s.clusters_merged} members_added=${s.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${te} bug-patterns failed:`,t)}finally{hr=!1}}async function Da(){if(Er)return;Er=!0;let e=Date.now();try{let t=0,s=0;for(let n of P_())try{let r=await ea({projectId:n.id});t+=r.progress.suggestions_created,s+=1}catch(r){console.error(`${te} citations failed for project "${n.name}":`,r)}t>0&&console.log(`${te} citations: ${t} suggestion(s) across ${s} project(s) (${Date.now()-e}ms)`)}catch(t){console.error(`${te} citations failed:`,t)}finally{Er=!1}}function Fa(){if(br)return;br=!0;let e=Date.now();try{let t=na({}),s=ra({});t.created+s.created>0&&console.log(`${te} l1: uuid=${t.created} plan=${s.created} (${Date.now()-e}ms)`)}catch(t){console.error(`${te} l1 failed:`,t)}finally{br=!1}}async function Pa(){if(Sr)return;let e=ae();if(!e.enabled||e.backfillPaused)return;Sr=!0;let t=Date.now();try{let s=await ks({limit:25});s.processed>0&&console.log(`${te} backfill: processed=${s.processed} ok=${s.ok} failed=${s.failed} (${Date.now()-t}ms)`)}catch(s){console.error(`${te} backfill failed:`,s)}finally{Sr=!1}}async function Ua(){if(Tr)return;let e=ae();if(e.autoExtractEnabled&&!ja&&st&&(U_(),console.log(`${te} auto-extract: circuit breaker reset (config toggled on).`)),ja=e.autoExtractEnabled,!e.autoExtractEnabled||st)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-Ia<t)return;if(!le()){yr||(console.log(`${te} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),yr=!0);return}if(yr=!1,!await _i().catch(()=>!1)){wr||(console.log(`${te} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),wr=!0);return}wr=!1,Tr=!0,Ia=Date.now();let n=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=tt({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,m=new Map,h=null;for(let S of o){let w=await dr(S.id,{model:qe});if(w.ok)a+=1;else if(!w.skipped){c+=1;let R=w.failed??"unknown";m.set(R,(m.get(R)??0)+1),!h&&w.stderr_excerpt&&(h=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(`${te} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-n}ms)${b}`),h){let w=_e(h).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${te} auto-extract: first failure excerpt: ${w}`)}o.length>0&&u===0&&d===0?(Xe+=1,Xe>=Cs&&(st=!0,vs=Date.now(),nt=`${Cs} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${te} auto-extract: CIRCUIT BREAKER tripped \u2014 ${nt}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(Xe=0)}catch(r){console.error(`${te} auto-extract failed:`,r),Xe+=1,Xe>=Cs&&(st=!0,vs=Date.now(),nt=`${Cs} consecutive thrown errors`,console.log(`${te} auto-extract: CIRCUIT BREAKER tripped \u2014 ${nt}. Pausing nibbler.`))}finally{Tr=!1}}function Ba(){return{broken:st,brokenAt:vs,reason:nt,consecutiveZeroTokenRuns:Xe}}function U_(){st=!1,vs=null,nt=null,Xe=0}var Rr=!1;async function $a(){if(Rr)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(gr(),xa)),s=e();if(!s.autoArchiveEnabled)return;let n=1380*60*1e3;if(s.lastRunAt&&Date.now()-Date.parse(s.lastRunAt)<n)return;Rr=!0;let r=Date.now();try{let a=new Date(Date.now()-s.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(va(),Ca)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${te} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${te} auto-archive failed: ${a}`)}finally{Rr=!1}}function Ha(){let m=[],h=[];m.push(setTimeout(()=>{Ma()},9e4)),h.push(setInterval(()=>{Ma()},18e5)),m.push(setTimeout(()=>{Da()},18e4)),h.push(setInterval(()=>{Da()},36e5)),m.push(setTimeout(()=>Fa(),12e4)),h.push(setInterval(()=>Fa(),18e5)),m.push(setTimeout(()=>{Pa()},24e4)),h.push(setInterval(()=>{Pa()},9e5)),m.push(setTimeout(()=>{Ua()},3e5)),h.push(setInterval(()=>{Ua()},9e5));let b=3600*1e3,T=300*1e3;return m.push(setTimeout(()=>{$a()},T)),h.push(setInterval(()=>{$a()},b)),console.log(`${te} 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:h,stop:()=>{for(let S of m)clearTimeout(S);for(let S of h)clearInterval(S)}}}H();import{serveStatic as op}from"@hono/node-server/serve-static";import{existsSync as qT,readFileSync as Yo}from"node:fs";import{stat as XT,readFile as JT,realpath as YT}from"node:fs/promises";import{timingSafeEqual as GT}from"node:crypto";import{homedir as zT}from"node:os";import{dirname as Ko,join as Mn}from"node:path";import{fileURLToPath as Vo}from"node:url";import{existsSync as RR,readFileSync as AR,statSync as kR,statfsSync as $_}from"node:fs";Ls();H();Q();var B_=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],LR=new RegExp(`^(${B_.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var CR=5*6e4;function Ar(){try{let e=$_(U);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function Wa(e){let{projects:t,sessions:s,messages:n,port:r,version:o}=e;return`<!DOCTYPE html>
948
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(...n),c=[],u=new Map;for(let d of a){let m={...d,alias_source:d.alias?"manual":null},h=$a(m,{force:e.force});if(!h.eligible){let b=h.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 Ha(e={}){let t=gt({projectId:e.projectId,limit:e.limit,force:e.force}),s={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())s.skipped+=r;e.onProgress?.({...s});let n=[];for(let r of t.eligible){if(e.signal?.aborted)break;s.current_session_id=r.id,e.onProgress?.({...s});let o=await Ar(r.id,{model:e.model,force:e.force,signal:e.signal});n.push(o),e.onResult?.(o),o.ok?s.ok+=1:o.skipped?s.skipped+=1:s.failed+=1,o.usage?.input_tokens&&(s.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(s.total_output_tokens+=o.usage.output_tokens),s.processed+=1,e.onProgress?.({...s})}return s.current_session_id=null,e.onProgress?.({...s}),{progress:s,results:n}}ye();var te="[daemon:inference]",Ir=!1,vr=!1,jr=!1,Mr=!1,Dr=!1,Qa=0,Fr=!1,Pr=!1,Ws=3,nt=0,_t=!1,qs=null,ft=null,ec=!1;function Sf(){return f().prepare("SELECT id, name FROM projects").all()}async function tc(){if(Ir)return;Ir=!0;let e=Date.now();try{let s=(await oa({minClusterSize:2})).progress;(s.clusters_created||s.clusters_merged||s.members_added)&&console.log(`${te} bug-patterns: created=${s.clusters_created} merged=${s.clusters_merged} members_added=${s.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${te} bug-patterns failed:`,t)}finally{Ir=!1}}async function sc(){if(vr)return;vr=!0;let e=Date.now();try{let t=0,s=0;for(let n of Sf())try{let r=await Sa({projectId:n.id});t+=r.progress.suggestions_created,s+=1}catch(r){console.error(`${te} citations failed for project "${n.name}":`,r)}t>0&&console.log(`${te} citations: ${t} suggestion(s) across ${s} project(s) (${Date.now()-e}ms)`)}catch(t){console.error(`${te} citations failed:`,t)}finally{vr=!1}}function nc(){if(jr)return;jr=!0;let e=Date.now();try{let t=wa({}),s=Ra({});t.created+s.created>0&&console.log(`${te} l1: uuid=${t.created} plan=${s.created} (${Date.now()-e}ms)`)}catch(t){console.error(`${te} l1 failed:`,t)}finally{jr=!1}}async function rc(){if(Mr)return;let e=ae();if(!e.enabled||e.backfillPaused)return;Mr=!0;let t=Date.now();try{let s=await Ps({limit:25});s.processed>0&&console.log(`${te} backfill: processed=${s.processed} ok=${s.ok} failed=${s.failed} (${Date.now()-t}ms)`)}catch(s){console.error(`${te} backfill failed:`,s)}finally{Mr=!1}}async function oc(){if(Dr)return;let e=ae();if(e.autoExtractEnabled&&!ec&&_t&&(Tf(),console.log(`${te} auto-extract: circuit breaker reset (config toggled on).`)),ec=e.autoExtractEnabled,!e.autoExtractEnabled||_t)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-Qa<t)return;if(!pe()){Fr||(console.log(`${te} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),Fr=!0);return}if(Fr=!1,!await Ii().catch(()=>!1)){Pr||(console.log(`${te} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),Pr=!0);return}Pr=!1,Dr=!0,Qa=Date.now();let n=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=gt({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,m=new Map,h=null;for(let S of o){let w=await Ar(S.id,{model:st});if(w.ok)a+=1;else if(!w.skipped){c+=1;let R=w.failed??"unknown";m.set(R,(m.get(R)??0)+1),!h&&w.stderr_excerpt&&(h=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(`${te} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-n}ms)${b}`),h){let w=Se(h).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${te} auto-extract: first failure excerpt: ${w}`)}o.length>0&&u===0&&d===0?(nt+=1,nt>=Ws&&(_t=!0,qs=Date.now(),ft=`${Ws} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${te} auto-extract: CIRCUIT BREAKER tripped \u2014 ${ft}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(nt=0)}catch(r){console.error(`${te} auto-extract failed:`,r),nt+=1,nt>=Ws&&(_t=!0,qs=Date.now(),ft=`${Ws} consecutive thrown errors`,console.log(`${te} auto-extract: CIRCUIT BREAKER tripped \u2014 ${ft}. Pausing nibbler.`))}finally{Dr=!1}}function ac(){return{broken:_t,brokenAt:qs,reason:ft,consecutiveZeroTokenRuns:nt}}function Tf(){_t=!1,qs=null,ft=null,nt=0}var Ur=!1;async function ic(){if(Ur)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(Or(),Ga)),s=e();if(!s.autoArchiveEnabled)return;let n=1380*60*1e3;if(s.lastRunAt&&Date.now()-Date.parse(s.lastRunAt)<n)return;Ur=!0;let r=Date.now();try{let a=new Date(Date.now()-s.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(Za(),Va)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${te} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${te} auto-archive failed: ${a}`)}finally{Ur=!1}}function cc(){let m=[],h=[];m.push(setTimeout(()=>{tc()},9e4)),h.push(setInterval(()=>{tc()},18e5)),m.push(setTimeout(()=>{sc()},18e4)),h.push(setInterval(()=>{sc()},36e5)),m.push(setTimeout(()=>nc(),12e4)),h.push(setInterval(()=>nc(),18e5)),m.push(setTimeout(()=>{rc()},24e4)),h.push(setInterval(()=>{rc()},9e5)),m.push(setTimeout(()=>{oc()},3e5)),h.push(setInterval(()=>{oc()},9e5));let b=3600*1e3,T=300*1e3;return m.push(setTimeout(()=>{ic()},T)),h.push(setInterval(()=>{ic()},b)),console.log(`${te} 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:h,stop:()=>{for(let S of m)clearTimeout(S);for(let S of h)clearInterval(S)}}}U();import{serveStatic as vp}from"@hono/node-server/serve-static";import{existsSync as Cy,readFileSync as li}from"node:fs";import{stat as Iy,readFile as vy,realpath as jy}from"node:fs/promises";import{timingSafeEqual as My}from"node:crypto";import{homedir as Dy}from"node:os";import{dirname as pi,join as Yn}from"node:path";import{fileURLToPath as mi}from"node:url";import{existsSync as yk,readFileSync as wk,statSync as Rk,statfsSync as yf}from"node:fs";Bs();U();ee();var wf=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],Nk=new RegExp(`^(${wf.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var Ok=5*6e4;function $r(){try{let e=yf(B);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function lc(e){let{projects:t,sessions:s,messages:n,port:r,version:o}=e;return`<!DOCTYPE html>
949
949
  <html lang="en">
950
950
  <head>
951
951
  <meta charset="utf-8" />
@@ -1000,71 +1000,71 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1000
1000
  </footer>
1001
1001
  </main>
1002
1002
  </body>
1003
- </html>`}var H_=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,W_=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,q_=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function X_(e){return e.replace(H_,"").trim()}function J_(e){let t=e.replace(W_,"[tool call]");return t=t.replace(q_,"[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,`
1003
+ </html>`}var Rf=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,kf=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,Af=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function xf(e){return e.replace(Rf,"").trim()}function Nf(e){let t=e.replace(kf,"[tool call]");return t=t.replace(Af,"[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,`
1004
1004
 
1005
- `),t.trim()}function Y_(e){return e.role??e.type??"message"}function qa(e,t,s={}){let n=s.mode??"condensed",r=s.includeSidechain===!0,o=s.since?Date.parse(s.since):0,a=t.filter(m=>!(!r&&m.is_sidechain===1||o&&m.timestamp&&Date.parse(m.timestamp)<o)),c=[];s.prelude&&(c.push(s.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${n})`),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 h=m.content_text??"",b=X_(h);n==="condensed"&&(b=J_(b));let T=b.length>0,S=!!m.tool_names&&m.tool_names.length>0;if(!T&&!S){d+=1;continue}let w=Y_(m),R=m.timestamp?` \`${m.timestamp}\``:"";c.push(`## ${w}${R}`),c.push(""),S&&n==="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(`
1006
- `)}H();Q();import{writeFileSync as G_}from"node:fs";import{join as z_}from"node:path";var K_=z_(U,"aliases.json");function kr(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Ae(e){return f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function V_(){return f().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:kr(t.previous_aliases)}))}function be(e,t){let s=t.trim();if(!s)throw new Error("alias must be non-empty");let n=f(),r=new Date().toISOString(),o=n.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=kr(o.previous_aliases),o.alias!==s&&a.push({alias:o.alias,replaced_at:r})),n.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
1005
+ `),t.trim()}function Of(e){return e.role??e.type??"message"}function uc(e,t,s={}){let n=s.mode??"condensed",r=s.includeSidechain===!0,o=s.since?Date.parse(s.since):0,a=t.filter(m=>!(!r&&m.is_sidechain===1||o&&m.timestamp&&Date.parse(m.timestamp)<o)),c=[];s.prelude&&(c.push(s.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${n})`),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 h=m.content_text??"",b=xf(h);n==="condensed"&&(b=Nf(b));let T=b.length>0,S=!!m.tool_names&&m.tool_names.length>0;if(!T&&!S){d+=1;continue}let w=Of(m),R=m.timestamp?` \`${m.timestamp}\``:"";c.push(`## ${w}${R}`),c.push(""),S&&n==="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(`
1006
+ `)}U();ee();import{writeFileSync as Lf}from"node:fs";import{join as Cf}from"node:path";var If=Cf(B,"aliases.json");function Hr(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Te(e){return f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function vf(){return f().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:Hr(t.previous_aliases)}))}function he(e,t){let s=t.trim();if(!s)throw new Error("alias must be non-empty");let n=f(),r=new Date().toISOString(),o=n.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=Hr(o.previous_aliases),o.alias!==s&&a.push({alias:o.alias,replaced_at:r})),n.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
1007
1007
  VALUES (?, ?, ?, ?)
1008
1008
  ON CONFLICT(session_id) DO UPDATE SET
1009
1009
  alias = excluded.alias,
1010
1010
  updated_at = excluded.updated_at,
1011
- previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(a)),Xa(),{session_id:e,alias:s,updated_at:r,previous_aliases:a}}function Is(e){let t=f(),s=new Date().toISOString(),n=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!n)return;let r=kr(n.previous_aliases);r.push({alias:n.alias,replaced_at:s}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
1012
- WHERE session_id = ?`).run(s,JSON.stringify(r),e),Xa()}function Xa(){try{J();let e=V_(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};G_(K_,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}function $t(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),s=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(s)?s-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}H();Q();import{writeFileSync as Z_,mkdirSync as Q_,existsSync as ef}from"node:fs";import{join as Ya}from"node:path";var xr=Ya(U,"notes"),Ja=200,tf=12e3,sf=800,nf=8e3;function Ga(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function za(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(s=>!!s&&typeof s=="object"&&typeof s.synopsis=="string"&&typeof s.replaced_at=="string"):[]}catch{return[]}}function rf(){J(),ef(xr)||Q_(xr,{recursive:!0})}function of(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:Ga(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:za(e.auto_synopsis_history)}}var af="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function js(e){let t=f().prepare(`SELECT ${af} FROM session_notes WHERE session_id = ?`).get(e);return t?of(t):null}function Ka(e,t){let s=f(),n=new Date().toISOString(),r=s.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=Ga(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:n})),s.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1011
+ previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(a)),dc(),{session_id:e,alias:s,updated_at:r,previous_aliases:a}}function Xs(e){let t=f(),s=new Date().toISOString(),n=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!n)return;let r=Hr(n.previous_aliases);r.push({alias:n.alias,replaced_at:s}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
1012
+ WHERE session_id = ?`).run(s,JSON.stringify(r),e),dc()}function dc(){try{z();let e=vf(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};Lf(If,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}function Kt(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),s=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(s)?s-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();ee();import{writeFileSync as jf,mkdirSync as Mf,existsSync as Df}from"node:fs";import{join as mc}from"node:path";var Br=mc(B,"notes"),pc=200,Ff=12e3,Pf=800,Uf=8e3;function gc(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function _c(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(s=>!!s&&typeof s=="object"&&typeof s.synopsis=="string"&&typeof s.replaced_at=="string"):[]}catch{return[]}}function $f(){z(),Df(Br)||Mf(Br,{recursive:!0})}function Hf(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:gc(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:_c(e.auto_synopsis_history)}}var Bf="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function Js(e){let t=f().prepare(`SELECT ${Bf} FROM session_notes WHERE session_id = ?`).get(e);return t?Hf(t):null}function fc(e,t){let s=f(),n=new Date().toISOString(),r=s.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=gc(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:n})),s.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1013
1013
  VALUES (?, ?, ?, ?)
1014
1014
  ON CONFLICT(session_id) DO UPDATE SET
1015
1015
  content = excluded.content,
1016
1016
  updated_at = excluded.updated_at,
1017
- previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(o)),lf(e,t,n),js(e)??{session_id:e,content:t,updated_at:n,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function Va(e){let s=f().prepare(`SELECT rowid AS rid, role, content_text
1017
+ previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(o)),qf(e,t,n),Js(e)??{session_id:e,content:t,updated_at:n,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function hc(e){let s=f().prepare(`SELECT rowid AS rid, role, content_text
1018
1018
  FROM messages
1019
1019
  WHERE session_id = ? AND is_sidechain = 0
1020
1020
  AND content_text IS NOT NULL AND content_text != ''
1021
1021
  AND role IN ('user', 'assistant')
1022
1022
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1023
- LIMIT ?`).all(e,Ja);if(s.length===0)throw new Error("no messages available to summarise");let n=tf,r=[];for(let b of s){if(n<=0)break;let T=(b.content_text??"").slice(0,sf);r.push({rid:b.rid,role:b.role,content:T}),n-=T.length}r.reverse();let o=s.length===Ja||n<=0,a=r.map(b=>`**${b.role}**: ${b.content}`).join(`
1023
+ LIMIT ?`).all(e,pc);if(s.length===0)throw new Error("no messages available to summarise");let n=Ff,r=[];for(let b of s){if(n<=0)break;let T=(b.content_text??"").slice(0,Pf);r.push({rid:b.rid,role:b.role,content:T}),n-=T.length}r.reverse();let o=s.length===pc||n<=0,a=r.map(b=>`**${b.role}**: ${b.content}`).join(`
1024
1024
 
1025
1025
  `),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(`
1026
- `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(he(),We));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 h=cf(m.stdout);if(!h)throw new Error("claude CLI returned an empty synopsis");return h.slice(0,nf)}function cf(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return n.trim()}}catch{}return t}function Za(e,t){let s=f(),n=new Date().toISOString(),r=Date.now(),o=s.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=za(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:n}),o?s.prepare(`UPDATE session_notes
1026
+ `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(ye(),tt));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 h=Wf(m.stdout);if(!h)throw new Error("claude CLI returned an empty synopsis");return h.slice(0,Uf)}function Wf(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return n.trim()}}catch{}return t}function Ec(e,t){let s=f(),n=new Date().toISOString(),r=Date.now(),o=s.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=_c(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:n}),o?s.prepare(`UPDATE session_notes
1027
1027
  SET auto_synopsis = ?,
1028
1028
  auto_synopsis_generated_at = ?,
1029
1029
  auto_synopsis_history = ?
1030
1030
  WHERE session_id = ?`).run(t,r,JSON.stringify(a),e):s.prepare(`INSERT INTO session_notes
1031
1031
  (session_id, content, updated_at, previous_versions, auto_synopsis,
1032
1032
  auto_synopsis_generated_at, auto_synopsis_history)
1033
- VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,n,t,r,JSON.stringify(a)),js(e)}function lf(e,t,s){try{rf();let n=Ya(xr,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
1034
- `;Z_(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}Qe();H();Q();import{randomUUID as uf}from"node:crypto";import{writeFileSync as df,readFileSync as GR,existsSync as zR}from"node:fs";import{join as pf}from"node:path";var mf=pf(U,"collections.json"),Ms=8;function Ds(e){return{...e}}function Se(e,t,s,n=null,r=new Date().toISOString()){f().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1035
- VALUES (?, ?, ?, ?, ?)`).run(e,n,t,s?JSON.stringify(s):null,r)}function Fs(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function Qa(e){if(!e)return 0;let t=0,s=e,n=new Set,r=f();for(;s;){if(n.has(s))throw new Error("collection cycle detected");n.add(s);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)break;t+=1,s=o.parent_id}return t}function gf(e,t){let s=f(),n=e,r=new Set;for(;n;){if(r.has(n))return!1;if(r.add(n),n===t)return!0;let o=s.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)return!1;n=o.parent_id}return!1}function ec(e=!1){return f().prepare(`SELECT c.*,
1033
+ VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,n,t,r,JSON.stringify(a)),Js(e)}function qf(e,t,s){try{$f();let n=mc(Br,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
1034
+ `;jf(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}pt();U();ee();import{randomUUID as Xf}from"node:crypto";import{writeFileSync as Jf,readFileSync as Jk,existsSync as Gk}from"node:fs";import{join as Gf}from"node:path";var Yf=Gf(B,"collections.json"),Gs=8;function Ys(e){return{...e}}function Re(e,t,s,n=null,r=new Date().toISOString()){f().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1035
+ VALUES (?, ?, ?, ?, ?)`).run(e,n,t,s?JSON.stringify(s):null,r)}function zs(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function bc(e){if(!e)return 0;let t=0,s=e,n=new Set,r=f();for(;s;){if(n.has(s))throw new Error("collection cycle detected");n.add(s);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)break;t+=1,s=o.parent_id}return t}function zf(e,t){let s=f(),n=e,r=new Set;for(;n;){if(r.has(n))return!1;if(r.add(n),n===t)return!0;let o=s.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)return!1;n=o.parent_id}return!1}function Sc(e=!1){return f().prepare(`SELECT c.*,
1036
1036
  (SELECT COUNT(*) FROM collection_sessions cs WHERE cs.collection_id = c.id) AS session_count
1037
1037
  FROM collections c
1038
1038
  ${e?"":"WHERE c.archived_at IS NULL"}
1039
- ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(n=>({...Ds(n),session_count:n.session_count}))}function Ie(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Ds(t):null}function tc(e,t=!0){let s=f(),n=t?Nr(e):[e];if(n.length===0)return[];let r=n.map(()=>"?").join(",");return s.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1039
+ ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(n=>({...Ys(n),session_count:n.session_count}))}function He(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Ys(t):null}function Tc(e,t=!0){let s=f(),n=t?Wr(e):[e];if(n.length===0)return[];let r=n.map(()=>"?").join(",");return s.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1040
1040
  FROM collection_sessions
1041
1041
  WHERE collection_id IN (${r})
1042
- ORDER BY added_at DESC`).all(...n)}function Nr(e){let t=f(),s=[e],n=[e],r=new Set([e]);for(;n.length>0;){let o=n.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...n),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),s.push(u.id),c.push(u.id));n=c}return s}function sc(e){return f().prepare(`SELECT c.* FROM collections c
1042
+ ORDER BY added_at DESC`).all(...n)}function Wr(e){let t=f(),s=[e],n=[e],r=new Set([e]);for(;n.length>0;){let o=n.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...n),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),s.push(u.id),c.push(u.id));n=c}return s}function yc(e){return f().prepare(`SELECT c.* FROM collections c
1043
1043
  JOIN collection_sessions cs ON cs.collection_id = c.id
1044
1044
  WHERE cs.session_id = ? AND c.archived_at IS NULL
1045
- ORDER BY LOWER(c.name)`).all(e)}function Bt(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 s=f(),n=new Date().toISOString(),r=uf();if(e.parent_id){if(!Ie(e.parent_id))throw new Error("parent collection not found");if(Qa(e.parent_id)>=Ms-1)throw new Error(`max collection depth is ${Ms}`)}return s.transaction(()=>{s.prepare(`INSERT INTO collections
1045
+ ORDER BY LOWER(c.name)`).all(e)}function Vt(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 s=f(),n=new Date().toISOString(),r=Xf();if(e.parent_id){if(!He(e.parent_id))throw new Error("parent collection not found");if(bc(e.parent_id)>=Gs-1)throw new Error(`max collection depth is ${Gs}`)}return s.transaction(()=>{s.prepare(`INSERT INTO collections
1046
1046
  (id, name, description, icon, color, parent_id, sort_key, created_at, updated_at, archived_at)
1047
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",n,n),Se(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),Je(),Ie(r)}function nc(e,t){let s=f(),n=Fs(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():n.name,description:t.description!==void 0?t.description:n.description,icon:t.icon!==void 0?t.icon:n.icon,color:t.color!==void 0?t.color:n.color,parent_id:t.parent_id!==void 0?t.parent_id:n.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:n.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!==n.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Ie(t.parent_id))throw new Error("parent collection not found");if(gf(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(Qa(t.parent_id)>=Ms-1)throw new Error(`max collection depth is ${Ms}`)}return s.transaction(()=>{s.prepare(`UPDATE collections
1047
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",n,n),Re(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),rt(),He(r)}function wc(e,t){let s=f(),n=zs(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():n.name,description:t.description!==void 0?t.description:n.description,icon:t.icon!==void 0?t.icon:n.icon,color:t.color!==void 0?t.color:n.color,parent_id:t.parent_id!==void 0?t.parent_id:n.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:n.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!==n.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!He(t.parent_id))throw new Error("parent collection not found");if(zf(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(bc(t.parent_id)>=Gs-1)throw new Error(`max collection depth is ${Gs}`)}return s.transaction(()=>{s.prepare(`UPDATE collections
1048
1048
  SET name = ?, description = ?, icon = ?, color = ?,
1049
1049
  parent_id = ?, sort_key = ?, updated_at = ?
1050
- 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!==n.name&&Se(e,"rename",{from:n.name,to:o.name},null,r),t.description!==void 0&&t.description!==n.description&&Se(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==n.icon||t.color!==void 0&&t.color!==n.color)&&Se(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==n.parent_id&&Se(e,"move",{from:n.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==n.sort_key&&Se(e,"reorder",{from:n.sort_key,to:o.sort_key},null,r)})(),Je(),Ie(e)}function rc(e){let t=f(),s=Fs(e);if(s.archived_at)return Ds(s);let n=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(n,n,e),Se(e,"archive",{name:s.name},null,n)})(),Je(),Ie(e)}function oc(e){let t=f(),s=Fs(e);if(!s.archived_at)return Ds(s);let n=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(n,e),Se(e,"restore",{name:s.name},null,n)})(),Je(),Ie(e)}function Ht(e,t,s=null,n={}){let r=f();if(Fs(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=n.source??"manual",u=n.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)
1051
- VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,s,c,u),Se(e,"add",{note:s,source:c,rule_id:u},t,d)})(),Je(),{added:!0}}function ic(e,t){let s=f();if(!s.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return s.transaction(()=>{s.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),Se(e,"remove",null,t,r)})(),Je(),{removed:!0}}function Ps(e){let t=f(),s=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1052
- WHERE rule_id = ?`).all(e);if(s.length===0)return{removed:0};let n=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of s)Se(o.collection_id,"remove",{rule_id:e},o.session_id,n)})(),Je(),{removed:s.length}}function _f(){return f().prepare(`SELECT id, collection_id, session_id, action, payload, at
1050
+ 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!==n.name&&Re(e,"rename",{from:n.name,to:o.name},null,r),t.description!==void 0&&t.description!==n.description&&Re(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==n.icon||t.color!==void 0&&t.color!==n.color)&&Re(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==n.parent_id&&Re(e,"move",{from:n.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==n.sort_key&&Re(e,"reorder",{from:n.sort_key,to:o.sort_key},null,r)})(),rt(),He(e)}function Rc(e){let t=f(),s=zs(e);if(s.archived_at)return Ys(s);let n=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(n,n,e),Re(e,"archive",{name:s.name},null,n)})(),rt(),He(e)}function kc(e){let t=f(),s=zs(e);if(!s.archived_at)return Ys(s);let n=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(n,e),Re(e,"restore",{name:s.name},null,n)})(),rt(),He(e)}function Zt(e,t,s=null,n={}){let r=f();if(zs(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=n.source??"manual",u=n.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)
1051
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,s,c,u),Re(e,"add",{note:s,source:c,rule_id:u},t,d)})(),rt(),{added:!0}}function Ac(e,t){let s=f();if(!s.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return s.transaction(()=>{s.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),Re(e,"remove",null,t,r)})(),rt(),{removed:!0}}function Ks(e){let t=f(),s=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1052
+ WHERE rule_id = ?`).all(e);if(s.length===0)return{removed:0};let n=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of s)Re(o.collection_id,"remove",{rule_id:e},o.session_id,n)})(),rt(),{removed:s.length}}function Kf(){return f().prepare(`SELECT id, collection_id, session_id, action, payload, at
1053
1053
  FROM collection_events
1054
- ORDER BY at ASC, id ASC`).all()}function Je(){try{J();let e=f(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
1054
+ ORDER BY at ASC, id ASC`).all()}function rt(){try{z();let e=f(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
1055
1055
  created_at, updated_at, archived_at
1056
1056
  FROM collections
1057
1057
  ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),s=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1058
1058
  FROM collection_sessions
1059
- ORDER BY collection_id, added_at`).all(),n=_f(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};df(mf,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}H();Q();import{randomUUID as Cr}from"node:crypto";import{existsSync as ff,mkdirSync as hf,writeFileSync as ac}from"node:fs";import{homedir as Ef}from"node:os";import{basename as bf,join as vr}from"node:path";var Us=vr(U,"auto-rules"),Sf=vr(Us,"rules.json"),Tf=vr(Us,"suggestions.json"),Or="Repositories",yf="Topics",cc=3;var wf=5,Rf=2,Af=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function Ir(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 kf(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 xf(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return Or;case"tag":return yf;case"plan-file":return null}}function Nf(e){let s=f().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(s)return s.id;let o=Bt({name:e,icon:e===Or?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===Or?"0000-repos":"0001-topics"});return f().prepare(`INSERT INTO auto_collection_rules
1059
+ ORDER BY collection_id, added_at`).all(),n=Kf(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};Jf(Yf,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}U();ee();import{randomUUID as Jr}from"node:crypto";import{existsSync as Vf,mkdirSync as Zf,writeFileSync as xc}from"node:fs";import{homedir as Qf}from"node:os";import{basename as eh,join as Gr}from"node:path";var Vs=Gr(B,"auto-rules"),th=Gr(Vs,"rules.json"),sh=Gr(Vs,"suggestions.json"),qr="Repositories",nh="Topics",Nc=3;var rh=5,oh=2,ih=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function Yr(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 ah(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 ch(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return qr;case"tag":return nh;case"plan-file":return null}}function lh(e){let s=f().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(s)return s.id;let o=Vt({name:e,icon:e===qr?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===qr?"0000-repos":"0001-topics"});return f().prepare(`INSERT INTO auto_collection_rules
1060
1060
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
1061
- VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(Cr(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function Of(e,t,s){let n;if(s!==void 0)n=s;else{let o=xf(t);n=o?Nf(o):null}return Bt({name:e,parent_id:n}).id}function $s(e){let t=f().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?Ir(t):null}function Lf(e){let t=f();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(Bs(e.pattern)+"%").map(n=>n.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(Bs(e.pattern)+"%").map(n=>n.id);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(s).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(n=>n.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1062
- WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(n=>n.id)}}function lc(e,t,s=3){let n=f(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1061
+ VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(Jr(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function uh(e,t,s){let n;if(s!==void 0)n=s;else{let o=ch(t);n=o?lh(o):null}return Vt({name:e,parent_id:n}).id}function Zs(e){let t=f().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?Yr(t):null}function dh(e){let t=f();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(Qs(e.pattern)+"%").map(n=>n.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(Qs(e.pattern)+"%").map(n=>n.id);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(s).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(n=>n.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1062
+ WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(n=>n.id)}}function Oc(e,t,s=3){let n=f(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1063
1063
  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=n.prepare(`SELECT ${r} FROM sessions s ${o}
1064
1064
  WHERE s.cwd IS NOT NULL AND s.cwd LIKE ? ESCAPE '\\'
1065
- ORDER BY s.started_at DESC LIMIT ?`).all(Bs(t)+"%",s);break;case"git-branch-prefix":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
1065
+ ORDER BY s.started_at DESC LIMIT ?`).all(Qs(t)+"%",s);break;case"git-branch-prefix":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
1066
1066
  WHERE s.git_branch IS NOT NULL AND s.git_branch LIKE ? ESCAPE '\\'
1067
- ORDER BY s.started_at DESC LIMIT ?`).all(Bs(t)+"%",s);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=n.prepare(`SELECT ${r} FROM sessions s ${o}
1067
+ ORDER BY s.started_at DESC LIMIT ?`).all(Qs(t)+"%",s);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=n.prepare(`SELECT ${r} FROM sessions s ${o}
1068
1068
  WHERE s.project_id = ?
1069
1069
  ORDER BY s.started_at DESC LIMIT ?`).all(c,s));break}case"tag":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
1070
1070
  JOIN session_tags st ON st.session_id = s.id
@@ -1072,19 +1072,19 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1072
1072
  ORDER BY s.started_at DESC LIMIT ?`).all(t,s);break;case"plan-file":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
1073
1073
  WHERE s.first_user_message IS NOT NULL
1074
1074
  AND s.first_user_message LIKE ?
1075
- ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",s);break}return a.map(c=>({id:c.id,title:Cf(c),cwd:c.cwd,started_at:c.started_at}))}function Cf(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 s=t.split(`
1076
- `)[0].trim();return s.length>80?s.slice(0,80)+"\u2026":s}function vf(e,t,s=f()){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 n=Number(e.pattern);return Number.isFinite(n)&&t.project_id===n}case"tag":return!!s.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 uc(e){let t=f(),s=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!s)return{added:0};let n=t.prepare(`SELECT * FROM auto_collection_rules
1075
+ ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",s);break}return a.map(c=>({id:c.id,title:ph(c),cwd:c.cwd,started_at:c.started_at}))}function ph(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 s=t.split(`
1076
+ `)[0].trim();return s.length>80?s.slice(0,80)+"\u2026":s}function mh(e,t,s=f()){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 n=Number(e.pattern);return Number.isFinite(n)&&t.project_id===n}case"tag":return!!s.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 Lc(e){let t=f(),s=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!s)return{added:0};let n=t.prepare(`SELECT * FROM auto_collection_rules
1077
1077
  WHERE enabled = 1 AND created_by != 'seed'
1078
- ORDER BY priority, created_at`).all(),r=0;for(let o of n){let a=Ir(o);if(vf(a,s,t))try{Ht(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 Lr(e){if(!e.enabled)return{added:0};let t=0;for(let s of Lf(e))try{Ht(e.collection_id,s,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(n){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${s}:`,n)}return{added:t}}function jr(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let s=(e.pattern??"").trim();if(!s)throw new Error("pattern required");let n=f(),r=new Date().toISOString(),o=Cr(),a=e.collection_id;a||(a=Of(t,e.type,e.parent_collection_id)),n.prepare(`INSERT INTO auto_collection_rules
1078
+ ORDER BY priority, created_at`).all(),r=0;for(let o of n){let a=Yr(o);if(mh(a,s,t))try{Zt(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 Xr(e){if(!e.enabled)return{added:0};let t=0;for(let s of dh(e))try{Zt(e.collection_id,s,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(n){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${s}:`,n)}return{added:t}}function zr(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let s=(e.pattern??"").trim();if(!s)throw new Error("pattern required");let n=f(),r=new Date().toISOString(),o=Jr(),a=e.collection_id;a||(a=uh(t,e.type,e.parent_collection_id)),n.prepare(`INSERT INTO auto_collection_rules
1079
1079
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
1080
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,s,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),n.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,s);let c=$s(o);return Lr(c),rt(),c}function dc(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 f().prepare(t).all().map(Ir)}function pc(e,t){let s=f(),n=$s(e);if(!n)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():n.name,pattern:t.pattern!==void 0?t.pattern.trim():n.pattern,enabled:t.enabled!==void 0?t.enabled:n.enabled,priority:t.priority!==void 0?t.priority:n.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");s.prepare(`UPDATE auto_collection_rules
1080
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,s,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),n.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,s);let c=Zs(o);return Xr(c),ht(),c}function Cc(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 f().prepare(t).all().map(Yr)}function Ic(e,t){let s=f(),n=Zs(e);if(!n)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():n.name,pattern:t.pattern!==void 0?t.pattern.trim():n.pattern,enabled:t.enabled!==void 0?t.enabled:n.enabled,priority:t.priority!==void 0?t.priority:n.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");s.prepare(`UPDATE auto_collection_rules
1081
1081
  SET name = ?, pattern = ?, enabled = ?, priority = ?
1082
- WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=$s(e);return t.pattern!==void 0&&t.pattern!==n.pattern?(Ps(e),o.enabled&&Lr(o)):t.enabled!==void 0&&t.enabled!==n.enabled&&(o.enabled?Lr(o):Ps(e)),rt(),o}function mc(e){let t=f();if(!$s(e))return{removed:0};let n=Ps(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),rt(),n}function Hs(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 f().prepare(t).all().map(kf)}function gc(e){f().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),rt()}function _c(e){let t=f(),s=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!s)throw new Error(`suggestion not found: ${e}`);if(s.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let n=jr({name:s.suggested_name,type:s.type,pattern:s.pattern,parent_collection_id:s.suggested_parent_collection_id===null?void 0:s.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),rt(),n}function Ws(){let e=f(),t=new Date().toISOString(),s=Ef(),n=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...If(s,t).filter(c=>!n.has(c.pattern)),...jf(t),...Mf(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),n.size>0){let c=Array.from(n).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(n))}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
1082
+ 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!==n.pattern?(Ks(e),o.enabled&&Xr(o)):t.enabled!==void 0&&t.enabled!==n.enabled&&(o.enabled?Xr(o):Ks(e)),ht(),o}function vc(e){let t=f();if(!Zs(e))return{removed:0};let n=Ks(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),ht(),n}function en(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 f().prepare(t).all().map(ah)}function jc(e){f().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),ht()}function Mc(e){let t=f(),s=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!s)throw new Error(`suggestion not found: ${e}`);if(s.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let n=zr({name:s.suggested_name,type:s.type,pattern:s.pattern,parent_collection_id:s.suggested_parent_collection_id===null?void 0:s.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),ht(),n}function tn(){let e=f(),t=new Date().toISOString(),s=Qf(),n=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...gh(s,t).filter(c=>!n.has(c.pattern)),..._h(t),...fh(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),n.size>0){let c=Array.from(n).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(n))}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
1083
1083
  SET session_count = ?, detected_at = ?, suggested_name = ?, suggested_parent_collection_id = ?
1084
1084
  WHERE id = ?`).run(c.session_count,t,c.suggested_name,c.suggested_parent_collection_id,d.id):e.prepare(`INSERT INTO auto_collection_suggestions
1085
1085
  (id, type, pattern, suggested_name, suggested_parent_collection_id, session_count, detected_at, dismissed)
1086
- VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(Cr(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return rt(),Hs()}function If(e,t){let n=f().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of n){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<cc)continue;let u=!1;for(let[d,m]of r.entries())if(d!==a&&d.startsWith(a+"/")&&m.size>=cc){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:bf(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function jf(e){return f().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(wf).map(s=>({type:"tag",pattern:s.tag,suggested_name:s.tag,suggested_parent_collection_id:null,session_count:s.n,detected_at:e,dismissed:!1}))}function Mf(e){let t=f().prepare(`SELECT id, first_user_message FROM sessions
1087
- WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),s=new Map;for(let r of t)for(let o of Af){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=s.get(c);u||(u=new Set,s.set(c,u)),u.add(r.id)}}let n=[];for(let[r,o]of s.entries())o.size<Rf||n.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return n}function Bs(e){return e.replace(/[\\%_]/g,"\\$&")}function rt(){try{J(),ff(Us)||hf(Us,{recursive:!0});let e=f(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),s=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();ac(Sf,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),ac(Tf,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:s},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function fc(){let e=f().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}H();Q();import{randomUUID as hc}from"node:crypto";import{writeFileSync as Ec,readFileSync as uA,existsSync as Df,mkdirSync as Ff}from"node:fs";import{join as Mr}from"node:path";var qs=Mr(U,"threads"),Pf=Mr(qs,"index.json");function bc(){J(),Df(qs)||Ff(qs,{recursive:!0})}function Dr(e,t,s){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:s?.project??null,project_count:s?.project_count??0,folder_id:e.folder_id??null}}function Sc(e){let t=new Map;if(e.length===0)return t;let s=f(),n=e.map(()=>"?").join(","),r=s.prepare(`SELECT te.thread_id AS thread_id,
1086
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(Jr(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return ht(),en()}function gh(e,t){let n=f().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of n){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<Nc)continue;let u=!1;for(let[d,m]of r.entries())if(d!==a&&d.startsWith(a+"/")&&m.size>=Nc){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:eh(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function _h(e){return f().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(rh).map(s=>({type:"tag",pattern:s.tag,suggested_name:s.tag,suggested_parent_collection_id:null,session_count:s.n,detected_at:e,dismissed:!1}))}function fh(e){let t=f().prepare(`SELECT id, first_user_message FROM sessions
1087
+ WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),s=new Map;for(let r of t)for(let o of ih){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=s.get(c);u||(u=new Set,s.set(c,u)),u.add(r.id)}}let n=[];for(let[r,o]of s.entries())o.size<oh||n.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return n}function Qs(e){return e.replace(/[\\%_]/g,"\\$&")}function ht(){try{z(),Vf(Vs)||Zf(Vs,{recursive:!0});let e=f(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),s=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();xc(th,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),xc(sh,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:s},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function Dc(){let e=f().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}U();ee();import{randomUUID as Fc}from"node:crypto";import{writeFileSync as Pc,readFileSync as cA,existsSync as hh,mkdirSync as Eh}from"node:fs";import{join as Kr}from"node:path";var sn=Kr(B,"threads"),bh=Kr(sn,"index.json");function Uc(){z(),hh(sn)||Eh(sn,{recursive:!0})}function Vr(e,t,s){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:s?.project??null,project_count:s?.project_count??0,folder_id:e.folder_id??null}}function $c(e){let t=new Map;if(e.length===0)return t;let s=f(),n=e.map(()=>"?").join(","),r=s.prepare(`SELECT te.thread_id AS thread_id,
1088
1088
  p.name AS project,
1089
1089
  COUNT(*) AS n,
1090
1090
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
@@ -1092,7 +1092,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1092
1092
  LEFT JOIN sessions s ON s.id = te.session_id
1093
1093
  LEFT JOIN projects p ON p.id = s.project_id
1094
1094
  WHERE te.thread_id IN (${n})
1095
- 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(h=>h.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 Tc(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 yc(e){let s=f().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1095
+ 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(h=>h.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 Hc(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 Bc(e){let s=f().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1096
1096
  s.auto_title AS auto_title,
1097
1097
  s.auto_title_source AS auto_title_source,
1098
1098
  s.first_user_message AS first_user_message,
@@ -1100,11 +1100,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1100
1100
  FROM (SELECT ? AS sid) q
1101
1101
  LEFT JOIN sessions s ON s.id = q.sid
1102
1102
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
1103
- LEFT JOIN projects p ON p.id = s.project_id`).get(e),n=s?.auto_title_source??null;return{alias:s?.alias??null,auto_title:s?.auto_title??null,auto_title_source:n==="agent"||n==="heuristic"?n:null,first_user_message:s?.first_user_message??null,project:s?.project??null}}function Fr(e){let s=f().prepare(`SELECT
1103
+ LEFT JOIN projects p ON p.id = s.project_id`).get(e),n=s?.auto_title_source??null;return{alias:s?.alias??null,auto_title:s?.auto_title??null,auto_title_source:n==="agent"||n==="heuristic"?n:null,first_user_message:s?.first_user_message??null,project:s?.project??null}}function Zr(e){let s=f().prepare(`SELECT
1104
1104
  COUNT(*) AS session_count,
1105
1105
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
1106
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function Te(e){let t=re(e);t&&(bc(),Ec(Mr(qs,`${e}.json`),JSON.stringify(t,null,2)),wc())}function wc(){bc();let e=Pr({includeArchived:!0});Ec(Pf,JSON.stringify({threads:e},null,2))}function Xs(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=f(),n=hc(),r=new Date().toISOString();s.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(n,t,e.summary?.trim()||null,r),e.originSessionId&&s.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1107
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),Te(n);let o=re(n);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Pr(e={}){let t=f(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=Sc(n.map(o=>o.id));return n.map(o=>Dr(o,Fr(o.id),r.get(o.id)))}function re(e){let t=f(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
1106
+ FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function ke(e){let t=oe(e);t&&(Uc(),Pc(Kr(sn,`${e}.json`),JSON.stringify(t,null,2)),Wc())}function Wc(){Uc();let e=Qr({includeArchived:!0});Pc(bh,JSON.stringify({threads:e},null,2))}function nn(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=f(),n=Fc(),r=new Date().toISOString();s.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(n,t,e.summary?.trim()||null,r),e.originSessionId&&s.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1107
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),ke(n);let o=oe(n);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Qr(e={}){let t=f(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=$c(n.map(o=>o.id));return n.map(o=>Vr(o,Zr(o.id),r.get(o.id)))}function oe(e){let t=f(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
1108
1108
  NULLIF(sa.alias, '') AS alias,
1109
1109
  s.auto_title AS auto_title,
1110
1110
  s.auto_title_source AS auto_title_source,
@@ -1115,10 +1115,10 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1115
1115
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1116
1116
  LEFT JOIN projects p ON p.id = s.project_id
1117
1117
  WHERE e.thread_id = ?
1118
- ORDER BY e.added_at ASC`).all(e).map(Tc),r=Sc([e]).get(e);return{...Dr(s,Fr(s.id),r),edges:n}}function Rc(e){return f().prepare(`SELECT t.* FROM threads t
1118
+ ORDER BY e.added_at ASC`).all(e).map(Hc),r=$c([e]).get(e);return{...Vr(s,Zr(s.id),r),edges:n}}function qc(e){return f().prepare(`SELECT t.* FROM threads t
1119
1119
  JOIN thread_edges e ON e.thread_id = t.id
1120
1120
  WHERE e.session_id = ? AND t.archived = 0
1121
- ORDER BY t.created_at DESC`).all(e).map(n=>Dr(n,Fr(n.id)))}function Js(e){let t=f();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let n=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
1121
+ ORDER BY t.created_at DESC`).all(e).map(n=>Vr(n,Zr(n.id)))}function rn(e){let t=f();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let n=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
1122
1122
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1123
1123
  VALUES (?, ?, ?, ?, ?, ?, ?)
1124
1124
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -1126,33 +1126,33 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1126
1126
  role = excluded.role,
1127
1127
  confidence = excluded.confidence,
1128
1128
  source = excluded.source,
1129
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,n),Te(e.threadId);let u=yc(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:n,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 Ac(e,t){let n=f().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&Te(e),{removed:n.changes}}function Wt(e,t,s){let n=f(),r=n.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(s!==null){if(s===t)throw new Error("cycle detected: session cannot be its own parent");let c=n.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=s,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=s?"child":"origin";n.prepare(`UPDATE thread_edges
1129
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,n),ke(e.threadId);let u=Bc(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:n,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 Xc(e,t){let n=f().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&ke(e),{removed:n.changes}}function Qt(e,t,s){let n=f(),r=n.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(s!==null){if(s===t)throw new Error("cycle detected: session cannot be its own parent");let c=n.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=s,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=s?"child":"origin";n.prepare(`UPDATE thread_edges
1130
1130
  SET parent_session_id = ?, role = ?, added_at = ?
1131
- WHERE thread_id = ? AND session_id = ?`).run(s,o,new Date().toISOString(),e,t),Te(e);let a=yc(t);return Tc({...r,parent_session_id:s,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 kc(e,t){let s=t.trim();if(!s)throw new Error("name cannot be empty");f().prepare("UPDATE threads SET name = ? WHERE id = ?").run(s,e),Te(e);let r=re(e);if(!r)throw new Error(`thread ${e} not found`);return r}function xc(e){f().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Te(e);let s=re(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Nc(e){f().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Te(e);let s=re(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Oc(e){f().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Te(e);let s=re(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Lc(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let s=f(),n=new Date().toISOString();s.transaction(()=>{let o=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)s.prepare(`INSERT INTO thread_edges
1131
+ WHERE thread_id = ? AND session_id = ?`).run(s,o,new Date().toISOString(),e,t),ke(e);let a=Bc(t);return Hc({...r,parent_session_id:s,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 Jc(e,t){let s=t.trim();if(!s)throw new Error("name cannot be empty");f().prepare("UPDATE threads SET name = ? WHERE id = ?").run(s,e),ke(e);let r=oe(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Gc(e){f().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),ke(e);let s=oe(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Yc(e){f().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),ke(e);let s=oe(e);if(!s)throw new Error(`thread ${e} not found`);return s}function zc(e){f().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),ke(e);let s=oe(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Kc(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let s=f(),n=new Date().toISOString();s.transaction(()=>{let o=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)s.prepare(`INSERT INTO thread_edges
1132
1132
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1133
1133
  VALUES (?, ?, ?, ?, ?, ?, ?)
1134
1134
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
1135
1135
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
1136
1136
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
1137
1137
  confidence = MAX(thread_edges.confidence, excluded.confidence),
1138
- source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,n);s.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Te(t),wc();let r=re(t);if(!r)throw new Error("merge destination disappeared");return r}function Cc(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=f(),s=new Date().toISOString(),n=hc();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(n,e.newThreadName.trim(),s);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
1138
+ source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,n);s.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),ke(t),Wc();let r=oe(t);if(!r)throw new Error("merge destination disappeared");return r}function Vc(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=f(),s=new Date().toISOString(),n=Fc();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(n,e.newThreadName.trim(),s);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
1139
1139
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1140
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(n,o,a.parent_session_id,a.role,a.confidence,a.source,s),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),Te(e.threadId),Te(n);let r=re(n);if(!r)throw new Error("split destination disappeared");return r}H();import{execFile as Qf}from"node:child_process";import{promisify as eh}from"node:util";import{readlink as th,readFile as Dc}from"node:fs/promises";import{platform as Vs}from"node:os";import{readFileSync as Uf,statSync as $f}from"node:fs";var Bf=200*1024*1024,Gs=.7,zs=.5,jc=zs,Hf=[{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"}],Wf=["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 vc(e){if(!e)return null;if(e.startsWith("/")){let s=e.split(" \xB7 ");if(s.length>1)return s[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function qf(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of Hf)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Xf(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],s=Math.min(e.length,t.length);for(let n=0;n<s;n++){let r=e[n];if(!r)continue;let o=r.toLowerCase();for(let a of Wf)if(o.includes(a))return{weight:t[n],matched:a,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function Ur(e,t){if(!e||!t||e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function Jf(e,t){if(e.length===0||t.length===0)return 0;let s=0;for(let n of e)for(let r of t){let o=Ur(n,r);o>s&&(s=o)}return s}function Yf(e,t){let s=Ur(e.mean_embedding,t.mean_embedding),n=Ur(e.tail_pool,t.head_pool),r=Jf(e.sample_chunks,t.sample_chunks),o=0,a=null;if(s>o&&(o=s,a="mean"),n>o&&(o=n,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 Gf(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 zf(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let s=0;for(let r of t)e.has(r)&&s++;return s===0?{weight:0,count:0}:{weight:Math.min(.4,s*.1),count:s}}function Kf(e,t){let s=vc(e),n=vc(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function Ic(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Vf(e,t){let s=0,n=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1141
- `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){s+=.5,n=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Ic(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Ic(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function Zf(e,t,s=jc){if(t.started_at_ms<=e.started_at_ms)return null;let n=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<n)return null;let r=qf(n,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=Xf(o),c=zf(e.touched_files,t.touched_files),u=Kf(e.auto_title,t.auto_title),d=Yf(e,t),m=Gf(e,t),h=Vf(e,t),b=r.weight+a.weight+c.weight+u.weight+d.weight+m.weight+h.weight;if(b<s)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})`),h.weight>0){let S=[];h.pathMatch&&S.push(`opened authored path "${h.pathMatch.split("/").pop()}"`),h.contentMatch&&S.push("verbatim-paste of authored content"),T.push(`doc-authorship: ${S.join(" + ")} (+${h.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:h.weight},reasons:T}}function ot(e,t=jc){let s=[];for(let n=0;n<e.length;n++){let r=e[n],o=null;for(let a=0;a<n;a++){let c=e[a],u=Zf(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&s.push(o)}return s}function Mc(e,t){let s=new Map,n=c=>{let u=c;for(;s.get(u)!==u;)u=s.get(u);let d=c;for(;s.get(d)!==u;){let m=s.get(d);s.set(d,u),d=m}return u},r=(c,u)=>{let d=n(c),m=n(u);d!==m&&s.set(d,m)};for(let c of e)s.has(c.parent_id)||s.set(c.parent_id,c.parent_id),s.has(c.child_id)||s.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of s.keys()){let u=n(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 Ks(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if($f(e).size>Bf)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=Uf(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(`
1142
- `,d),h=m===-1?u.length:m,b=u.slice(d,h);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<s){let R=S.message.content.trim();R&&o.push(R.length>n?R.slice(0,n):R)}let w=S.message?.content;if(Array.isArray(w))for(let R of w){if(!R||typeof R!="object")continue;let D=R;if(D.type!=="tool_use")continue;let O=D.input??{},Y=typeof O.file_path=="string"?O.file_path:null;if(Y){let M=Ys(Y);M&&r.add(M)}if((D.name==="Write"||D.name==="Edit"||D.name==="MultiEdit")&&Y){let M=Ys(Y);M&&a.add(M);let W=typeof O.content=="string"?O.content:typeof O.new_string=="string"?O.new_string:null;W&&W.length>=200&&c.push(W.length>4096?W.slice(0,4096):W)}if(D.name==="Bash"&&typeof O.command=="string")for(let M of O.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let W=Ys(M[1]);W&&r.add(W)}if((D.name==="Glob"||D.name==="Grep")&&typeof O.pattern=="string"){let M=Ys(O.pattern);M&&!M.includes("*")&&r.add(M)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function Ys(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Br=eh(Qf),sh=6,Fc="Active ",Pc=" sessions \u2014 ",nh=6e4;async function rh(){if(Vs()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Br(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
1143
- `)){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)&&n.push(a)}return n}catch{continue}return[]}async function oh(e){let t=Vs();if(t==="linux")try{return(await th(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:n}=await Br(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
1144
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function ih(e){let t=Vs();if(t==="linux")try{let s=await Dc(`/proc/${e}/stat`,"utf8"),n=s.lastIndexOf(")");if(n===-1)return null;let r=s.slice(n+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await Dc("/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 s of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Br(s,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(n.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function ah(e,t){let s=await rh();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let a of s){let c=await oh(a);if(c&&(c===n||c.startsWith(n+"/"))){let u=await ih(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=nh;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 h=Math.abs(m-a);h<u&&(u=h,c=d)}c&&o.add(c.session_id)}return o}function ch(e){let s=f().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!s)throw new Error(`project ${e} not found`);return s}function lh(e,t){let s=f(),n=`${Fc}${t}${Pc}%`,r=s.prepare(`SELECT t.id
1140
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(n,o,a.parent_session_id,a.role,a.confidence,a.source,s),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),ke(e.threadId),ke(n);let r=oe(n);if(!r)throw new Error("split destination disappeared");return r}U();import{execFile as jh}from"node:child_process";import{promisify as Mh}from"node:util";import{readlink as Dh,readFile as sl}from"node:fs/promises";import{platform as un}from"node:os";import{readFileSync as Sh,statSync as Th}from"node:fs";var yh=200*1024*1024,an=.7,cn=.5,el=cn,wh=[{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"}],Rh=["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 Zc(e){if(!e)return null;if(e.startsWith("/")){let s=e.split(" \xB7 ");if(s.length>1)return s[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function kh(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of wh)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Ah(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],s=Math.min(e.length,t.length);for(let n=0;n<s;n++){let r=e[n];if(!r)continue;let o=r.toLowerCase();for(let a of Rh)if(o.includes(a))return{weight:t[n],matched:a,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function eo(e,t){if(!e||!t||e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function xh(e,t){if(e.length===0||t.length===0)return 0;let s=0;for(let n of e)for(let r of t){let o=eo(n,r);o>s&&(s=o)}return s}function Nh(e,t){let s=eo(e.mean_embedding,t.mean_embedding),n=eo(e.tail_pool,t.head_pool),r=xh(e.sample_chunks,t.sample_chunks),o=0,a=null;if(s>o&&(o=s,a="mean"),n>o&&(o=n,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 Oh(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 Lh(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let s=0;for(let r of t)e.has(r)&&s++;return s===0?{weight:0,count:0}:{weight:Math.min(.4,s*.1),count:s}}function Ch(e,t){let s=Zc(e),n=Zc(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function Qc(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Ih(e,t){let s=0,n=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1141
+ `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){s+=.5,n=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Qc(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Qc(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function vh(e,t,s=el){if(t.started_at_ms<=e.started_at_ms)return null;let n=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<n)return null;let r=kh(n,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=Ah(o),c=Lh(e.touched_files,t.touched_files),u=Ch(e.auto_title,t.auto_title),d=Nh(e,t),m=Oh(e,t),h=Ih(e,t),b=r.weight+a.weight+c.weight+u.weight+d.weight+m.weight+h.weight;if(b<s)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})`),h.weight>0){let S=[];h.pathMatch&&S.push(`opened authored path "${h.pathMatch.split("/").pop()}"`),h.contentMatch&&S.push("verbatim-paste of authored content"),T.push(`doc-authorship: ${S.join(" + ")} (+${h.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:h.weight},reasons:T}}function Et(e,t=el){let s=[];for(let n=0;n<e.length;n++){let r=e[n],o=null;for(let a=0;a<n;a++){let c=e[a],u=vh(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&s.push(o)}return s}function tl(e,t){let s=new Map,n=c=>{let u=c;for(;s.get(u)!==u;)u=s.get(u);let d=c;for(;s.get(d)!==u;){let m=s.get(d);s.set(d,u),d=m}return u},r=(c,u)=>{let d=n(c),m=n(u);d!==m&&s.set(d,m)};for(let c of e)s.has(c.parent_id)||s.set(c.parent_id,c.parent_id),s.has(c.child_id)||s.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of s.keys()){let u=n(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 ln(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if(Th(e).size>yh)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=Sh(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(`
1142
+ `,d),h=m===-1?u.length:m,b=u.slice(d,h);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<s){let R=S.message.content.trim();R&&o.push(R.length>n?R.slice(0,n):R)}let w=S.message?.content;if(Array.isArray(w))for(let R of w){if(!R||typeof R!="object")continue;let j=R;if(j.type!=="tool_use")continue;let x=j.input??{},q=typeof x.file_path=="string"?x.file_path:null;if(q){let D=on(q);D&&r.add(D)}if((j.name==="Write"||j.name==="Edit"||j.name==="MultiEdit")&&q){let D=on(q);D&&a.add(D);let H=typeof x.content=="string"?x.content:typeof x.new_string=="string"?x.new_string:null;H&&H.length>=200&&c.push(H.length>4096?H.slice(0,4096):H)}if(j.name==="Bash"&&typeof x.command=="string")for(let D of x.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let H=on(D[1]);H&&r.add(H)}if((j.name==="Glob"||j.name==="Grep")&&typeof x.pattern=="string"){let D=on(x.pattern);D&&!D.includes("*")&&r.add(D)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function on(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var so=Mh(jh),Fh=6,nl="Active ",rl=" sessions \u2014 ",Ph=6e4;async function Uh(){if(un()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await so(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
1143
+ `)){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)&&n.push(a)}return n}catch{continue}return[]}async function $h(e){let t=un();if(t==="linux")try{return(await Dh(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:n}=await so(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
1144
+ `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Hh(e){let t=un();if(t==="linux")try{let s=await sl(`/proc/${e}/stat`,"utf8"),n=s.lastIndexOf(")");if(n===-1)return null;let r=s.slice(n+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await sl("/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 s of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await so(s,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(n.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function Bh(e,t){let s=await Uh();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let a of s){let c=await $h(a);if(c&&(c===n||c.startsWith(n+"/"))){let u=await Hh(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=Ph;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 h=Math.abs(m-a);h<u&&(u=h,c=d)}c&&o.add(c.session_id)}return o}function Wh(e){let s=f().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!s)throw new Error(`project ${e} not found`);return s}function qh(e,t){let s=f(),n=`${nl}${t}${rl}%`,r=s.prepare(`SELECT t.id
1145
1145
  FROM threads t
1146
1146
  WHERE t.archived = 0
1147
1147
  AND t.name LIKE ?
1148
- ORDER BY t.created_at DESC`).all(n);for(let a of r){let c=re(a.id);if(c&&c.project===t)return c}let o=s.prepare(`SELECT DISTINCT te.thread_id AS id
1148
+ ORDER BY t.created_at DESC`).all(n);for(let a of r){let c=oe(a.id);if(c&&c.project===t)return c}let o=s.prepare(`SELECT DISTINCT te.thread_id AS id
1149
1149
  FROM thread_edges te
1150
1150
  JOIN sessions s ON s.id = te.session_id
1151
1151
  JOIN threads t ON t.id = te.thread_id
1152
1152
  WHERE s.project_id = ?
1153
1153
  AND t.archived = 0
1154
1154
  AND t.name LIKE ?
1155
- LIMIT 1`).get(e,n);return o?re(o.id):null}function Uc(e){let t=e?new Date(e):new Date,s=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${s}-${n}-${r}`}function $r(e,t){let s=f(),n=t>0,r=n?Date.now()-t*60*60*1e3:0;return n?s.prepare(`SELECT s.id AS session_id,
1155
+ LIMIT 1`).get(e,n);return o?oe(o.id):null}function ol(e){let t=e?new Date(e):new Date,s=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${s}-${n}-${r}`}function to(e,t){let s=f(),n=t>0,r=n?Date.now()-t*60*60*1e3:0;return n?s.prepare(`SELECT s.id AS session_id,
1156
1156
  sa.alias AS alias,
1157
1157
  s.auto_title AS auto_title,
1158
1158
  s.first_user_message AS first_user_message,
@@ -1172,19 +1172,19 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1172
1172
  FROM sessions s
1173
1173
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1174
1174
  WHERE s.project_id = ?
1175
- ORDER BY s.started_at ASC`).all(e)}function uh(e){let t=f(),s=[];for(let n of e){if(!n.started_at)continue;let r=Date.parse(n.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(n.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=Ks(o.file_path,{maxUserMessages:7});s.push({id:n.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:n.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:n.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 s}function dh(e){let s=f().prepare(`SELECT session_id, parent_session_id, source
1175
+ ORDER BY s.started_at ASC`).all(e)}function Xh(e){let t=f(),s=[];for(let n of e){if(!n.started_at)continue;let r=Date.parse(n.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(n.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=ln(o.file_path,{maxUserMessages:7});s.push({id:n.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:n.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:n.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 s}function Jh(e){let s=f().prepare(`SELECT session_id, parent_session_id, source
1176
1176
  FROM thread_edges
1177
- WHERE thread_id = ?`).all(e),n=new Map;for(let r of s)n.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return n}async function $c(e,t={}){let s=ch(e),n=t.windowHours??sh,r=t.scoreThreshold??zs,o=t.useLivePids??!0,a=[],c=[];if(o&&s.decoded_path){let w=$r(e,0),R=await ah(s.decoded_path,w);if(R===null){let O=Vs()==="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(O),c=$r(e,n)}else R.size===0?(a.push(`No active terminals open in ${s.name} (cwd=${s.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=w.filter(D=>R.has(D.session_id))}else c=$r(e,n);c.length===0&&!a.length&&a.push(`No active sessions in ${s.name} within the last ${n}h.`);let u=lh(e,s.name),d=new Set(u?u.edges.map(w=>w.session_id):[]),m=c.filter(w=>!d.has(w.session_id)),h=uh(c);h.sort((w,R)=>w.started_at_ms-R.started_at_ms);let b=ot(h,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:`${Fc}${s.name}${Pc}${Uc(t.todayIso)}`;return{project:s,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 Bc(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},s;e.thread.exists&&e.thread.id?s=e.thread.id:s=Xs({name:e.thread.name,summary:`Auto-captured by sync-active on ${Uc()}. 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=s;let n=dh(s);for(let a of e.candidates)n.has(a.session_id)||(Js({threadId:s,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,n.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=n.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&n.has(a.parent_id))try{Wt(s,a.child_id,a.parent_id),t.edges_set++,n.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=f().prepare(`SELECT session_id FROM thread_edges
1177
+ WHERE thread_id = ?`).all(e),n=new Map;for(let r of s)n.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return n}async function il(e,t={}){let s=Wh(e),n=t.windowHours??Fh,r=t.scoreThreshold??cn,o=t.useLivePids??!0,a=[],c=[];if(o&&s.decoded_path){let w=to(e,0),R=await Bh(s.decoded_path,w);if(R===null){let x=un()==="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(x),c=to(e,n)}else R.size===0?(a.push(`No active terminals open in ${s.name} (cwd=${s.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=w.filter(j=>R.has(j.session_id))}else c=to(e,n);c.length===0&&!a.length&&a.push(`No active sessions in ${s.name} within the last ${n}h.`);let u=qh(e,s.name),d=new Set(u?u.edges.map(w=>w.session_id):[]),m=c.filter(w=>!d.has(w.session_id)),h=Xh(c);h.sort((w,R)=>w.started_at_ms-R.started_at_ms);let b=Et(h,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:`${nl}${s.name}${rl}${ol(t.todayIso)}`;return{project:s,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 al(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},s;e.thread.exists&&e.thread.id?s=e.thread.id:s=nn({name:e.thread.name,summary:`Auto-captured by sync-active on ${ol()}. 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=s;let n=Jh(s);for(let a of e.candidates)n.has(a.session_id)||(rn({threadId:s,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,n.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=n.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&n.has(a.parent_id))try{Qt(s,a.child_id,a.parent_id),t.edges_set++,n.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=f().prepare(`SELECT session_id FROM thread_edges
1178
1178
  WHERE thread_id = ?
1179
1179
  AND source = 'auto-active'
1180
1180
  AND parent_session_id IS NULL
1181
- AND role = 'child'`).all(s);for(let a of o)try{Wt(s,a.session_id,null)}catch{}return t}H();Q();import{randomUUID as ph}from"node:crypto";import{writeFileSync as mh}from"node:fs";import{join as gh}from"node:path";var _h=gh(U,"thread-folders.json");function Hc(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 qt(){try{J();let e=Hr({includeArchived:!0});mh(_h,JSON.stringify({folders:e},null,2))}catch{}}function Hr(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return f().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(Hc)}function Ye(e){let t=f().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?Hc(t):null}function Wc(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 s=e.parentFolderId??null,n=e.projectScope??null;if(s){let c=Ye(s);if(!c)throw new Error(`parent folder ${s} not found`);n=c.project_scope}let r=ph(),o=new Date().toISOString(),a=qc(s,n);return f().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1182
- VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,s,n,o,a),qt(),{id:r,name:t,parent_folder_id:s,project_scope:n,created_at:o,archived:!1,sort_order:a}}function qc(e,t){return f().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1181
+ AND role = 'child'`).all(s);for(let a of o)try{Qt(s,a.session_id,null)}catch{}return t}U();ee();import{randomUUID as Gh}from"node:crypto";import{writeFileSync as Yh}from"node:fs";import{join as zh}from"node:path";var Kh=zh(B,"thread-folders.json");function cl(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 es(){try{z();let e=no({includeArchived:!0});Yh(Kh,JSON.stringify({folders:e},null,2))}catch{}}function no(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return f().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(cl)}function ot(e){let t=f().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?cl(t):null}function ll(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 s=e.parentFolderId??null,n=e.projectScope??null;if(s){let c=ot(s);if(!c)throw new Error(`parent folder ${s} not found`);n=c.project_scope}let r=Gh(),o=new Date().toISOString(),a=ul(s,n);return f().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1182
+ VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,s,n,o,a),es(),{id:r,name:t,parent_folder_id:s,project_scope:n,created_at:o,archived:!1,sort_order:a}}function ul(e,t){return f().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1183
1183
  FROM thread_folders
1184
1184
  WHERE parent_folder_id IS ?
1185
- AND project_scope IS ?`).get(e,t)?.next??0}function Xc(e,t){let s=t.trim();if(!s)throw new Error("folder name cannot be empty");if(s.length>200)throw new Error("folder name too long (200 char max)");let n=Ye(e);if(!n)throw new Error(`folder ${e} not found`);return f().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(s,e),qt(),{...n,name:s}}function Jc(e,t){let s=Ye(e);if(!s)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let n=s.project_scope;if(t!==null){let o=Ye(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=Ye(a);if(!u)break;a=u.parent_folder_id,c++}n=o.project_scope}let r=qc(t,n);return f().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,n,r,e),qt(),{...s,parent_folder_id:t,project_scope:n,sort_order:r}}function Yc(e,t,s){if(!Array.isArray(s)||s.length===0)throw new Error("ordered_ids must be a non-empty array");let n=new Set;for(let d of s){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(n.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);n.add(d)}let r=f(),o=r.prepare(`SELECT id FROM thread_folders
1185
+ AND project_scope IS ?`).get(e,t)?.next??0}function dl(e,t){let s=t.trim();if(!s)throw new Error("folder name cannot be empty");if(s.length>200)throw new Error("folder name too long (200 char max)");let n=ot(e);if(!n)throw new Error(`folder ${e} not found`);return f().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(s,e),es(),{...n,name:s}}function pl(e,t){let s=ot(e);if(!s)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let n=s.project_scope;if(t!==null){let o=ot(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=ot(a);if(!u)break;a=u.parent_folder_id,c++}n=o.project_scope}let r=ul(t,n);return f().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,n,r,e),es(),{...s,parent_folder_id:t,project_scope:n,sort_order:r}}function ml(e,t,s){if(!Array.isArray(s)||s.length===0)throw new Error("ordered_ids must be a non-empty array");let n=new Set;for(let d of s){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(n.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);n.add(d)}let r=f(),o=r.prepare(`SELECT id FROM thread_folders
1186
1186
  WHERE parent_folder_id IS ?
1187
- AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==s.length)throw new Error(`ordered_ids length ${s.length} does not match sibling count ${a.size}`);for(let d of s)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,h)=>c.run(h*100,m))})(s),qt()}function Gc(e){if(!Ye(e))throw new Error(`folder ${e} not found`);f().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),qt()}function zc(e,t){if(t!==null&&!Ye(t))throw new Error(`folder ${t} not found`);if(f().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function Kc(e,t){let s=new Set,n=[];for(let r of t){if(s.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;s.add(r.id),n.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return n.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),n}H();var fh=4e3,hh=2,Eh=30,bh=.2,Sh={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function Zs(e){return e?Math.ceil(e.length/4):0}function Vc(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Eh);return Math.max(bh,t)}function Zc(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function Qc(e){return f().prepare(`SELECT s.id,
1187
+ AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==s.length)throw new Error(`ordered_ids length ${s.length} does not match sibling count ${a.size}`);for(let d of s)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,h)=>c.run(h*100,m))})(s),es()}function gl(e){if(!ot(e))throw new Error(`folder ${e} not found`);f().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),es()}function _l(e,t){if(t!==null&&!ot(t))throw new Error(`folder ${t} not found`);if(f().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function fl(e,t){let s=new Set,n=[];for(let r of t){if(s.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;s.add(r.id),n.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return n.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),n}U();var Vh=4e3,Zh=2,Qh=30,eE=.2,tE={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function dn(e){return e?Math.ceil(e.length/4):0}function hl(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Qh);return Math.max(eE,t)}function El(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function bl(e){return f().prepare(`SELECT s.id,
1188
1188
  NULLIF(sa.alias, '') AS alias,
1189
1189
  s.auto_title,
1190
1190
  s.auto_title_source,
@@ -1195,25 +1195,25 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1195
1195
  FROM sessions s
1196
1196
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1197
1197
  LEFT JOIN projects p ON p.id = s.project_id
1198
- WHERE s.id = ?`).get(e)??null}function el(e){let s=f().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!s||!s.summary)return null;let n=s.summary.trim();return n.length>0?n:null}function tl(e){let t=e.alias?.trim(),s=e.auto_title?.trim(),n=e.first_user_message?.trim();return s&&e.auto_title_source==="agent"?s:t||s||(n?n.slice(0,80):e.id.slice(0,8))}function Th(e){let s=f().prepare(`SELECT id, auto_title, started_at
1198
+ WHERE s.id = ?`).get(e)??null}function Sl(e){let s=f().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!s||!s.summary)return null;let n=s.summary.trim();return n.length>0?n:null}function Tl(e){let t=e.alias?.trim(),s=e.auto_title?.trim(),n=e.first_user_message?.trim();return s&&e.auto_title_source==="agent"?s:t||s||(n?n.slice(0,80):e.id.slice(0,8))}function sE(e){let s=f().prepare(`SELECT id, auto_title, started_at
1199
1199
  FROM sessions
1200
1200
  WHERE project_id = ?
1201
- ORDER BY COALESCE(started_at, ''), id`).all(e),n=new Set,r=new Set,o=[];for(let b of s){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&&n.add(w),S&&r.add(S)}let a=[...n].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,h=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}`,R=(m.get(w)??0)+1;m.set(w,R),h.set(b.id,`${T}.${S}.${R}`)}return{byId:h}}function yh(e){return{table:e!==null?Th(e):null,originProjectId:e,cache:new Map}}function Qs(e,t){let s=e.cache.get(t);if(s)return s;let n=Qc(t);if(!n)return null;let r=e.table&&n.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:n.id,title:tl(n),decimal:r,summary:el(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,o),o}function wh(e,t){let n=f().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1201
+ ORDER BY COALESCE(started_at, ''), id`).all(e),n=new Set,r=new Set,o=[];for(let b of s){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&&n.add(w),S&&r.add(S)}let a=[...n].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,h=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}`,R=(m.get(w)??0)+1;m.set(w,R),h.set(b.id,`${T}.${S}.${R}`)}return{byId:h}}function nE(e){return{table:e!==null?sE(e):null,originProjectId:e,cache:new Map}}function pn(e,t){let s=e.cache.get(t);if(s)return s;let n=bl(t);if(!n)return null;let r=e.table&&n.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:n.id,title:Tl(n),decimal:r,summary:Sl(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,o),o}function rE(e,t){let n=f().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1202
1202
  FROM thread_edges te
1203
1203
  WHERE te.session_id = ?
1204
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of n){if(!o.pid)continue;let a=Qs(e,o.pid);a&&r.push(a)}return r}function Rh(e,t){let n=f().prepare(`SELECT DISTINCT te.session_id AS sid
1204
+ AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of n){if(!o.pid)continue;let a=pn(e,o.pid);a&&r.push(a)}return r}function oE(e,t){let n=f().prepare(`SELECT DISTINCT te.session_id AS sid
1205
1205
  FROM thread_edges te
1206
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of n){if(!o.sid)continue;let a=Qs(e,o.sid);a&&r.push(a)}return r}function sl(e){let t=Sh[e.linkType]??.5,s=it(e.confidence),n=t*s,r=Vc(e.daysApart),o=e.embeddingCosine??.5,a=it(e.pagerank);if(e.scoring==="pagerank")return it(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?it(n):it(o);let c=.35*n+.2*r+.2*o+.25*a;return it(c)}function it(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function Ah(e,t,s,n,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 s)a(e,c.session_id);for(let c of s)a(c.session_id,e);for(let c of n)a(e,c.session_id);for(let c of n)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 h of c){let b=o.get(h);if(b)for(let T of b){if(u.has(T))continue;let S=vt(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 h of m)c.add(h)}}return{edges:o}}function kh(e,t={}){let s=t.iterations??12,n=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<s;d++){let m=new Map(r.map(h=>[h,(1-n)/r.length]));for(let h of r){let b=e.edges.get(h);if(!b||b.size===0)continue;let T=(a.get(h)??0)/b.size;for(let S of b)m.set(S,(m.get(S)??0)+n*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 nl=240;function rl(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function xh(e){let t=e.decimal?`${e.decimal} `:"",s=e.session_id.slice(0,8),n="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${s})${n}`;if(e.summary){let o=rl(e.summary,nl);return`${r}
1207
- ${o}`}return r}function Nh(e,t,s){let n=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(n.push(c),o+=Zs(c),e.summary){let u=rl(e.summary,nl*4);n.push(u),o+=Zs(u)}n.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,m=Zs(d),h=[],b=0;for(let T of u.refs){let S=xh(T),w=Zs(S);if(o+m+b+w>s){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}h.push(S),b+=w}if(h.length>0){n.push(d);for(let T of h)n.push(T);n.push(""),o+=m+b}}for(;n.length>0&&n[n.length-1]==="";)n.pop();return{bundle:n.join(`
1206
+ WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of n){if(!o.sid)continue;let a=pn(e,o.sid);a&&r.push(a)}return r}function yl(e){let t=tE[e.linkType]??.5,s=bt(e.confidence),n=t*s,r=hl(e.daysApart),o=e.embeddingCosine??.5,a=bt(e.pagerank);if(e.scoring==="pagerank")return bt(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?bt(n):bt(o);let c=.35*n+.2*r+.2*o+.25*a;return bt(c)}function bt(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function iE(e,t,s,n,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 s)a(e,c.session_id);for(let c of s)a(c.session_id,e);for(let c of n)a(e,c.session_id);for(let c of n)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 h of c){let b=o.get(h);if(b)for(let T of b){if(u.has(T))continue;let S=Bt(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 h of m)c.add(h)}}return{edges:o}}function aE(e,t={}){let s=t.iterations??12,n=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<s;d++){let m=new Map(r.map(h=>[h,(1-n)/r.length]));for(let h of r){let b=e.edges.get(h);if(!b||b.size===0)continue;let T=(a.get(h)??0)/b.size;for(let S of b)m.set(S,(m.get(S)??0)+n*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 wl=240;function Rl(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function cE(e){let t=e.decimal?`${e.decimal} `:"",s=e.session_id.slice(0,8),n="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${s})${n}`;if(e.summary){let o=Rl(e.summary,wl);return`${r}
1207
+ ${o}`}return r}function lE(e,t,s){let n=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(n.push(c),o+=dn(c),e.summary){let u=Rl(e.summary,wl*4);n.push(u),o+=dn(u)}n.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,m=dn(d),h=[],b=0;for(let T of u.refs){let S=cE(T),w=dn(S);if(o+m+b+w>s){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}h.push(S),b+=w}if(h.length>0){n.push(d);for(let T of h)n.push(T);n.push(""),o+=m+b}}for(;n.length>0&&n[n.length-1]==="";)n.pop();return{bundle:n.join(`
1208
1208
  `)+`
1209
- `,budgetUsed:o,truncated:r}}function Oh(e,t,s,n,r,o){let a=[];for(let c of s){if(n&&!n.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=Qs(e,u);if(!d)continue;let m=Zc(t.started_at,d.started_at),h=sl({confidence:c.confidence,linkType:c.link_type,daysApart:m,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:h,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(m)}d apart`,link_type:c.link_type})}return a}function en(e,t={}){let s=Math.max(100,Math.floor(t.budget??fh)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??hh)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=Qc(e);if(!u)throw new Error(`session not found: ${e}`);let d=yh(u.project_id),m={session_id:u.id,title:tl(u),decimal:d.table?.byId.get(u.id)??null,summary:el(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,m);let h=wh(d,e),b=Rh(d,e),T=vt(e).filter($=>$.approved).filter($=>!c||c.has($.link_type)).filter($=>o||$.link_type!=="wiki_link"),S=Ah(e,T,h,b,r),w=kh(S),R=[],D=[],O=[],Y=[];for(let $ of T){let se=$.source_session_id===e?$.target_session_id:$.source_session_id,i=Qs(d,se);if(!i)continue;let l=Zc(m.started_at,i.started_at),p=sl({confidence:$.confidence,linkType:$.link_type,daysApart:l,embeddingCosine:null,pagerank:w.get(se)??0,scoring:n}),g=Vc(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"?R.push(E):$.link_type==="similar"?D.push(E):$.link_type==="wiki_link"?Y.push(E):O.push(E)}if(a){let $=Ke({sourceSessionId:e,status:"pending",limit:100}),se=Ke({targetSessionId:e,status:"pending",limit:100}),i=[...$,...se],l=new Set,p=i.filter(_=>l.has(_.id)?!1:(l.add(_.id),!0)),g=Oh(d,m,p,c,n,w);for(let _ of g)_.link_type==="citation"?R.push(_):_.link_type==="similar"?D.push(_):_.link_type==="wiki_link"?Y.push(_):O.push(_)}let M=($,se)=>se.score-$.score;R.sort(M),D.sort(M),O.sort(M),Y.sort(M);let oe=Nh(m,[{heading:"Parents",refs:h},{heading:"Children",refs:b},{heading:"Citations (approved)",refs:R},{heading:"Similar sessions",refs:D},{heading:"Cousins (skill track + temporal)",refs:O},{heading:"Wiki links (manual)",refs:Y}],s);return{origin:m,parents:h,children:b,citations:R,similar:D,cousins:O,wikiLinks:Y,bundle:oe.bundle,budgetUsed:oe.budgetUsed,budgetRemaining:Math.max(0,s-oe.budgetUsed),truncated:oe.truncated}}import{randomUUID as uE}from"node:crypto";H();Q();import{writeFileSync as Mh,mkdirSync as Dh,existsSync as Fh}from"node:fs";import{join as ml}from"node:path";H();var ol=80;function il(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 s=t.split(`
1210
- `)[0].trim();return s.length>ol?s.slice(0,ol)+"\u2026":s}function Lh(e){return f().prepare(`SELECT s.id AS id,
1209
+ `,budgetUsed:o,truncated:r}}function uE(e,t,s,n,r,o){let a=[];for(let c of s){if(n&&!n.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=pn(e,u);if(!d)continue;let m=El(t.started_at,d.started_at),h=yl({confidence:c.confidence,linkType:c.link_type,daysApart:m,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:h,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(m)}d apart`,link_type:c.link_type})}return a}function mn(e,t={}){let s=Math.max(100,Math.floor(t.budget??Vh)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Zh)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=bl(e);if(!u)throw new Error(`session not found: ${e}`);let d=nE(u.project_id),m={session_id:u.id,title:Tl(u),decimal:d.table?.byId.get(u.id)??null,summary:Sl(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,m);let h=rE(d,e),b=oE(d,e),T=Bt(e).filter($=>$.approved).filter($=>!c||c.has($.link_type)).filter($=>o||$.link_type!=="wiki_link"),S=iE(e,T,h,b,r),w=aE(S),R=[],j=[],x=[],q=[];for(let $ of T){let se=$.source_session_id===e?$.target_session_id:$.source_session_id,i=pn(d,se);if(!i)continue;let l=El(m.started_at,i.started_at),p=yl({confidence:$.confidence,linkType:$.link_type,daysApart:l,embeddingCosine:null,pagerank:w.get(se)??0,scoring:n}),g=hl(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"?R.push(E):$.link_type==="similar"?j.push(E):$.link_type==="wiki_link"?q.push(E):x.push(E)}if(a){let $=lt({sourceSessionId:e,status:"pending",limit:100}),se=lt({targetSessionId:e,status:"pending",limit:100}),i=[...$,...se],l=new Set,p=i.filter(_=>l.has(_.id)?!1:(l.add(_.id),!0)),g=uE(d,m,p,c,n,w);for(let _ of g)_.link_type==="citation"?R.push(_):_.link_type==="similar"?j.push(_):_.link_type==="wiki_link"?q.push(_):x.push(_)}let D=($,se)=>se.score-$.score;R.sort(D),j.sort(D),x.sort(D),q.sort(D);let ie=lE(m,[{heading:"Parents",refs:h},{heading:"Children",refs:b},{heading:"Citations (approved)",refs:R},{heading:"Similar sessions",refs:j},{heading:"Cousins (skill track + temporal)",refs:x},{heading:"Wiki links (manual)",refs:q}],s);return{origin:m,parents:h,children:b,citations:R,similar:j,cousins:x,wikiLinks:q,bundle:ie.bundle,budgetUsed:ie.budgetUsed,budgetRemaining:Math.max(0,s-ie.budgetUsed),truncated:ie.truncated}}import{randomUUID as JE}from"node:crypto";U();ee();import{writeFileSync as hE,mkdirSync as EE,existsSync as bE}from"node:fs";import{join as jl}from"node:path";U();var kl=80;function Al(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 s=t.split(`
1210
+ `)[0].trim();return s.length>kl?s.slice(0,kl)+"\u2026":s}function dE(e){return f().prepare(`SELECT s.id AS id,
1211
1211
  sa.alias AS alias,
1212
1212
  s.auto_title AS auto_title,
1213
1213
  s.first_user_message AS first_user_message
1214
1214
  FROM sessions s
1215
1215
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1216
- WHERE s.id = ?`).get(e)??null}function Ch(e){let t=Lh(e);return t?il(t):e.slice(0,8)}function al(e){if(!e)return null;let t=f(),s=t.prepare(`SELECT e.thread_id AS thread_id,
1216
+ WHERE s.id = ?`).get(e)??null}function pE(e){let t=dE(e);return t?Al(t):e.slice(0,8)}function xl(e){if(!e)return null;let t=f(),s=t.prepare(`SELECT e.thread_id AS thread_id,
1217
1217
  t.name AS thread_name,
1218
1218
  e.parent_session_id AS parent_session_id,
1219
1219
  e.added_at AS added_at
@@ -1222,7 +1222,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1222
1222
  WHERE e.session_id = ?
1223
1223
  AND t.archived = 0
1224
1224
  ORDER BY e.added_at DESC
1225
- LIMIT 1`).get(e);if(!s)return null;let n=s.parent_session_id?{id:s.parent_session_id,title:Ch(s.parent_session_id)}:null,r=s.parent_session_id?[e,s.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1225
+ LIMIT 1`).get(e);if(!s)return null;let n=s.parent_session_id?{id:s.parent_session_id,title:pE(s.parent_session_id)}:null,r=s.parent_session_id?[e,s.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1226
1226
  s.id AS id,
1227
1227
  sa.alias AS alias,
1228
1228
  s.auto_title AS auto_title,
@@ -1232,33 +1232,33 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1232
1232
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1233
1233
  WHERE e.thread_id = ?
1234
1234
  AND e.session_id NOT IN (${o})
1235
- ORDER BY e.added_at ASC`).all(s.thread_id,...r).map(u=>({id:u.session_id,title:u.id?il(u):u.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:c}}var cl=[/^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 Wr(e){return cl.some(t=>t.test(e))}var vh=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i],Ih=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],jh=20;function Xt(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<jh)return"low_signal";for(let t of cl)if(t.test(e.auto_title))return"recursive_meta";for(let t of vh)if(t.test(e.auto_title))return"programmatic";for(let t of Ih)if(t.test(e.auto_title))return"template_pending";return"clean"}function tn(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function ll(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let s=e.slice(0,t),n=e.slice(t+3),r=tn(n);return!r||r===n?e:`${s} \xB7 ${r}`}var Jr=ml(U,"titles"),Ph=80,Uh=60,$h=100,Bh=50,Jt=5,sn=15,Hh=500;function gl(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>!!s&&typeof s=="object"&&typeof s.title=="string"&&typeof s.replaced_at=="string")}catch{}return[]}function ct(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 s=Wh(t,e);if(s)return s;let n=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(n&&n.length<=Ph?n:t.slice(0,Uh)).trim()||null}function Wh(e,t){let s=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(s){let n=`/${s[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=Yr(r);return o?at(`${n} \xB7 ${o}`):n}for(let n of qh){if(!e.match(n.match))continue;let o=n.prefix,a=n.extract?n.extract(e,t):Yr(t);return a?n.completeFromExtract?at(a):at(`${o} \xB7 ${a}`):o}for(let n of Yh){if(!e.match(n.match))continue;let o=n.extract?n.extract(e,t):nn(t);return o?n.completeFromExtract?at(o):at(`${n.prefix} \xB7 ${o}`):n.prefix}return null}var qh=[{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 n=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=Yr(t);return n&&r?`${n} \xB7 ${r}`:n||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let s=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return s?s.split("/").filter(Boolean).slice(-2).join("/")||s:null}},{match:/^(?:read|inspect) this and then begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>Jh(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let s=t.match(/\.claude\/skills\/([^/\s]+)/);return s?.[1]?`[skill] ${s[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>Xh(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>ul(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)=>ul(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 s=t[1],n=t[2]?.trim();return n?`Implementing ${s} \xB7 ${n}`:`Implementing ${s}`}}];function Xh(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),s=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),n=t?.[1]?.trim(),r=s?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return n&&o?`[output-index] \xB7 ${n} \xB7 ${o}`:n?`[output-index] \xB7 ${n}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function ul(e,t){if(!e)return t;let s=e.indexOf("Messages:");if(s===-1)return t;let n=e.slice(s+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of n.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 Jh(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 s=t[1].split("/").filter(Boolean);return(s[s.length-1]??"").replace(/\.[^.]+$/,"")||null}var Yh=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>nn(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let n=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=n?`${n} co-pilot`:"co-pilot",o=nn(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>nn(t)}];function nn(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,s;for(;(s=t.exec(e))!==null;){let n=s[2].trim();if(!n||/^(null|none|n\/a|—|-)$/i.test(n))continue;let r=n.replace(/\s+/g," ");return tn(r)||r}return null}function at(e){return e.slice(0,$h).trim()}var Gh=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],zh=[/^(?: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 qr(e){let t=e.trim();return t.length<3?!0:Gh.some(s=>s.test(t))}function Kh(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return zh.some(s=>s.test(t))}function Yr(e){let t=Vh(e);return t===null?null:tn(t)||t}function Vh(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,s,n=[];for(;(s=t.exec(e))!==null;){let u=s[2].trim(),d=s[3].trim().replace(/\s+/g," ");if(!qr(d)){if(Kh(u))return d;n.push(d)}}if(n.length>0)return n[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!qr(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(s=o.exec(e))!==null;){let u=s[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!qr(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(`
1235
+ ORDER BY e.added_at ASC`).all(s.thread_id,...r).map(u=>({id:u.session_id,title:u.id?Al(u):u.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:c}}var Nl=[/^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 ro(e){return Nl.some(t=>t.test(e))}var mE=[/^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 Ol(e){return e?mE.some(t=>t.test(e)):!1}var gE=[/^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],_E=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],fE=20;function ts(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<fE)return"low_signal";for(let t of Nl)if(t.test(e.auto_title))return"recursive_meta";for(let t of gE)if(t.test(e.auto_title))return"programmatic";for(let t of _E)if(t.test(e.auto_title))return"template_pending";return"clean"}function gn(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function Ll(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let s=e.slice(0,t),n=e.slice(t+3),r=gn(n);return!r||r===n?e:`${s} \xB7 ${r}`}var ao=jl(B,"titles"),SE=80,TE=60,yE=100,wE=50,ss=5,_n=15,RE=500;function Ml(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>!!s&&typeof s=="object"&&typeof s.title=="string"&&typeof s.replaced_at=="string")}catch{}return[]}function Tt(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 s=kE(t,e);if(s)return s;let n=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(n&&n.length<=SE?n:t.slice(0,TE)).trim()||null}function kE(e,t){let s=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(s){let n=`/${s[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=co(r);return o?St(`${n} \xB7 ${o}`):n}for(let n of AE){if(!e.match(n.match))continue;let o=n.prefix,a=n.extract?n.extract(e,t):co(t);return a?n.completeFromExtract?St(a):St(`${o} \xB7 ${a}`):o}for(let n of OE){if(!e.match(n.match))continue;let o=n.extract?n.extract(e,t):fn(t);return o?n.completeFromExtract?St(o):St(`${n.prefix} \xB7 ${o}`):n.prefix}return null}var AE=[{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 n=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=co(t);return n&&r?`${n} \xB7 ${r}`:n||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let s=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return s?s.split("/").filter(Boolean).slice(-2).join("/")||s:null}},{match:/^(?:read|inspect) this and then begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>NE(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let s=t.match(/\.claude\/skills\/([^/\s]+)/);return s?.[1]?`[skill] ${s[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>xE(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>Cl(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)=>Cl(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 s=t[1],n=t[2]?.trim();return n?`Implementing ${s} \xB7 ${n}`:`Implementing ${s}`}}];function xE(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),s=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),n=t?.[1]?.trim(),r=s?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return n&&o?`[output-index] \xB7 ${n} \xB7 ${o}`:n?`[output-index] \xB7 ${n}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function Cl(e,t){if(!e)return t;let s=e.indexOf("Messages:");if(s===-1)return t;let n=e.slice(s+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of n.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 NE(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 s=t[1].split("/").filter(Boolean);return(s[s.length-1]??"").replace(/\.[^.]+$/,"")||null}var OE=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>fn(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let n=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=n?`${n} co-pilot`:"co-pilot",o=fn(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>fn(t)}];function fn(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,s;for(;(s=t.exec(e))!==null;){let n=s[2].trim();if(!n||/^(null|none|n\/a|—|-)$/i.test(n))continue;let r=n.replace(/\s+/g," ");return gn(r)||r}return null}function St(e){return e.slice(0,yE).trim()}var LE=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],CE=[/^(?: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 oo(e){let t=e.trim();return t.length<3?!0:LE.some(s=>s.test(t))}function IE(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return CE.some(s=>s.test(t))}function co(e){let t=vE(e);return t===null?null:gn(t)||t}function vE(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,s,n=[];for(;(s=t.exec(e))!==null;){let u=s[2].trim(),d=s[3].trim().replace(/\s+/g," ");if(!oo(d)){if(IE(u))return d;n.push(d)}}if(n.length>0)return n[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!oo(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(s=o.exec(e))!==null;){let u=s[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!oo(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(`
1236
1236
  `).map(d=>d.trim()).find(d=>d.length>=4);if(u)return u.slice(0,60)}let c=e.split(`
1237
- `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function Gr(e){let t=f(),s=t.prepare(`SELECT rowid AS rid, content_text
1237
+ `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function lo(e){let t=f(),s=t.prepare(`SELECT rowid AS rid, content_text
1238
1238
  FROM messages
1239
1239
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1240
1240
  AND content_text IS NOT NULL AND content_text != ''
1241
1241
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1242
- LIMIT ?`).all(e,Jt),n=t.prepare(`SELECT rowid AS rid, content_text
1242
+ LIMIT ?`).all(e,ss),n=t.prepare(`SELECT rowid AS rid, content_text
1243
1243
  FROM messages
1244
1244
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1245
1245
  AND content_text IS NOT NULL AND content_text != ''
1246
1246
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1247
- LIMIT ?`).all(e,sn),r=new Map;for(let m of s)r.set(m.rid,m.content_text);for(let m of n)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,h)=>m[0]-h[0]).map(([,m])=>({content_text:m})),a=s.length===Jt&&n.length===sn&&r.size===Jt+sn,c=o.map((m,h)=>{let b=(m.content_text??"").slice(0,Hh);return a&&h===Jt?`--- (middle of session omitted) ---
1247
+ LIMIT ?`).all(e,_n),r=new Map;for(let m of s)r.set(m.rid,m.content_text);for(let m of n)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,h)=>m[0]-h[0]).map(([,m])=>({content_text:m})),a=s.length===ss&&n.length===_n&&r.size===ss+_n,c=o.map((m,h)=>{let b=(m.content_text??"").slice(0,RE);return a&&h===ss?`--- (middle of session omitted) ---
1248
1248
  ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1249
- `),u=null;try{u=al(e)}catch(m){console.error("[autoTitle] thread context resolution failed:",m),u=null}let d=[];return u&&(d.push(Zh(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${Jt}`,`messages (initial intent) and the last ${sn} 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(`
1250
- `)}var Xr=5;function Zh(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 s=e.siblings.length;if(s>0){let r=e.siblings.slice(0,Xr).map(a=>`"${a.title}"`).join(", "),o=s>Xr?`, and ${s-Xr} more`:"";t.push(`- Sibling sessions (${s}): ${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(`
1251
- `)}async function _l(e){let t=Gr(e),{spawnClaudePrompt:s,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(he(),We));if(!n())throw new Error("claude CLI not found on PATH");let r=await s(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=Qh(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,Bh)}function Qh(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return dl(n)}}catch{}return dl(t)}function dl(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function me(e,t,s){let n=t.trim();if(!n)return;let r=f(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1252
- FROM sessions WHERE id = ?`).get(e);if(!o||s==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===n&&o.auto_title_source===s)return;let a=gl(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
1249
+ `),u=null;try{u=xl(e)}catch(m){console.error("[autoTitle] thread context resolution failed:",m),u=null}let d=[];return u&&(d.push(jE(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${ss}`,`messages (initial intent) and the last ${_n} 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(`
1250
+ `)}var io=5;function jE(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 s=e.siblings.length;if(s>0){let r=e.siblings.slice(0,io).map(a=>`"${a.title}"`).join(", "),o=s>io?`, and ${s-io} more`:"";t.push(`- Sibling sessions (${s}): ${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(`
1251
+ `)}async function Dl(e){let t=lo(e),{spawnClaudePrompt:s,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(ye(),tt));if(!n())throw new Error("claude CLI not found on PATH");let r=await s(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=ME(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,wE)}function ME(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return Il(n)}}catch{}return Il(t)}function Il(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function Ee(e,t,s){let n=t.trim();if(!n)return;let r=f(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1252
+ FROM sessions WHERE id = ?`).get(e);if(!o||s==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===n&&o.auto_title_source===s)return;let a=Ml(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
1253
1253
  SET auto_title = ?,
1254
1254
  auto_title_source = ?,
1255
1255
  auto_title_generated_at = ?,
1256
1256
  auto_title_history = ?
1257
- WHERE id = ?`).run(n,s,Date.now(),JSON.stringify(a),e),nE(e,n,s,c)}function ke(e){let t=f().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1258
- 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:gl(t.auto_title_history)}:null}function fl(){let t=f().prepare(`SELECT id, first_user_message
1257
+ WHERE id = ?`).run(n,s,Date.now(),JSON.stringify(a),e),UE(e,n,s,c)}function Le(e){let t=f().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1258
+ 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:Ml(t.auto_title_history)}:null}function Fl(){let t=f().prepare(`SELECT id, first_user_message
1259
1259
  FROM sessions
1260
1260
  WHERE auto_title IS NULL
1261
- AND first_user_message IS NOT NULL`).all(),s=0;for(let n of t){let r=ct(n.first_user_message);r&&(me(n.id,r,"heuristic"),s+=1)}return{updated:s}}function hl(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1261
+ AND first_user_message IS NOT NULL`).all(),s=0;for(let n of t){let r=Tt(n.first_user_message);r&&(Ee(n.id,r,"heuristic"),s+=1)}return{updated:s}}function Pl(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1262
1262
  SELECT auto_title, project_id
1263
1263
  FROM sessions
1264
1264
  WHERE auto_title IS NOT NULL
@@ -1311,7 +1311,7 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1311
1311
  AND content_text IS NOT NULL
1312
1312
  AND content_text != ''
1313
1313
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1314
- LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=null;for(let h of d){let b=h.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!b||/^<local-command-caveat>/.test(b))continue;let T=ct(b);if(T&&!pl(T)){m=T;break}}if(!m){let h=d.map(T=>T.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(T=>T.length>0&&!/^<local-command-caveat>/.test(T)),b=h?ct(h):null;b&&pl(b)&&(m=`[vacuous] ${b}`)}m&&(me(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function pl(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(n=>n.test(t))}function El(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1314
+ LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=null;for(let h of d){let b=h.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!b||/^<local-command-caveat>/.test(b))continue;let T=Tt(b);if(T&&!vl(T)){m=T;break}}if(!m){let h=d.map(T=>T.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(T=>T.length>0&&!/^<local-command-caveat>/.test(T)),b=h?Tt(h):null;b&&vl(b)&&(m=`[vacuous] ${b}`)}m&&(Ee(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function vl(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(n=>n.test(t))}function Ul(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1315
1315
  FROM sessions s
1316
1316
  WHERE s.auto_title_source = 'heuristic'${s}
1317
1317
  AND (
@@ -1326,75 +1326,75 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1326
1326
  AND content_text IS NOT NULL
1327
1327
  AND content_text != ''
1328
1328
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1329
- LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=eE(d,u.auto_title);m&&(me(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function eE(e,t){for(let s of e){let n=tE(s.content_text);if(!n||Wr(n))continue;let r=ct(n);if(r){if(r===t)return null;if(!Wr(r))return r}}return t.startsWith("[meta]")?null:at(`[meta] ${t}`)}function bl(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1329
+ LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=DE(d,u.auto_title);m&&(Ee(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function DE(e,t){for(let s of e){let n=FE(s.content_text);if(!n||ro(n))continue;let r=Tt(n);if(r){if(r===t)return null;if(!ro(r))return r}}return t.startsWith("[meta]")?null:St(`[meta] ${t}`)}function $l(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1330
1330
  FROM sessions s
1331
1331
  WHERE s.auto_title_source = 'heuristic'${s}
1332
1332
  AND s.auto_title IS NOT NULL
1333
1333
  AND s.auto_title LIKE '% \xB7 %'
1334
- AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...n),o=0,a=0;for(let c of r){o+=1;let u=ll(c.auto_title);u!==c.auto_title&&(me(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function tE(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 sE(){J(),Fh(Jr)||Dh(Jr,{recursive:!0})}function nE(e,t,s,n){try{sE();let r=ml(Jr,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
1335
- `;Mh(r,o+t+`
1336
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var rE=50;function oE(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),s=new Map,n=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=s.get(u.parent_session_id)??[];d.push(u),s.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(n.has(u.session_id))continue;n.add(u.session_id),r.push(u.session_id);let d=s.get(u.session_id)??[];for(let m of d)n.has(m.session_id)||c.push(m)}for(let u of t)n.has(u.session_id)||(n.add(u.session_id),r.push(u.session_id));return r}function iE(e){let t=Gr(e.sessionId),s=["",`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(`
1334
+ AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...n),o=0,a=0;for(let c of r){o+=1;let u=Ll(c.auto_title);u!==c.auto_title&&(Ee(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function FE(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 PE(){z(),bE(ao)||EE(ao,{recursive:!0})}function UE(e,t,s,n){try{PE();let r=jl(ao,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
1335
+ `;hE(r,o+t+`
1336
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var $E=50;function HE(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),s=new Map,n=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=s.get(u.parent_session_id)??[];d.push(u),s.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(n.has(u.session_id))continue;n.add(u.session_id),r.push(u.session_id);let d=s.get(u.session_id)??[];for(let m of d)n.has(m.session_id)||c.push(m)}for(let u of t)n.has(u.session_id)||(n.add(u.session_id),r.push(u.session_id));return r}function BE(e){let t=lo(e.sessionId),s=["",`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(`
1337
1337
  `);return`${t}
1338
- ${s}`}function aE(e){return ke(e)?.auto_title_source??null}async function cE(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(he(),We));if(!s())throw new Error("claude CLI not found on PATH");let n=await t(e.prompt,[],{model:e.model});if(!n.success)throw new Error(`claude CLI exited ${n.exitCode}: ${n.stderr.slice(-500)}`);let r=lE(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,rE)}function lE(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return Sl(n)}}catch{}return Sl(t)}function Sl(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function yl(e,t={}){let s=re(e);if(!s)throw new Error(`thread not found: ${e}`);let n=oE(s.edges),r=n.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let m=0;m<n.length;m++){let h=n[m],b=m+1;if(a?.aborted){let S={sessionId:h,reason:"cancelled"};u.push(S),t.onSkipped?.(S);continue}if(!o&&aE(h)==="agent"){let S={sessionId:h,reason:"already-titled"};u.push(S),t.onSkipped?.(S);continue}let T;try{if(Tl)T=await Tl({sessionId:h,current:b,total:r});else{let S=iE({sessionId:h,current:b,total:r});T=await cE({prompt:S,model:t.model})}}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),R={sessionId:h,error:w};d.push(R),t.onFailed?.(R);continue}try{me(h,T,"agent")}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),R={sessionId:h,error:`setAutoTitle failed: ${w}`};d.push(R),t.onFailed?.(R);continue}c.push(h),t.onProgress?.({current:b,total:r,sessionId:h,title:T})}return{generated:c,skipped:u,failed:d}}var Tl=null;var Gt=new Map,dE=300*1e3;function Yt(e,t,s){let n=e.events.length+1;e.events.push({id:n,kind:t,data:s});for(let r of e.waiters)r.resolve();e.waiters.clear()}function pE(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{Gt.delete(e.jobId)},dE)}function mE(e){let t=0,s=0,n=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(s+=1),r.kind==="error"&&(n+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:s,failed:n,result:e.result}}function wl(e){let t=uE(),s=new AbortController,n={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:s,result:null,cleanupTimer:null};return Gt.set(t,n),(async()=>{await Promise.resolve();try{let r=await yl(e.threadId,{force:e.force??!1,signal:s.signal,model:e.model,onProgress:o=>{n.total=o.total,Yt(n,"progress",o)},onSkipped:o=>{Yt(n,"skipped",o)},onFailed:o=>{Yt(n,"error",o)}});n.result=r,n.status=s.signal.aborted?"cancelled":"done",n.endedAt=new Date().toISOString(),Yt(n,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");n.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},n.status="failed",n.endedAt=new Date().toISOString(),Yt(n,"done",n.result)}finally{pE(n)}})(),t}async function*Rl(e,t=0){let s=Gt.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(n+=1,yield r,r.kind==="done")return}if(s.endedAt)return;await new Promise(r=>{s.waiters.add({resolve:r})})}}function Al(e){let t=Gt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function zr(e){let t=Gt.get(e);return t?mE(t):null}Q();import{existsSync as gE,readFileSync as _E,writeFileSync as fE}from"node:fs";import{join as hE}from"node:path";import{z as pe}from"zod";var Kr=hE(U,"terminals.json"),kl=1440*60*1e3,EE=3e4,bE=6e4,SE=pe.object({shell_pid:pe.number(),tab_name:pe.string(),cwd:pe.string().nullable().optional(),opened_at:pe.string(),last_seen_at:pe.string()}),TE=pe.object({schema:pe.string().optional(),saved_at:pe.string().optional(),terminals:pe.array(SE).max(500).default([]),sessions_by_pid:pe.record(pe.string(),pe.array(pe.string()).max(50)).optional().default({})}),xl=/^[⠀-⣿✳\s]+/,Nl=/^\d+(\.\d+){1,3}$/;function ie(e){let t=e.trim();return!!(!t||xl.test(t)||Nl.test(t))}function lt(e){let t=e.trim();if(!t||Nl.test(t))return null;let s=t.replace(xl,"").trim();return s.length>0?s:null}function Vr(e,t){if(!ie(e))return e;let s=lt(e);return s||(t&&!ie(t)?t:e)}function yE(e){let t=e.now-e.withinMs,s=e.pending.filter(n=>{let r=Date.parse(n.started_at);return Number.isFinite(r)&&r>=t});if(s.length===0)return{kind:"none"};if(e.shellPid!=null){let n=s.find(r=>r.shell_pid===e.shellPid);if(n)return{kind:"pid-match",entry:n}}if(e.cwd){let n=e.cwd.replace(/\/+$/,""),r=s.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===n);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var Zr=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,!!gE(Kr)))try{let t=_E(Kr,"utf8"),s=JSON.parse(t),n=TE.safeParse(s);if(!n.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",n.error.issues);return}let r=n.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{J();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(([s,n])=>[String(s),Array.from(n)]))};fE(Kr,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=Vr(t.tab_name,n?.tab_name),o=n?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:s};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,s){this.ensureLoaded();let n=this.entries.get(t);if(!n)return null;let r=Vr(s,n.tab_name),o={...n,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let s=this.entries.delete(t),n=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(s||n)&&this.save(),s}claimPidOwnership(t,s,n=Date.now()){if(this.ensureLoaded(),!s)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===s?(r.last_claim_at=n,"refreshed"):n-r.last_claim_at>bE?(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,s])=>({shell_pid:t,instance_id:s.instance_id,last_claim_at:s.last_claim_at}))}sync(t){this.ensureLoaded();let s=new Date().toISOString(),n=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=Vr(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:s}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:n++}return this.gc(),this.save(),{added:n,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,s){this.ensureLoaded();let n=this.sessionsByPid.get(s);n||(n=new Set,this.sessionsByPid.set(s,n)),n.has(t)||(n.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let s of this.sessionsByPid.values())if(s.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let s=!1;for(let[n,r]of this.sessionsByPid)r.delete(t)&&(s=!0,r.size===0&&this.sessionsByPid.delete(n));return s&&this.save(),s}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let s=yE({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(s.kind==="pid-match"||s.kind==="singleton-cwd"){let n=this.pendingClaudeStarts.indexOf(s.entry);n>=0&&this.pendingClaudeStarts.splice(n,1)}return s}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,s,n,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:s,queued_at:Date.now(),cwd:n,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,s])=>({session_id:t,...s}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[s,n]of this.deferredLinks)n.queued_at<t&&this.deferredLinks.delete(s)}gcPending(){let t=Date.now()-EE;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(s=>{let n=Date.parse(s.started_at);return Number.isFinite(n)&&n>=t}))}outputTails=new Map;setOutputTail(t,s,n){this.ensureLoaded(),this.outputTails.set(t,{text:s,captured_at:n})}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,s){this.ensureLoaded(),this.origins.set(t,s),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()-kl;for(let[s,n]of this.entries){let r=Date.parse(n.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(s)}}gcOrigins(){let t=Date.now()-kl;for(let[s,n]of this.origins)n.detectedAt<t&&this.origins.delete(s)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let n=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>=n)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,s=0;for(let n of[...this.entries.keys()]){let r=!0;try{process.kill(n,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(n),t++;let o=this.sessionsByPid.get(n);o&&(s+=o.size,this.sessionsByPid.delete(n)),this.outputTails.delete(n),this.pidOwnership.delete(n)}return(t||s)&&this.save(),{pruned_pids:t,pruned_sessions:s}}},v=new Zr;import{execFile as vE}from"node:child_process";import{promisify as IE}from"node:util";import{existsSync as jE}from"node:fs";import{basename as ME}from"node:path";H();import{execFile as wE}from"node:child_process";import{readFile as RE}from"node:fs/promises";import{promisify as AE}from"node:util";var kE=AE(wE),Ol=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function xE(e){let t={};for(let s of Ol){let n=e[s];typeof n=="string"&&n.length>0&&(t[s]=n)}return t}function NE(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 s=e.TERM_PROGRAM;return s==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:s==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:s==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:s==="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 OE(e){if(process.platform==="linux")try{let t=await RE(`/proc/${e}/environ`,"utf8");return LE(t)}catch{return{}}try{let{stdout:t}=await kE("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return CE(t)}catch{return{}}}function LE(e){let t={};for(let s of e.split("\0")){if(!s)continue;let n=s.indexOf("=");if(n<=0)continue;let r=s.slice(0,n),o=s.slice(n+1);t[r]=o}return t}function CE(e){let t={},s=new Set(Ol),n=e.replace(/\s+/g," ").trim().split(" ");for(let r of n){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);s.has(a)&&(t[a]=r.slice(o+1))}return t}async function Ll(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await OE(e),s=xE(t);return NE(s)}catch{return null}}var on=IE(vE),rn;function DE(){if(rn!==void 0)return rn;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(jE(t))return rn=t,t;return rn=null,null}var FE=3,PE=3600*1e3,zt=new Map;function Il(){let e=v.all(),t=e.map(s=>s.shell_pid).sort((s,n)=>s-n).join(",");return`${e.length}:${t}`}function UE(e){let t=zt.get(e);return t?Date.now()-t.lastAt>PE?(zt.delete(e),!1):t.refusals<FE?!1:t.fingerprint===Il():!1}function Cl(e){let t=Il(),s=zt.get(e);s&&s.fingerprint===t?(s.refusals+=1,s.lastAt=Date.now()):zt.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function $E(e){zt.delete(e)}async function BE(e){let t=DE();if(!t)return null;try{let{stdout:s}=await on(t,["-Fpc",e],{timeout:2e3}),n=s.split(`
1339
- `),r=null,o=null,a=null;for(let c of n)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 vl(e){try{let{stdout:t}=await on("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),s=Number(t.trim());return Number.isFinite(s)&&s>0?s:null}catch{return null}}async function HE(e){try{let{stdout:t}=await on("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),s=Date.parse(t.trim());return Number.isFinite(s)?s:null}catch{return null}}var WE=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 ce(e){let t=e.trim().toLowerCase();if(!t)return!0;let s=t.replace(/^[-/]+/,"").replace(/^.*\//,"").replace(/\s*\(\d+\)\s*$/,"").trim();return WE.has(s)}function ut(e){let t=e.tabName?.trim();return t&&!ce(t)&&!ie(t)?t:null}function qE(e){try{return f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function an(e){let t=ME(e,".jsonl");if(Ae(t)||UE(t))return;let s=qE(t),n=await BE(e),r=n?await vl(n):null,o=n?await Ll(n):null;o&&v.setOrigin(t,o);let a=null,c=null,u=null,d=v.takePendingMatched({shellPid:r,cwd:s?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${s?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),Cl(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=v.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&&n){let S=n;for(let w=0;w<4&&S!=null;w++){let R=await vl(S);if(!R)break;let D=v.get(R);if(D){a=D,c=R,u="ppid";break}m==null&&(m=R),S=R}}if(!a&&s?.cwd){let S=s.cwd.replace(/\/+$/,""),w=v.all().filter(R=>R.cwd&&R.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`),Cl(t))}let h=null;if(a?.tab_name&&!ce(a.tab_name)&&!ie(a.tab_name))h=a.tab_name;else if(a?.tab_name&&ie(a.tab_name)){let S=lt(a.tab_name);S&&!ce(S)&&(h=S)}if(!h&&!(u==="pending-pid"||u==="pending-cwd")&&a&&s?.cwd){let S=s.cwd.replace(/\/+$/,""),w=v.all().filter(R=>R.shell_pid!==c&&R.cwd&&R.cwd.replace(/\/+$/,"")===S&&!ce(R.tab_name)&&!ie(R.tab_name)).sort((R,D)=>Date.parse(D.last_seen_at)-Date.parse(R.last_seen_at))[0];w&&(h=w.tab_name)}let T=ut({tabName:h,origin:o,cwd:s?.cwd??null,gitBranch:s?.git_branch??null});if(m!=null&&!c&&v.deferSessionLink(t,m,s?.cwd??null,s?.git_branch??null),!!T)try{be(t,T),$E(t);let S=!!h&&!ce(h)&&!ie(h)&&T===h.trim();c!=null&&v.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 XE(){try{let{stdout:e}=await on("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let s of e.split(`
1340
- `)){let n=s.trim();if(!n)continue;let r=n.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 JE=9e4;function YE(e){let t=(Date.now()-JE)/1e3,s=e.replace(/\/+$/,"");return f().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1338
+ ${s}`}function WE(e){return Le(e)?.auto_title_source??null}async function qE(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(ye(),tt));if(!s())throw new Error("claude CLI not found on PATH");let n=await t(e.prompt,[],{model:e.model});if(!n.success)throw new Error(`claude CLI exited ${n.exitCode}: ${n.stderr.slice(-500)}`);let r=XE(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,$E)}function XE(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return Hl(n)}}catch{}return Hl(t)}function Hl(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function Wl(e,t={}){let s=oe(e);if(!s)throw new Error(`thread not found: ${e}`);let n=HE(s.edges),r=n.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let m=0;m<n.length;m++){let h=n[m],b=m+1;if(a?.aborted){let S={sessionId:h,reason:"cancelled"};u.push(S),t.onSkipped?.(S);continue}if(!o&&WE(h)==="agent"){let S={sessionId:h,reason:"already-titled"};u.push(S),t.onSkipped?.(S);continue}let T;try{if(Bl)T=await Bl({sessionId:h,current:b,total:r});else{let S=BE({sessionId:h,current:b,total:r});T=await qE({prompt:S,model:t.model})}}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),R={sessionId:h,error:w};d.push(R),t.onFailed?.(R);continue}try{Ee(h,T,"agent")}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),R={sessionId:h,error:`setAutoTitle failed: ${w}`};d.push(R),t.onFailed?.(R);continue}c.push(h),t.onProgress?.({current:b,total:r,sessionId:h,title:T})}return{generated:c,skipped:u,failed:d}}var Bl=null;var rs=new Map,GE=300*1e3;function ns(e,t,s){let n=e.events.length+1;e.events.push({id:n,kind:t,data:s});for(let r of e.waiters)r.resolve();e.waiters.clear()}function YE(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{rs.delete(e.jobId)},GE)}function zE(e){let t=0,s=0,n=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(s+=1),r.kind==="error"&&(n+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:s,failed:n,result:e.result}}function ql(e){let t=JE(),s=new AbortController,n={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:s,result:null,cleanupTimer:null};return rs.set(t,n),(async()=>{await Promise.resolve();try{let r=await Wl(e.threadId,{force:e.force??!1,signal:s.signal,model:e.model,onProgress:o=>{n.total=o.total,ns(n,"progress",o)},onSkipped:o=>{ns(n,"skipped",o)},onFailed:o=>{ns(n,"error",o)}});n.result=r,n.status=s.signal.aborted?"cancelled":"done",n.endedAt=new Date().toISOString(),ns(n,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");n.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},n.status="failed",n.endedAt=new Date().toISOString(),ns(n,"done",n.result)}finally{YE(n)}})(),t}async function*Xl(e,t=0){let s=rs.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(n+=1,yield r,r.kind==="done")return}if(s.endedAt)return;await new Promise(r=>{s.waiters.add({resolve:r})})}}function Jl(e){let t=rs.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function uo(e){let t=rs.get(e);return t?zE(t):null}ee();import{existsSync as KE,readFileSync as VE,writeFileSync as ZE}from"node:fs";import{join as QE}from"node:path";import{z as fe}from"zod";var po=QE(B,"terminals.json"),Gl=1440*60*1e3,eb=3e4,tb=6e4,sb=fe.object({shell_pid:fe.number(),tab_name:fe.string(),cwd:fe.string().nullable().optional(),opened_at:fe.string(),last_seen_at:fe.string()}),nb=fe.object({schema:fe.string().optional(),saved_at:fe.string().optional(),terminals:fe.array(sb).max(500).default([]),sessions_by_pid:fe.record(fe.string(),fe.array(fe.string()).max(50)).optional().default({})}),Yl=/^[⠀-⣿✳\s]+/,zl=/^\d+(\.\d+){1,3}$/;function ce(e){let t=e.trim();return!!(!t||Yl.test(t)||zl.test(t))}function yt(e){let t=e.trim();if(!t||zl.test(t))return null;let s=t.replace(Yl,"").trim();return s.length>0?s:null}function mo(e,t){if(!ce(e))return e;let s=yt(e);return s||(t&&!ce(t)?t:e)}function rb(e){let t=e.now-e.withinMs,s=e.pending.filter(n=>{let r=Date.parse(n.started_at);return Number.isFinite(r)&&r>=t});if(s.length===0)return{kind:"none"};if(e.shellPid!=null){let n=s.find(r=>r.shell_pid===e.shellPid);if(n)return{kind:"pid-match",entry:n}}if(e.cwd){let n=e.cwd.replace(/\/+$/,""),r=s.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===n);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var go=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,!!KE(po)))try{let t=VE(po,"utf8"),s=JSON.parse(t),n=nb.safeParse(s);if(!n.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",n.error.issues);return}let r=n.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(([s,n])=>[String(s),Array.from(n)]))};ZE(po,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=mo(t.tab_name,n?.tab_name),o=n?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:s};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,s){this.ensureLoaded();let n=this.entries.get(t);if(!n)return null;let r=mo(s,n.tab_name),o={...n,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let s=this.entries.delete(t),n=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(s||n)&&this.save(),s}claimPidOwnership(t,s,n=Date.now()){if(this.ensureLoaded(),!s)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===s?(r.last_claim_at=n,"refreshed"):n-r.last_claim_at>tb?(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,s])=>({shell_pid:t,instance_id:s.instance_id,last_claim_at:s.last_claim_at}))}sync(t){this.ensureLoaded();let s=new Date().toISOString(),n=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=mo(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:s}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:n++}return this.gc(),this.save(),{added:n,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,s){this.ensureLoaded();let n=this.sessionsByPid.get(s);n||(n=new Set,this.sessionsByPid.set(s,n)),n.has(t)||(n.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let s of this.sessionsByPid.values())if(s.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let s=!1;for(let[n,r]of this.sessionsByPid)r.delete(t)&&(s=!0,r.size===0&&this.sessionsByPid.delete(n));return s&&this.save(),s}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let s=rb({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(s.kind==="pid-match"||s.kind==="singleton-cwd"){let n=this.pendingClaudeStarts.indexOf(s.entry);n>=0&&this.pendingClaudeStarts.splice(n,1)}return s}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,s,n,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:s,queued_at:Date.now(),cwd:n,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,s])=>({session_id:t,...s}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[s,n]of this.deferredLinks)n.queued_at<t&&this.deferredLinks.delete(s)}gcPending(){let t=Date.now()-eb;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(s=>{let n=Date.parse(s.started_at);return Number.isFinite(n)&&n>=t}))}outputTails=new Map;setOutputTail(t,s,n){this.ensureLoaded(),this.outputTails.set(t,{text:s,captured_at:n})}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,s){this.ensureLoaded(),this.origins.set(t,s),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()-Gl;for(let[s,n]of this.entries){let r=Date.parse(n.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(s)}}gcOrigins(){let t=Date.now()-Gl;for(let[s,n]of this.origins)n.detectedAt<t&&this.origins.delete(s)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let n=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>=n)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,s=0;for(let n of[...this.entries.keys()]){let r=!0;try{process.kill(n,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(n),t++;let o=this.sessionsByPid.get(n);o&&(s+=o.size,this.sessionsByPid.delete(n)),this.outputTails.delete(n),this.pidOwnership.delete(n)}return(t||s)&&this.save(),{pruned_pids:t,pruned_sessions:s}}},v=new go;import{execFile as gb}from"node:child_process";import{promisify as _b}from"node:util";import{existsSync as fb}from"node:fs";import{basename as hb}from"node:path";U();import{execFile as ob}from"node:child_process";import{readFile as ib}from"node:fs/promises";import{promisify as ab}from"node:util";var cb=ab(ob),Kl=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function lb(e){let t={};for(let s of Kl){let n=e[s];typeof n=="string"&&n.length>0&&(t[s]=n)}return t}function ub(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 s=e.TERM_PROGRAM;return s==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:s==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:s==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:s==="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 db(e){if(process.platform==="linux")try{let t=await ib(`/proc/${e}/environ`,"utf8");return pb(t)}catch{return{}}try{let{stdout:t}=await cb("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return mb(t)}catch{return{}}}function pb(e){let t={};for(let s of e.split("\0")){if(!s)continue;let n=s.indexOf("=");if(n<=0)continue;let r=s.slice(0,n),o=s.slice(n+1);t[r]=o}return t}function mb(e){let t={},s=new Set(Kl),n=e.replace(/\s+/g," ").trim().split(" ");for(let r of n){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);s.has(a)&&(t[a]=r.slice(o+1))}return t}async function Vl(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await db(e),s=lb(t);return ub(s)}catch{return null}}var En=_b(gb),hn;function Eb(){if(hn!==void 0)return hn;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(fb(t))return hn=t,t;return hn=null,null}var bb=3,Sb=3600*1e3,os=new Map;function eu(){let e=v.all(),t=e.map(s=>s.shell_pid).sort((s,n)=>s-n).join(",");return`${e.length}:${t}`}function Tb(e){let t=os.get(e);return t?Date.now()-t.lastAt>Sb?(os.delete(e),!1):t.refusals<bb?!1:t.fingerprint===eu():!1}function Zl(e){let t=eu(),s=os.get(e);s&&s.fingerprint===t?(s.refusals+=1,s.lastAt=Date.now()):os.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function yb(e){os.delete(e)}async function wb(e){let t=Eb();if(!t)return null;try{let{stdout:s}=await En(t,["-Fpc",e],{timeout:2e3}),n=s.split(`
1339
+ `),r=null,o=null,a=null;for(let c of n)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 Ql(e){try{let{stdout:t}=await En("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),s=Number(t.trim());return Number.isFinite(s)&&s>0?s:null}catch{return null}}async function Rb(e){try{let{stdout:t}=await En("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),s=Date.parse(t.trim());return Number.isFinite(s)?s:null}catch{return null}}var kb=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 de(e){let t=e.trim().toLowerCase();if(!t)return!0;let s=t.replace(/^[-/]+/,"").replace(/^.*\//,"").replace(/\s*\(\d+\)\s*$/,"").trim();return kb.has(s)}function wt(e){let t=e.tabName?.trim();return t&&!de(t)&&!ce(t)?t:null}function Ab(e){try{return f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function bn(e){let t=hb(e,".jsonl");if(Te(t)||Tb(t))return;let s=Ab(t),n=await wb(e),r=n?await Ql(n):null,o=n?await Vl(n):null;o&&v.setOrigin(t,o);let a=null,c=null,u=null,d=v.takePendingMatched({shellPid:r,cwd:s?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${s?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),Zl(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=v.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&&n){let S=n;for(let w=0;w<4&&S!=null;w++){let R=await Ql(S);if(!R)break;let j=v.get(R);if(j){a=j,c=R,u="ppid";break}m==null&&(m=R),S=R}}if(!a&&s?.cwd){let S=s.cwd.replace(/\/+$/,""),w=v.all().filter(R=>R.cwd&&R.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`),Zl(t))}let h=null;if(a?.tab_name&&!de(a.tab_name)&&!ce(a.tab_name))h=a.tab_name;else if(a?.tab_name&&ce(a.tab_name)){let S=yt(a.tab_name);S&&!de(S)&&(h=S)}if(!h&&!(u==="pending-pid"||u==="pending-cwd")&&a&&s?.cwd){let S=s.cwd.replace(/\/+$/,""),w=v.all().filter(R=>R.shell_pid!==c&&R.cwd&&R.cwd.replace(/\/+$/,"")===S&&!de(R.tab_name)&&!ce(R.tab_name)).sort((R,j)=>Date.parse(j.last_seen_at)-Date.parse(R.last_seen_at))[0];w&&(h=w.tab_name)}let T=wt({tabName:h,origin:o,cwd:s?.cwd??null,gitBranch:s?.git_branch??null});if(m!=null&&!c&&v.deferSessionLink(t,m,s?.cwd??null,s?.git_branch??null),!!T)try{he(t,T),yb(t);let S=!!h&&!de(h)&&!ce(h)&&T===h.trim();c!=null&&v.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 xb(){try{let{stdout:e}=await En("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let s of e.split(`
1340
+ `)){let n=s.trim();if(!n)continue;let r=n.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 Nb=9e4;function Ob(e){let t=(Date.now()-Nb)/1e3,s=e.replace(/\/+$/,"");return f().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1341
1341
  FROM sessions s
1342
1342
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1343
- WHERE s.cwd = ? AND s.file_mtime > ?`).all(s,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function GE(e){let t=await import("node:fs/promises"),s="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);s=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let n=[];for(let r of s.split(`
1343
+ WHERE s.cwd = ? AND s.file_mtime > ?`).all(s,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function Lb(e){let t=await import("node:fs/promises"),s="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);s=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let n=[];for(let r of s.split(`
1344
1344
  `)){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+`
1345
- `);if(u){for(let d of u.split(/\r?\n/)){let m=d.trim();m.length>=60&&m.length<=400&&n.push(m)}if(n.length>=8)break}}return n}async function jl(e){if(Ae(e))return null;let t=f().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let s=await GE(t.file_path);if(s.length===0)return null;let n=t.cwd.replace(/\/+$/,""),r=v.allOutputTails(),o=[];for(let[a,c]of r){let u=v.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==n||ce(u.tab_name)||ie(u.tab_name))continue;let d=0;for(let m of s)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 zE=3e4;function Ml(){let e=Date.now(),t=v.all(),s=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>zE},n=new Map;for(let o of t){if(s(o)||!o.cwd||ce(o.tab_name)||ie(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=n.get(a);c||(c=[],n.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(!s(o))continue;let a=v.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=Ae(c);if(!u)continue;let d=f().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let m=`${d.cwd.replace(/\/+$/,"")}::${u}`,h=n.get(m)??[];if(h.length===0)continue;if(h.length>1){r.ambiguous++;continue}let b=h[0];b.shell_pid!==o.shell_pid&&(v.unlinkSession(c),v.linkSession(c,b.shell_pid),r.rebound++)}}}return r}function Dl(){let e={resolved:0,expired:0},t=v.allDeferredLinks();for(let s of t){let n=v.get(s.parent_shell_pid);if(!n||ce(n.tab_name)||ie(n.tab_name))continue;let r=Ae(s.session_id);if(r&&!v.isSessionAutoLinked(s.session_id)){v.resolveDeferredLink(s.session_id);continue}let o=v.getOrigin(s.session_id),a=ut({tabName:n.tab_name,origin:o??null,cwd:s.cwd,gitBranch:s.git_branch});if(!a){v.resolveDeferredLink(s.session_id);continue}r!==a&&be(s.session_id,a),v.linkSession(s.session_id,s.parent_shell_pid),v.resolveDeferredLink(s.session_id),e.resolved++}return e}var KE=6e4;async function cn(){let e=await XE(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let s=[];for(let[a,c]of e){let u=v.get(c);if(!u||!u.cwd||ce(u.tab_name)||ie(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let m=await HE(a);s.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:m})}let n=new Map,r=a=>{let c=n.get(a);if(c)return c;let u=YE(a);return n.set(a,u),u},o=new Set;for(let a of s){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=KE;for(let m of c){if(m.started_at_ms==null)continue;let h=Math.abs(m.started_at_ms-a.startTimeMs);h<d&&(d=h,u=m)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!v.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){v.linkSession(u.id,a.shellPid);continue}try{be(u.id,a.target),v.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}import{existsSync as Fl,mkdirSync as VE,readFileSync as ZE,writeFileSync as QE,chmodSync as eb}from"node:fs";import{homedir as tb}from"node:os";import{join as Pl}from"node:path";import{z as je}from"zod";function Ul(){return process.env.RECALL_HOME??Pl(tb(),".recall")}function sb(){let e=Ul();Fl(e)||VE(e,{recursive:!0})}function $l(){return Pl(Ul(),"config.json")}var un=je.object({enabled:je.boolean().default(!1),backend:je.enum(["api","mcp"]).default("api"),apiKey:je.string().optional(),model:je.string().default("claude-opus-4-7"),maxTagsPerSession:je.number().int().min(1).max(10).default(4),minTagsPerSession:je.number().int().min(1).max(10).default(2),autopilot:je.boolean().default(!1)}),ln={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function Bl(){let e=$l();if(!Fl(e))return{};try{return JSON.parse(ZE(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Le(){let e=Bl().autoTag;if(!e)return{...ln};let t=un.safeParse({...ln,...e});return t.success?t.data:{...ln}}function Hl(e){sb();let t=Bl(),s=un.parse({...ln,...t.autoTag??{},...e}),n={...t,autoTag:s},r=$l();QE(r,JSON.stringify(n,null,2));try{eb(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return s}function Qr(e){let{apiKey:t,...s}=e;return{...s,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}import{existsSync as Wl,mkdirSync as nb,readFileSync as rb,writeFileSync as ob}from"node:fs";import{homedir as ib}from"node:os";import{join as ql}from"node:path";import{z as eo}from"zod";function Xl(){return process.env.RECALL_HOME??ql(ib(),".recall")}function ab(){let e=Xl();Wl(e)||nb(e,{recursive:!0})}function Jl(){return ql(Xl(),"config.json")}var pn=eo.object({heuristicEnabled:eo.boolean().default(!0),agentEnabled:eo.boolean().default(!1)}),dn={heuristicEnabled:!0,agentEnabled:!1};function Yl(){let e=Jl();if(!Wl(e))return{};try{return JSON.parse(rb(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function Ge(){let e=Yl().autoTitle;if(!e)return{...dn};let t=pn.safeParse({...dn,...e});return t.success?t.data:{...dn}}function Gl(e){ab();let t=Yl(),s=pn.parse({...dn,...t.autoTitle??{},...e}),n={...t,autoTitle:s};return ob(Jl(),JSON.stringify(n,null,2)),s}H();var mn="claude-haiku-4-5-20251001",zl=80,cb=2e3,dt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},Kl=null;async function lb(e,t){if(Kl)return Kl(e,t);let{spawnClaudePrompt:s,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(he(),We));return n()?s(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function ub(e){let s=f().prepare(`SELECT s.id,
1345
+ `);if(u){for(let d of u.split(/\r?\n/)){let m=d.trim();m.length>=60&&m.length<=400&&n.push(m)}if(n.length>=8)break}}return n}async function tu(e){if(Te(e))return null;let t=f().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let s=await Lb(t.file_path);if(s.length===0)return null;let n=t.cwd.replace(/\/+$/,""),r=v.allOutputTails(),o=[];for(let[a,c]of r){let u=v.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==n||de(u.tab_name)||ce(u.tab_name))continue;let d=0;for(let m of s)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 Cb=3e4;function su(){let e=Date.now(),t=v.all(),s=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>Cb},n=new Map;for(let o of t){if(s(o)||!o.cwd||de(o.tab_name)||ce(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=n.get(a);c||(c=[],n.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(!s(o))continue;let a=v.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=Te(c);if(!u)continue;let d=f().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let m=`${d.cwd.replace(/\/+$/,"")}::${u}`,h=n.get(m)??[];if(h.length===0)continue;if(h.length>1){r.ambiguous++;continue}let b=h[0];b.shell_pid!==o.shell_pid&&(v.unlinkSession(c),v.linkSession(c,b.shell_pid),r.rebound++)}}}return r}function nu(){let e={resolved:0,expired:0},t=v.allDeferredLinks();for(let s of t){let n=v.get(s.parent_shell_pid);if(!n||de(n.tab_name)||ce(n.tab_name))continue;let r=Te(s.session_id);if(r&&!v.isSessionAutoLinked(s.session_id)){v.resolveDeferredLink(s.session_id);continue}let o=v.getOrigin(s.session_id),a=wt({tabName:n.tab_name,origin:o??null,cwd:s.cwd,gitBranch:s.git_branch});if(!a){v.resolveDeferredLink(s.session_id);continue}r!==a&&he(s.session_id,a),v.linkSession(s.session_id,s.parent_shell_pid),v.resolveDeferredLink(s.session_id),e.resolved++}return e}var Ib=6e4;async function Sn(){let e=await xb(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let s=[];for(let[a,c]of e){let u=v.get(c);if(!u||!u.cwd||de(u.tab_name)||ce(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let m=await Rb(a);s.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:m})}let n=new Map,r=a=>{let c=n.get(a);if(c)return c;let u=Ob(a);return n.set(a,u),u},o=new Set;for(let a of s){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=Ib;for(let m of c){if(m.started_at_ms==null)continue;let h=Math.abs(m.started_at_ms-a.startTimeMs);h<d&&(d=h,u=m)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!v.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){v.linkSession(u.id,a.shellPid);continue}try{he(u.id,a.target),v.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}import{existsSync as ru,mkdirSync as vb,readFileSync as jb,writeFileSync as Mb,chmodSync as Db}from"node:fs";import{homedir as Fb}from"node:os";import{join as ou}from"node:path";import{z as Be}from"zod";function iu(){return process.env.RECALL_HOME??ou(Fb(),".recall")}function Pb(){let e=iu();ru(e)||vb(e,{recursive:!0})}function au(){return ou(iu(),"config.json")}var yn=Be.object({enabled:Be.boolean().default(!1),backend:Be.enum(["api","mcp"]).default("api"),apiKey:Be.string().optional(),model:Be.string().default("claude-opus-4-7"),maxTagsPerSession:Be.number().int().min(1).max(10).default(4),minTagsPerSession:Be.number().int().min(1).max(10).default(2),autopilot:Be.boolean().default(!1)}),Tn={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function cu(){let e=au();if(!ru(e))return{};try{return JSON.parse(jb(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Fe(){let e=cu().autoTag;if(!e)return{...Tn};let t=yn.safeParse({...Tn,...e});return t.success?t.data:{...Tn}}function lu(e){Pb();let t=cu(),s=yn.parse({...Tn,...t.autoTag??{},...e}),n={...t,autoTag:s},r=au();Mb(r,JSON.stringify(n,null,2));try{Db(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return s}function _o(e){let{apiKey:t,...s}=e;return{...s,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}import{existsSync as uu,mkdirSync as Ub,readFileSync as $b,writeFileSync as Hb}from"node:fs";import{homedir as Bb}from"node:os";import{join as du}from"node:path";import{z as fo}from"zod";function pu(){return process.env.RECALL_HOME??du(Bb(),".recall")}function Wb(){let e=pu();uu(e)||Ub(e,{recursive:!0})}function mu(){return du(pu(),"config.json")}var Rn=fo.object({heuristicEnabled:fo.boolean().default(!0),agentEnabled:fo.boolean().default(!1)}),wn={heuristicEnabled:!0,agentEnabled:!1};function gu(){let e=mu();if(!uu(e))return{};try{return JSON.parse($b(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function it(){let e=gu().autoTitle;if(!e)return{...wn};let t=Rn.safeParse({...wn,...e});return t.success?t.data:{...wn}}function _u(e){Wb();let t=gu(),s=Rn.parse({...wn,...t.autoTitle??{},...e}),n={...t,autoTitle:s};return Hb(mu(),JSON.stringify(n,null,2)),s}U();var kn="claude-haiku-4-5-20251001",fu=80,qb=2e3,Rt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},hu=null;async function Xb(e,t){if(hu)return hu(e,t);let{spawnClaudePrompt:s,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(ye(),tt));return n()?s(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function Jb(e){let s=f().prepare(`SELECT s.id,
1346
1346
  s.auto_title,
1347
1347
  s.auto_title_source,
1348
1348
  CASE WHEN sa.alias IS NOT NULL AND sa.alias != ''
1349
1349
  THEN 1 ELSE 0 END AS has_alias_int
1350
1350
  FROM sessions s
1351
1351
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1352
- WHERE s.id = ?`).get(e);return s?{id:s.id,auto_title:s.auto_title,auto_title_source:s.auto_title_source??null,has_alias:s.has_alias_int===1}:null}function db(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 pb(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let s=Xt({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(s){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 mb(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(`
1353
- `)}function gb(e){let t=e.trim();if(!t)return null;let s=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")s=a.result.trim();else if(typeof a.title=="string")return Vl(a)}}catch{}let n=s.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);n&&(s=n[1].trim());let r=null;try{r=JSON.parse(s)}catch{let o=s.indexOf("{"),a=s.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(s.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:Vl(r)}function Vl(e){let t=e.title;if(typeof t!="string")return null;let s=_b(t).trim();if(!s)return null;let n=e.evidence,r=typeof n=="string"?n.trim():"";return{title:s,evidence:r}}function _b(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function fb(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=zl?t:t.slice(0,zl)}function hb(e,t){let s=e.auto_title??"";return!s&&e.has_alias&&(s=f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:s,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function to(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let s=ub(e);if(!s)throw new Error(`session not found: ${e}`);let n=pb(s,t.force===!0);if(!n.eligible)return hb(s,n.reason);let r=t.budget??cb,o=en(e,{budget:r});if(db(o))throw new dt(e);if(t.signal?.aborted)return{session_id:e,title:s.auto_title??"",source:s.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=mb(o.bundle),c=t.model??mn,u=await lb(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=gb(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=fb(d.title);if(!m)throw new Error("regeneration produced empty title after clamp");me(e,m,"agent");let b=ke(e)?.auto_title??m;return{session_id:e,title:b,source:"agent",confidence:bb(o),evidence:d.evidence||`regenerated from neighborhood (${Eb(o)})`,written:!0}}function Eb(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 bb(e){let t=e.parents.length,s=e.children.length,n=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,s)+.2*Math.min(1,n/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 Ue}from"hono/streaming";import{bodyLimit as sy}from"hono/body-limit";import{z as j}from"zod";H();H();ze();function Kt(e){return f().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}H();function Zl(e){let t=f(),s=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1352
+ WHERE s.id = ?`).get(e);return s?{id:s.id,auto_title:s.auto_title,auto_title_source:s.auto_title_source??null,has_alias:s.has_alias_int===1}:null}function Gb(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 Yb(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let s=ts({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(s){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 zb(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(`
1353
+ `)}function Kb(e){let t=e.trim();if(!t)return null;let s=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")s=a.result.trim();else if(typeof a.title=="string")return Eu(a)}}catch{}let n=s.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);n&&(s=n[1].trim());let r=null;try{r=JSON.parse(s)}catch{let o=s.indexOf("{"),a=s.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(s.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:Eu(r)}function Eu(e){let t=e.title;if(typeof t!="string")return null;let s=Vb(t).trim();if(!s)return null;let n=e.evidence,r=typeof n=="string"?n.trim():"";return{title:s,evidence:r}}function Vb(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function Zb(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=fu?t:t.slice(0,fu)}function Qb(e,t){let s=e.auto_title??"";return!s&&e.has_alias&&(s=f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:s,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function ho(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let s=Jb(e);if(!s)throw new Error(`session not found: ${e}`);let n=Yb(s,t.force===!0);if(!n.eligible)return Qb(s,n.reason);let r=t.budget??qb,o=mn(e,{budget:r});if(Gb(o))throw new Rt(e);if(t.signal?.aborted)return{session_id:e,title:s.auto_title??"",source:s.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=zb(o.bundle),c=t.model??kn,u=await Xb(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=Kb(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=Zb(d.title);if(!m)throw new Error("regeneration produced empty title after clamp");Ee(e,m,"agent");let b=Le(e)?.auto_title??m;return{session_id:e,title:b,source:"agent",confidence:tS(o),evidence:d.evidence||`regenerated from neighborhood (${eS(o)})`,written:!0}}function eS(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 tS(e){let t=e.parents.length,s=e.children.length,n=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,s)+.2*Math.min(1,n/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 Ye}from"hono/streaming";import{bodyLimit as Wy}from"hono/body-limit";import{z as M}from"zod";U();U();Ve();function is(e){return f().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}U();function bu(e){let t=f(),s=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1354
1354
  (message_hash, resolved_in_session_id, fix_summary, resolved_at, unresolved_at)
1355
1355
  VALUES (?, ?, ?, ?, NULL)
1356
1356
  ON CONFLICT(message_hash) DO UPDATE SET
1357
1357
  resolved_in_session_id = excluded.resolved_in_session_id,
1358
1358
  fix_summary = excluded.fix_summary,
1359
1359
  resolved_at = excluded.resolved_at,
1360
- unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,s),Sb(e.messageHash)}function Ql(e){f().prepare(`UPDATE bug_signature_resolutions
1360
+ unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,s),sS(e.messageHash)}function Su(e){f().prepare(`UPDATE bug_signature_resolutions
1361
1361
  SET unresolved_at = ?
1362
- WHERE message_hash = ?`).run(new Date().toISOString(),e)}function Sb(e){return f().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function so(e){if(e.length===0)return new Map;let t=f(),s=e.map(()=>"?").join(","),n=t.prepare(`SELECT * FROM bug_signature_resolutions
1363
- WHERE message_hash IN (${s})`).all(...e),r=new Map;for(let o of n)r.set(o.message_hash,o);return r}function no(e){return!!e&&e.unresolved_at===null}H();function Vt(){return new Date().toISOString()}function ro(){let e=f(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1362
+ WHERE message_hash = ?`).run(new Date().toISOString(),e)}function sS(e){return f().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function Eo(e){if(e.length===0)return new Map;let t=f(),s=e.map(()=>"?").join(","),n=t.prepare(`SELECT * FROM bug_signature_resolutions
1363
+ WHERE message_hash IN (${s})`).all(...e),r=new Map;for(let o of n)r.set(o.message_hash,o);return r}function bo(e){return!!e&&e.unresolved_at===null}U();function as(){return new Date().toISOString()}function So(){let e=f(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1364
1364
  FROM macro_repos
1365
1365
  ORDER BY name COLLATE NOCASE`).all();if(t.length===0)return[];let s=t.map(a=>a.id),n=s.map(()=>"?").join(","),r=e.prepare(`SELECT m.macro_repo_id, m.project_id, p.name AS project_name
1366
1366
  FROM macro_repo_members m
1367
1367
  JOIN projects p ON p.id = m.project_id
1368
1368
  WHERE m.macro_repo_id IN (${n})
1369
- ORDER BY p.name COLLATE NOCASE`).all(...s),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 pt(e){return ro().find(s=>s.id===e)??null}function eu(){return f().prepare(`SELECT p.id, p.name
1369
+ ORDER BY p.name COLLATE NOCASE`).all(...s),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 kt(e){return So().find(s=>s.id===e)??null}function Tu(){return f().prepare(`SELECT p.id, p.name
1370
1370
  FROM projects p
1371
1371
  LEFT JOIN macro_repo_members m ON m.project_id = p.id
1372
1372
  WHERE m.project_id IS NULL
1373
- ORDER BY p.name COLLATE NOCASE`).all()}function tu(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let s=f(),n=Vt(),r=s.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1374
- VALUES (?, ?, ?, ?)`).run(t,e.description??null,n,n),o=Number(r.lastInsertRowid);return pt(o)}function su(e,t){let s=pt(e);if(!s)throw new Error(`macro repo ${e} not found`);let n=t.name!==void 0?t.name.trim():s.name;if(!n)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:s.description;return f().prepare(`UPDATE macro_repos
1373
+ ORDER BY p.name COLLATE NOCASE`).all()}function yu(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let s=f(),n=as(),r=s.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1374
+ VALUES (?, ?, ?, ?)`).run(t,e.description??null,n,n),o=Number(r.lastInsertRowid);return kt(o)}function wu(e,t){let s=kt(e);if(!s)throw new Error(`macro repo ${e} not found`);let n=t.name!==void 0?t.name.trim():s.name;if(!n)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:s.description;return f().prepare(`UPDATE macro_repos
1375
1375
  SET name = ?, description = ?, updated_at = ?
1376
- WHERE id = ?`).run(n,r,Vt(),e),pt(e)}function nu(e){f().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function ru(e,t){let s=f();if(!s.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!s.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);s.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1377
- VALUES (?, ?, ?)`).run(e,t,Vt()),s.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Vt(),e)}function ou(e,t){let s=f();s.prepare(`DELETE FROM macro_repo_members
1378
- WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),s.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Vt(),e)}H();function iu(e){let t=f(),s=new Date().toISOString(),n=t.prepare(`INSERT INTO bug_synthesis_results
1376
+ WHERE id = ?`).run(n,r,as(),e),kt(e)}function Ru(e){f().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function ku(e,t){let s=f();if(!s.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!s.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);s.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1377
+ VALUES (?, ?, ?)`).run(e,t,as()),s.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(as(),e)}function Au(e,t){let s=f();s.prepare(`DELETE FROM macro_repo_members
1378
+ WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),s.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(as(),e)}U();function xu(e){let t=f(),s=new Date().toISOString(),n=t.prepare(`INSERT INTO bug_synthesis_results
1379
1379
  (scope, target_id, mode, model, output_markdown, input_tokens, output_tokens, context_summary, created_at, job_id)
1380
- 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??{}),s,e.job_id??null),r=Number(n.lastInsertRowid);return oo(r)}function au(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 oo(e){let s=f().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return s?au(s):null}function cu(e={}){let t=f(),s=[],n=[];e.scope&&(s.push("scope = ?"),n.push(e.scope)),e.target_id&&(s.push("target_id = ?"),n.push(e.target_id));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1380
+ 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??{}),s,e.job_id??null),r=Number(n.lastInsertRowid);return To(r)}function Nu(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 To(e){let s=f().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return s?Nu(s):null}function Ou(e={}){let t=f(),s=[],n=[];e.scope&&(s.push("scope = ?"),n.push(e.scope)),e.target_id&&(s.push("target_id = ?"),n.push(e.target_id));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1381
1381
  ${r}
1382
1382
  ORDER BY created_at DESC
1383
- LIMIT ?`).all(...n,o).map(au)}function lu(e){let s=f().prepare(`SELECT target_id, COUNT(*) AS n
1383
+ LIMIT ?`).all(...n,o).map(Nu)}function Lu(e){let s=f().prepare(`SELECT target_id, COUNT(*) AS n
1384
1384
  FROM bug_synthesis_results
1385
1385
  WHERE scope = ?
1386
- GROUP BY target_id`).all(e),n=new Map;for(let r of s)n.set(r.target_id,r.n);return n}function uu(e){f().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as Tb,timingSafeEqual as yb}from"node:crypto";var wb=6e4,Rb=new Set(["127.0.0.1","localhost"]),Zt=new Map;function io(){return Date.now()}function du(){let e=io();for(let[t,s]of Zt)(s.expiresAt<=e||s.used)&&Zt.delete(t)}function xe(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!Rb.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 s=e.req.header("sec-fetch-site");return s&&s!=="same-origin"&&s!=="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 ao(e){du();let t=Tb(32).toString("hex"),s=io()+wb;return Zt.set(t,{token:t,intent:e,expiresAt:s,used:!1}),{token:t,expiresAt:s}}function co(e){if(du(),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 s of Zt.values()){if(s.used||s.expiresAt<=io())continue;let n;try{n=Buffer.from(s.token,"hex")}catch{continue}if(n.length===t.length&&yb(n,t))return s.used=!0,Zt.delete(s.token),s.intent}return null}import{existsSync as Lb,mkdirSync as Zk,readFileSync as Cb,writeFileSync as Qk}from"node:fs";import{homedir as vb}from"node:os";import{join as Eu}from"node:path";import{z as lo}from"zod";import{appendFileSync as Ab,existsSync as pu,mkdirSync as kb,readFileSync as xb}from"node:fs";import{homedir as Nb}from"node:os";import{join as mu}from"node:path";function gu(){return process.env.RECALL_HOME??mu(Nb(),".recall")}function Ob(){let e=gu();pu(e)||kb(e,{recursive:!0})}function _u(){return mu(gu(),"launcher-audit.log")}function ne(e){Ob();let t={ts:new Date().toISOString(),...e};try{Ab(_u(),JSON.stringify(t)+`
1387
- `,"utf8")}catch(s){console.error("[launcher-audit] failed to append:",s)}}function fu(e){let t=_u();if(!pu(t))return{input_tokens:0,output_tokens:0,records_counted:0};let s=Date.now()-e,n=0,r=0,o=0,a;try{a=xb(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1388
- `)){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<s||(n+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:n,output_tokens:r,records_counted:o}}var Ib=1440*60*1e3,jb=lo.object({dailyTokenBudget:lo.number().int().nonnegative().default(1e6),sessionCeiling:lo.number().int().positive().max(1e4).default(500)}),uo={dailyTokenBudget:1e6,sessionCeiling:500};function Mb(){return process.env.RECALL_HOME??Eu(vb(),".recall")}function Db(){return Eu(Mb(),"config.json")}function Fb(){let e=Db();if(!Lb(e))return{};try{return JSON.parse(Cb(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function po(){let e=Fb().launcher;if(!e)return{...uo};let t=jb.safeParse({...uo,...e});return t.success?t.data:{...uo}}function mt(){let e=po(),t=fu(Ib),s=t.input_tokens+t.output_tokens,n=Math.max(0,e.dailyTokenBudget-s);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:s,remaining_tokens_24h:n}}function mo(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function go(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 s=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*s),estimated_output_tokens_max:t}}var hu={pro:45,"max-5x":225,"max-20x":900};function Pb(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var Ub=4e3;function bu(e){return Math.max(1,Math.ceil(e/Ub))}function Me(e,t){let s=Pb(t),n=e*s,r=Object.keys(hu).map(o=>{let a=n/hu[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:s,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 $b}from"node:crypto";var gt=new Map,Qt=new Map,Bb=300*1e3;function gn(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function Hb(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{gt.delete(e.jobId),Qt.get(e.project)===e.jobId&&Qt.delete(e.project)},Bb),e.cleanupTimer.unref?.())}function _o(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 Su(e){let t=Kt(e.project);if(!t)return{error:`project "${e.project}" not found`};let s=Qt.get(t.name);if(s){let m=gt.get(s);if(m&&m.status==="running")return{jobId:s,reused:!0};Qt.delete(t.name)}let n=$b(),r=e.model??qe,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:n,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 gt.set(n,d),Qt.set(t.name,n),ne({kind:"run-launched",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await Ta({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:m=>{d.progress=m,gn(d,"progress",m)},onResult:m=>{!m.ok&&!m.skipped&&gn(d,"error",{session_id:m.session_id,reason:m.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),gn(d,"done",_o(d)),ne({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:n,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 h=m instanceof Error?m.message:String(m??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=h,gn(d,"done",_o(d)),ne({kind:"run-failed",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null,reason:h,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{Hb(d)}})(),{jobId:n,reused:!1}}async function*Tu(e,t=0){let s=gt.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function yu(e){let t=gt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function fo(e){let t=gt.get(e);return t?_o(t):null}H();import{randomUUID as Wb}from"node:crypto";import{spawn as qb}from"node:child_process";import{execSync as Xb}from"node:child_process";var wu="claude-haiku-4-5-20251001",ft=new Map,ss=new Map,Jb=300*1e3;function Ru(e){return`${e.scope}:${e.target_id}:${e.mode}`}function es(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function Yb(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{ft.delete(e.jobId);let t=Ru(e.intent);ss.get(t)===e.jobId&&ss.delete(t)},Jb),e.cleanupTimer.unref?.())}function _t(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 Au(){return f().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1386
+ GROUP BY target_id`).all(e),n=new Map;for(let r of s)n.set(r.target_id,r.n);return n}function Cu(e){f().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as nS,timingSafeEqual as rS}from"node:crypto";var oS=6e4,iS=new Set(["127.0.0.1","localhost"]),cs=new Map;function yo(){return Date.now()}function Iu(){let e=yo();for(let[t,s]of cs)(s.expiresAt<=e||s.used)&&cs.delete(t)}function Ce(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!iS.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 s=e.req.header("sec-fetch-site");return s&&s!=="same-origin"&&s!=="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 wo(e){Iu();let t=nS(32).toString("hex"),s=yo()+oS;return cs.set(t,{token:t,intent:e,expiresAt:s,used:!1}),{token:t,expiresAt:s}}function Ro(e){if(Iu(),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 s of cs.values()){if(s.used||s.expiresAt<=yo())continue;let n;try{n=Buffer.from(s.token,"hex")}catch{continue}if(n.length===t.length&&rS(n,t))return s.used=!0,cs.delete(s.token),s.intent}return null}import{existsSync as pS,mkdirSync as Kx,readFileSync as mS,writeFileSync as Vx}from"node:fs";import{homedir as gS}from"node:os";import{join as Uu}from"node:path";import{z as ko}from"zod";import{appendFileSync as aS,existsSync as vu,mkdirSync as cS,readFileSync as lS}from"node:fs";import{homedir as uS}from"node:os";import{join as ju}from"node:path";function Mu(){return process.env.RECALL_HOME??ju(uS(),".recall")}function dS(){let e=Mu();vu(e)||cS(e,{recursive:!0})}function Du(){return ju(Mu(),"launcher-audit.log")}function ne(e){dS();let t={ts:new Date().toISOString(),...e};try{aS(Du(),JSON.stringify(t)+`
1387
+ `,"utf8")}catch(s){console.error("[launcher-audit] failed to append:",s)}}function Fu(e){let t=Du();if(!vu(t))return{input_tokens:0,output_tokens:0,records_counted:0};let s=Date.now()-e,n=0,r=0,o=0,a;try{a=lS(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1388
+ `)){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<s||(n+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:n,output_tokens:r,records_counted:o}}var _S=1440*60*1e3,fS=ko.object({dailyTokenBudget:ko.number().int().nonnegative().default(1e6),sessionCeiling:ko.number().int().positive().max(1e4).default(500)}),Ao={dailyTokenBudget:1e6,sessionCeiling:500};function hS(){return process.env.RECALL_HOME??Uu(gS(),".recall")}function ES(){return Uu(hS(),"config.json")}function bS(){let e=ES();if(!pS(e))return{};try{return JSON.parse(mS(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function xo(){let e=bS().launcher;if(!e)return{...Ao};let t=fS.safeParse({...Ao,...e});return t.success?t.data:{...Ao}}function At(){let e=xo(),t=Fu(_S),s=t.input_tokens+t.output_tokens,n=Math.max(0,e.dailyTokenBudget-s);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:s,remaining_tokens_24h:n}}function No(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function Oo(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 s=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*s),estimated_output_tokens_max:t}}var Pu={pro:45,"max-5x":225,"max-20x":900};function SS(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var TS=4e3;function $u(e){return Math.max(1,Math.ceil(e/TS))}function We(e,t){let s=SS(t),n=e*s,r=Object.keys(Pu).map(o=>{let a=n/Pu[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:s,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 yS}from"node:crypto";var xt=new Map,ls=new Map,wS=300*1e3;function An(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function RS(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{xt.delete(e.jobId),ls.get(e.project)===e.jobId&&ls.delete(e.project)},wS),e.cleanupTimer.unref?.())}function Lo(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 Hu(e){let t=is(e.project);if(!t)return{error:`project "${e.project}" not found`};let s=ls.get(t.name);if(s){let m=xt.get(s);if(m&&m.status==="running")return{jobId:s,reused:!0};ls.delete(t.name)}let n=yS(),r=e.model??st,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:n,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 xt.set(n,d),ls.set(t.name,n),ne({kind:"run-launched",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await Ha({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:m=>{d.progress=m,An(d,"progress",m)},onResult:m=>{!m.ok&&!m.skipped&&An(d,"error",{session_id:m.session_id,reason:m.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),An(d,"done",Lo(d)),ne({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:n,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 h=m instanceof Error?m.message:String(m??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=h,An(d,"done",Lo(d)),ne({kind:"run-failed",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null,reason:h,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{RS(d)}})(),{jobId:n,reused:!1}}async function*Bu(e,t=0){let s=xt.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function Wu(e){let t=xt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Co(e){let t=xt.get(e);return t?Lo(t):null}U();import{randomUUID as kS}from"node:crypto";import{spawn as AS}from"node:child_process";import{execSync as xS}from"node:child_process";var qu="claude-haiku-4-5-20251001",Ot=new Map,ps=new Map,NS=300*1e3;function Xu(e){return`${e.scope}:${e.target_id}:${e.mode}`}function us(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function OS(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Ot.delete(e.jobId);let t=Xu(e.intent);ps.get(t)===e.jobId&&ps.delete(t)},NS),e.cleanupTimer.unref?.())}function Nt(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 Ju(){return f().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1389
1389
  oi.bug_signatures
1390
1390
  FROM session_output_index oi
1391
1391
  JOIN sessions s ON s.id = oi.session_id
1392
1392
  JOIN projects p ON p.id = s.project_id
1393
1393
  WHERE oi.bug_signatures IS NOT NULL
1394
- AND oi.bug_signatures != '[]'`).all()}function ku(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Gb(e){let t=Au(),s=[];for(let a of t)for(let c of ku(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&s.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(s.length===0)return null;let n=s[0],r=Array.from(new Set(s.map(a=>a.session_id))),o=Array.from(new Set(s.map(a=>a.project))).sort();return{message_hash:n.sig.message_hash??null,error_type:n.sig.error_type??null,snippet:(n.sig.snippet??"").slice(0,400),file:n.sig.file??null,occurrence_count:s.length,projects:o,member_session_ids:r}}function zb(e){let t=Au().filter(o=>o.project===e),s=new Map;for(let o of t)for(let a of ku(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=s.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):s.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let n=new Map;try{let o=Array.from(s.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=f(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1394
+ AND oi.bug_signatures != '[]'`).all()}function Gu(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function LS(e){let t=Ju(),s=[];for(let a of t)for(let c of Gu(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&s.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(s.length===0)return null;let n=s[0],r=Array.from(new Set(s.map(a=>a.session_id))),o=Array.from(new Set(s.map(a=>a.project))).sort();return{message_hash:n.sig.message_hash??null,error_type:n.sig.error_type??null,snippet:(n.sig.snippet??"").slice(0,400),file:n.sig.file??null,occurrence_count:s.length,projects:o,member_session_ids:r}}function CS(e){let t=Ju().filter(o=>o.project===e),s=new Map;for(let o of t)for(let a of Gu(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=s.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):s.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let n=new Map;try{let o=Array.from(s.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=f(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1395
1395
  FROM bug_signature_resolutions
1396
1396
  WHERE message_hash IN (${c})
1397
- AND unresolved_at IS NULL`).all(...o);for(let d of u)n.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of s){let c=a.first.message_hash??null,u=c?n.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 Kb(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function Vb(e,t){let s=f(),n=Kb(t),r=[];for(let o of e.slice(0,8)){let a=s.prepare(`SELECT s.id, p.name AS project, s.auto_title
1397
+ AND unresolved_at IS NULL`).all(...o);for(let d of u)n.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of s){let c=a.first.message_hash??null,u=c?n.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 IS(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function vS(e,t){let s=f(),n=IS(t),r=[];for(let o of e.slice(0,8)){let a=s.prepare(`SELECT s.id, p.name AS project, s.auto_title
1398
1398
  FROM sessions s
1399
1399
  JOIN projects p ON p.id = s.project_id
1400
1400
  WHERE s.id = ?`).get(o);if(!a)continue;let c=[];if(n.length>0){let u=s.prepare(`SELECT rowid FROM messages
@@ -1406,9 +1406,9 @@ ${s}`}function aE(e){return ke(e)?.auto_title_source??null}async function cE(e){
1406
1406
  WHERE session_id = ?
1407
1407
  AND is_sidechain = 0
1408
1408
  AND rowid BETWEEN ? AND ?
1409
- ORDER BY rowid`).all(o,h.rowid-2,h.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 Zb="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 Qb(e,t){let s=[];s.push("[CONTEXT]"),s.push("Bug fingerprint:"),s.push(`- error_type: ${e.error_type??"unknown"}`),s.push(`- description: ${e.snippet||"(no snippet)"}`),s.push(`- file: ${e.file??"(no file recorded)"}`),s.push(`- occurrences: ${e.occurrence_count} sessions`),s.push(`- projects: ${e.projects.join(", ")}`),s.push(`- finding_id: ${e.message_hash??"(no hash)"}`),s.push(""),s.push("Member session excerpts:");for(let n of t){let r=n.auto_title??"(untitled)";if(s.push(`=== Session ${n.short_id} | ${n.project} | "${r}" ===`),n.excerpts.length===0)s.push("(no surrounding messages found for this snippet)");else for(let o of n.excerpts)s.push(`${o.role}: ${o.content}`);s.push("")}return s.join(`
1410
- `)}function eS(e,t,s){let n=[];n.push("[CONTEXT]"),n.push(`Project: ${e}`),n.push(`Total clusters: ${s}`),n.push(""),n.push("Clusters (sorted by occurrence_count desc):");for(let r of t)n.push(`- cluster_id: ${r.cluster_id}`),n.push(` error_type: ${r.error_type??"unknown"}`),n.push(` snippet: ${r.snippet||"(none)"}`),r.file&&n.push(` file: ${r.file}`),n.push(` occurrence_count: ${r.occurrence_count}`),n.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&n.push(` fix_summary: ${r.fix_summary}`),n.push("");return t.length<s&&n.push(`(Showing top ${t.length} of ${s} clusters by occurrence.)`),n.join(`
1411
- `)}var tS=`[TASK]
1409
+ ORDER BY rowid`).all(o,h.rowid-2,h.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 jS="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 MS(e,t){let s=[];s.push("[CONTEXT]"),s.push("Bug fingerprint:"),s.push(`- error_type: ${e.error_type??"unknown"}`),s.push(`- description: ${e.snippet||"(no snippet)"}`),s.push(`- file: ${e.file??"(no file recorded)"}`),s.push(`- occurrences: ${e.occurrence_count} sessions`),s.push(`- projects: ${e.projects.join(", ")}`),s.push(`- finding_id: ${e.message_hash??"(no hash)"}`),s.push(""),s.push("Member session excerpts:");for(let n of t){let r=n.auto_title??"(untitled)";if(s.push(`=== Session ${n.short_id} | ${n.project} | "${r}" ===`),n.excerpts.length===0)s.push("(no surrounding messages found for this snippet)");else for(let o of n.excerpts)s.push(`${o.role}: ${o.content}`);s.push("")}return s.join(`
1410
+ `)}function DS(e,t,s){let n=[];n.push("[CONTEXT]"),n.push(`Project: ${e}`),n.push(`Total clusters: ${s}`),n.push(""),n.push("Clusters (sorted by occurrence_count desc):");for(let r of t)n.push(`- cluster_id: ${r.cluster_id}`),n.push(` error_type: ${r.error_type??"unknown"}`),n.push(` snippet: ${r.snippet||"(none)"}`),r.file&&n.push(` file: ${r.file}`),n.push(` occurrence_count: ${r.occurrence_count}`),n.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&n.push(` fix_summary: ${r.fix_summary}`),n.push("");return t.length<s&&n.push(`(Showing top ${t.length} of ${s} clusters by occurrence.)`),n.join(`
1411
+ `)}var FS=`[TASK]
1412
1412
  Output a Markdown synopsis with these sections:
1413
1413
 
1414
1414
  ## What this bug is
@@ -1430,7 +1430,7 @@ prefer "consider <pattern>" over "do <action>" unless the evidence
1430
1430
  is overwhelming.
1431
1431
 
1432
1432
  ## Confidence
1433
- One of: high / medium / low. With one sentence justifying the level.`,sS=`[TASK]
1433
+ One of: high / medium / low. With one sentence justifying the level.`,PS=`[TASK]
1434
1434
  Output a Markdown response with these sections:
1435
1435
 
1436
1436
  ## Most likely root cause
@@ -1447,7 +1447,7 @@ list is acceptable but explicitly say "(none found)".
1447
1447
  At most 2 alternatives, each with one sentence why it is less likely.
1448
1448
 
1449
1449
  ## Confidence
1450
- high / medium / low + one-sentence justification.`,nS=`[TASK]
1450
+ high / medium / low + one-sentence justification.`,US=`[TASK]
1451
1451
  Output a Markdown response with these sections:
1452
1452
 
1453
1453
  ## Top 5 recurring concerns
@@ -1465,20 +1465,20 @@ Clusters that look small + high-confidence-fix. Empty list is fine.
1465
1465
  Clusters that suggest deeper issues (multiple files, repeated patterns).
1466
1466
 
1467
1467
  ## Confidence
1468
- high / medium / low.`;function rS(e,t){let s;return e.scope==="cluster"?s=e.mode==="root_cause"?sS:tS:s=nS,[Zb,"",t,"",s].join(`
1469
- `)}var oS=null;var ts;function iS(){if(ts)return ts;try{ts=Xb("which claude",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{ts="claude"}return ts}function aS(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
1468
+ high / medium / low.`;function $S(e,t){let s;return e.scope==="cluster"?s=e.mode==="root_cause"?PS:FS:s=US,[jS,"",t,"",s].join(`
1469
+ `)}var HS=null;var ds;function BS(){if(ds)return ds;try{ds=xS("which claude",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{ds="claude"}return ds}function WS(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
1470
1470
  `);for(;n!==-1;){let r=t.slice(0,n);t=t.slice(n+1),r.length>0&&e(r),n=t.indexOf(`
1471
- `)}}}function cS(e){return new Promise(t=>{let s=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],n="",r=0,o=0,a=null,c=qb(iS(),s,{stdio:["ignore","pipe","pipe"]}),u=()=>{try{c.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",u,{once:!0});let m=aS(b=>{let T=b.trim();if(!T.startsWith("{"))return;let S;try{S=JSON.parse(T)}catch{return}if(!S||typeof S!="object")return;let w=S;if(w.type==="assistant"&&w.message?.content){for(let D of w.message.content)D?.type==="text"&&typeof D.text=="string"&&(n+=D.text,e.onPartial(D.text,n));let R=w.message?.usage;R&&(typeof R.input_tokens=="number"&&(r=Math.max(r,R.input_tokens)),typeof R.output_tokens=="number"&&(o=Math.max(o,R.output_tokens)))}});c.stdout.on("data",b=>m(b)),c.stderr.on("data",b=>{let T=b.toString("utf8");T.trim()&&(a=(a??"")+T)});let h=setTimeout(()=>{try{c.kill("SIGKILL")}catch{}},1800*1e3);c.on("close",b=>{clearTimeout(h),e.signal.removeEventListener("abort",u);let T=b===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${b}`);t({output_markdown:n,input_tokens:r,output_tokens:o,error:T})}),c.on("error",b=>{clearTimeout(h),e.signal.removeEventListener("abort",u),t({output_markdown:n,input_tokens:r,output_tokens:o,error:b instanceof Error?b.message:String(b)})})})}function _n(e){if(e.scope==="cluster"){let r=Gb(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=zb(e.target_id);if(t.length===0)return null;let s=t.slice(0,30),n=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:s,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:n}}}function xu(e){let t=_n(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 s=Ru(e.intent),n=ss.get(s);if(n){let u=ft.get(n);if(u&&u.status==="running")return{jobId:n,reused:!0};ss.delete(s)}let r=Wb(),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 ft.set(r,c),ss.set(s,r),ne({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=Vb(t.cluster.member_session_ids,t.cluster.snippet);u=Qb(t.cluster,b)}else if(e.intent.scope==="project"&&t.project_clusters)u=eS(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=rS(e.intent,u),h=await(oS??cS)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(b,T)=>{c.output_markdown=T,es(c,"partial",_t(c))}});if(c.output_markdown=h.output_markdown,c.input_tokens=h.input_tokens,c.output_tokens=h.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),es(c,"done",_t(c)),ne({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(h.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=h.error,es(c,"done",_t(c)),ne({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:h.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),es(c,"done",_t(c)),ne({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{iu({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,es(c,"done",_t(c)),ne({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{Yb(c)}})(),{jobId:r,reused:!1}}async function*Nu(e,t=0){let s=ft.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function Ou(e){let t=ft.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function ho(e){let t=ft.get(e);return t?_t(t):null}import{randomUUID as TS}from"node:crypto";H();Ls();import{randomUUID as hS}from"node:crypto";H();var Lu=10,Cu=20;function lS(e){if(!e)return!1;let t=e.split(`
1472
- `);if(t.length<4)return!1;let s=0,n=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?s++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&n++;return s/t.length>.7||n/t.length>.5}function uS(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let s=new Float32Array(t),n=new Float32Array(e.buffer,e.byteOffset,t);return s.set(n),s}function vu(e){let t=0;for(let n=0;n<e.length;n++)t+=e[n]*e[n];if(t<=0)return!1;let s=1/Math.sqrt(t);for(let n=0;n<e.length;n++)e[n]*=s;return!0}function Eo(e){if(e.length===0)return null;let t=e[0].length,s=new Float32Array(t);for(let n of e)if(n.length===t)for(let r=0;r<t;r++)s[r]+=n[r];for(let n=0;n<t;n++)s[n]/=e.length;return vu(s)?s:null}function Iu(e){let t=new Map;if(e.length===0)return t;let s=f(),n=e.map(()=>"?").join(","),r=[];try{r=s.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1471
+ `)}}}function qS(e){return new Promise(t=>{let s=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],n="",r=0,o=0,a=null,c=AS(BS(),s,{stdio:["ignore","pipe","pipe"]}),u=()=>{try{c.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",u,{once:!0});let m=WS(b=>{let T=b.trim();if(!T.startsWith("{"))return;let S;try{S=JSON.parse(T)}catch{return}if(!S||typeof S!="object")return;let w=S;if(w.type==="assistant"&&w.message?.content){for(let j of w.message.content)j?.type==="text"&&typeof j.text=="string"&&(n+=j.text,e.onPartial(j.text,n));let R=w.message?.usage;R&&(typeof R.input_tokens=="number"&&(r=Math.max(r,R.input_tokens)),typeof R.output_tokens=="number"&&(o=Math.max(o,R.output_tokens)))}});c.stdout.on("data",b=>m(b)),c.stderr.on("data",b=>{let T=b.toString("utf8");T.trim()&&(a=(a??"")+T)});let h=setTimeout(()=>{try{c.kill("SIGKILL")}catch{}},1800*1e3);c.on("close",b=>{clearTimeout(h),e.signal.removeEventListener("abort",u);let T=b===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${b}`);t({output_markdown:n,input_tokens:r,output_tokens:o,error:T})}),c.on("error",b=>{clearTimeout(h),e.signal.removeEventListener("abort",u),t({output_markdown:n,input_tokens:r,output_tokens:o,error:b instanceof Error?b.message:String(b)})})})}function xn(e){if(e.scope==="cluster"){let r=LS(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=CS(e.target_id);if(t.length===0)return null;let s=t.slice(0,30),n=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:s,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:n}}}function Yu(e){let t=xn(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 s=Xu(e.intent),n=ps.get(s);if(n){let u=Ot.get(n);if(u&&u.status==="running")return{jobId:n,reused:!0};ps.delete(s)}let r=kS(),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 Ot.set(r,c),ps.set(s,r),ne({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=vS(t.cluster.member_session_ids,t.cluster.snippet);u=MS(t.cluster,b)}else if(e.intent.scope==="project"&&t.project_clusters)u=DS(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=$S(e.intent,u),h=await(HS??qS)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(b,T)=>{c.output_markdown=T,us(c,"partial",Nt(c))}});if(c.output_markdown=h.output_markdown,c.input_tokens=h.input_tokens,c.output_tokens=h.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),us(c,"done",Nt(c)),ne({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(h.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=h.error,us(c,"done",Nt(c)),ne({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:h.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),us(c,"done",Nt(c)),ne({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{xu({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,us(c,"done",Nt(c)),ne({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{OS(c)}})(),{jobId:r,reused:!1}}async function*zu(e,t=0){let s=Ot.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function Ku(e){let t=Ot.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Io(e){let t=Ot.get(e);return t?Nt(t):null}import{randomUUID as nT}from"node:crypto";U();Bs();import{randomUUID as QS}from"node:crypto";U();var Vu=10,Zu=20;function XS(e){if(!e)return!1;let t=e.split(`
1472
+ `);if(t.length<4)return!1;let s=0,n=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?s++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&n++;return s/t.length>.7||n/t.length>.5}function JS(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let s=new Float32Array(t),n=new Float32Array(e.buffer,e.byteOffset,t);return s.set(n),s}function Qu(e){let t=0;for(let n=0;n<e.length;n++)t+=e[n]*e[n];if(t<=0)return!1;let s=1/Math.sqrt(t);for(let n=0;n<e.length;n++)e[n]*=s;return!0}function vo(e){if(e.length===0)return null;let t=e[0].length,s=new Float32Array(t);for(let n of e)if(n.length===t)for(let r=0;r<t;r++)s[r]+=n[r];for(let n=0;n<t;n++)s[n]/=e.length;return Qu(s)?s:null}function ed(e){let t=new Map;if(e.length===0)return t;let s=f(),n=e.map(()=>"?").join(","),r=[];try{r=s.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1473
1473
  cm.text AS text, v.embedding AS embedding
1474
1474
  FROM chunk_meta cm
1475
1475
  JOIN vec_chunks v ON v.rowid = cm.rowid
1476
1476
  WHERE cm.stale = 0
1477
1477
  AND cm.session_id IN (${n})
1478
- 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(M=>!lS(M.text)),d=u.length>0?u:c,m=[];for(let M of d){let W=uS(M.embedding);W&&vu(W)&&m.push(W)}if(m.length===0)continue;let h=Math.min(Lu,m.length),b=Math.max(0,m.length-Lu),T=m.slice(0,h),S=m.slice(b),w=Eo(T),R=Eo(S),D=Eo(m),O=new Map;for(let M=0;M<T.length&&O.size<Cu;M++)O.set(M,T[M]);for(let M=0;M<S.length&&O.size<Cu;M++)O.set(b+M,S[M]);let Y=Array.from(O.values());t.set(a,{session_id:a,full_mean:D,head_pool:w,tail_pool:R,sample_chunks:Y})}return t}function dS(e,t){if(e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function ju(e,t=.8){let s=new Map,n=[],r=[];for(let[d,m]of e)m.full_mean&&(n.push(d),r.push(m.full_mean));if(n.length===0)return s;let o=new Int32Array(n.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 h=d;for(;o[h]!==m;){let b=o[h];o[h]=m,h=b}return m},c=(d,m)=>{let h=a(d),b=a(m);h!==b&&(o[h]=b)};for(let d=0;d<n.length;d++)for(let m=d+1;m<n.length;m++)dS(r[d],r[m])>=t&&c(d,m);let u=new Map;for(let d=0;d<n.length;d++){let m=a(d),h=u.get(m);h===void 0&&(h=u.size,u.set(m,h)),s.set(n[d],h)}return s}var Ce={lo:.4,hi:.7},ht="claude-haiku-4-5-20251001",Et=50,Mu=1500,Du=50,pS={same_workflow:.15,unrelated:-.2,unsure:0};function Fu(e,t,s=Ce){if(s.lo>s.hi)throw new Error(`borderline band invalid: lo=${s.lo} > hi=${s.hi}`);let n=[];for(let r of e){if(r.confidence<s.lo||r.confidence>s.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||n.push({parent:o,child:a,step1:r})}return n.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),n}function Pu(e,t=Ce){let s=0;for(let n of e)n.confidence>=t.lo&&n.confidence<=t.hi&&s++;return s}function mS(e,t){let s=e.replace(/\s+/g," ").trim();return s.length>t?s.slice(0,t-1)+"\u2026":s}function gS(e,t){if(e===null)return"unknown";let s=t-e;if(s<0)return"overlap";let n=Math.round(s/6e4);if(n<60)return`${n}m`;let r=Math.round(s/36e5);return r<24?`${r}h`:`${Math.round(s/864e5)}d`}function _S(e){let s=e.parent.recent_user_messages,n=e.child.recent_user_messages,r=s.slice(0,3),o=s.length>3?s.slice(-3):[],a=n.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(m=>` - ${mS(m,500)}`).join(`
1479
- `),u=gS(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(`
1478
+ 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(D=>!XS(D.text)),d=u.length>0?u:c,m=[];for(let D of d){let H=JS(D.embedding);H&&Qu(H)&&m.push(H)}if(m.length===0)continue;let h=Math.min(Vu,m.length),b=Math.max(0,m.length-Vu),T=m.slice(0,h),S=m.slice(b),w=vo(T),R=vo(S),j=vo(m),x=new Map;for(let D=0;D<T.length&&x.size<Zu;D++)x.set(D,T[D]);for(let D=0;D<S.length&&x.size<Zu;D++)x.set(b+D,S[D]);let q=Array.from(x.values());t.set(a,{session_id:a,full_mean:j,head_pool:w,tail_pool:R,sample_chunks:q})}return t}function GS(e,t){if(e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function td(e,t=.8){let s=new Map,n=[],r=[];for(let[d,m]of e)m.full_mean&&(n.push(d),r.push(m.full_mean));if(n.length===0)return s;let o=new Int32Array(n.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 h=d;for(;o[h]!==m;){let b=o[h];o[h]=m,h=b}return m},c=(d,m)=>{let h=a(d),b=a(m);h!==b&&(o[h]=b)};for(let d=0;d<n.length;d++)for(let m=d+1;m<n.length;m++)GS(r[d],r[m])>=t&&c(d,m);let u=new Map;for(let d=0;d<n.length;d++){let m=a(d),h=u.get(m);h===void 0&&(h=u.size,u.set(m,h)),s.set(n[d],h)}return s}var Pe={lo:.4,hi:.7},Lt="claude-haiku-4-5-20251001",Ct=50,sd=1500,nd=50,YS={same_workflow:.15,unrelated:-.2,unsure:0};function rd(e,t,s=Pe){if(s.lo>s.hi)throw new Error(`borderline band invalid: lo=${s.lo} > hi=${s.hi}`);let n=[];for(let r of e){if(r.confidence<s.lo||r.confidence>s.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||n.push({parent:o,child:a,step1:r})}return n.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),n}function od(e,t=Pe){let s=0;for(let n of e)n.confidence>=t.lo&&n.confidence<=t.hi&&s++;return s}function zS(e,t){let s=e.replace(/\s+/g," ").trim();return s.length>t?s.slice(0,t-1)+"\u2026":s}function KS(e,t){if(e===null)return"unknown";let s=t-e;if(s<0)return"overlap";let n=Math.round(s/6e4);if(n<60)return`${n}m`;let r=Math.round(s/36e5);return r<24?`${r}h`:`${Math.round(s/864e5)}d`}function VS(e){let s=e.parent.recent_user_messages,n=e.child.recent_user_messages,r=s.slice(0,3),o=s.length>3?s.slice(-3):[],a=n.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(m=>` - ${zS(m,500)}`).join(`
1479
+ `),u=KS(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(`
1480
1480
  `),` 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(`
1481
- `)}function fS(e){if(!e)return null;let t=e.trim();if(!t)return null;let s=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(s=u.result.trim())}catch{}let n=s.match(/\{[\s\S]*\}/);if(!n)return null;let r;try{r=JSON.parse(n[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:pS[a]}}async function Uu(e,t={}){if(t.signal?.aborted)return null;let s=t.model??ht,n=_S(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(he(),We));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(n,{model:s}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:fS(o.stdout)}function $u(e){let t=[];for(let s of e.proposals){let n=`${s.parent_id}::${s.child_id}`,r=e.rescored.get(n);if(!r){t.push(s);continue}let o=Math.max(0,Math.min(1,s.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({...s,confidence:o,reasons:[...s.reasons,a]})}return t}function Bu(e){return`${e.parent_id}::${e.child_id}`}async function ES(e){if(e.signal?.aborted)return null;let t,s;try{({spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(he(),We)))}catch{return null}if(!s())return null;let n=[];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)");n.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:
1481
+ `)}function ZS(e){if(!e)return null;let t=e.trim();if(!t)return null;let s=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(s=u.result.trim())}catch{}let n=s.match(/\{[\s\S]*\}/);if(!n)return null;let r;try{r=JSON.parse(n[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:YS[a]}}async function id(e,t={}){if(t.signal?.aborted)return null;let s=t.model??Lt,n=VS(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(ye(),tt));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(n,{model:s}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:ZS(o.stdout)}function ad(e){let t=[];for(let s of e.proposals){let n=`${s.parent_id}::${s.child_id}`,r=e.rescored.get(n);if(!r){t.push(s);continue}let o=Math.max(0,Math.min(1,s.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({...s,confidence:o,reasons:[...s.reasons,a]})}return t}function cd(e){return`${e.parent_id}::${e.child_id}`}async function eT(e){if(e.signal?.aborted)return null;let t,s;try{({spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(ye(),tt)))}catch{return null}if(!s())return null;let n=[];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)");n.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:
1482
1482
  - 4 to 8 words
1483
1483
  - Title-case, no trailing punctuation
1484
1484
  - Capture the WORKFLOW (e.g., "Update Remotion deps + lint cleanup"), not any one session
@@ -1489,12 +1489,12 @@ Sessions:
1489
1489
  `+n.join(`
1490
1490
  `)+`
1491
1491
 
1492
- Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(h=>{e.signal&&(a=()=>h(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 h=JSON.parse(d);m=typeof h.result=="string"?h.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 bo(e){return bS(e)}function So(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 s=e.map(o=>o.id),n=new Map;try{n=Iu(s)}catch{n=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let m of a){let h=n.get(m.id);h&&c.set(m.id,h)}let u=ju(c,.8),d=[];for(let m of a){let h=SS(m,n,u);h&&d.push(h)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function Wu(e){return f().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1492
+ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(h=>{e.signal&&(a=()=>h(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 h=JSON.parse(d);m=typeof h.result=="string"?h.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 jo(e){return tT(e)}function Mo(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 s=e.map(o=>o.id),n=new Map;try{n=ed(s)}catch{n=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let m of a){let h=n.get(m.id);h&&c.set(m.id,h)}let u=td(c,.8),d=[];for(let m of a){let h=sT(m,n,u);h&&d.push(h)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function ud(e){return f().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1493
1493
  FROM threads t
1494
1494
  JOIN thread_edges te ON te.thread_id = t.id
1495
1495
  JOIN sessions s ON s.id = te.session_id
1496
1496
  JOIN projects p ON p.id = s.project_id
1497
- WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function bS(e){let t=f(),s={},n="1=1 AND s.message_count > 2";return n+=" 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&&(n+=" AND p.name = @project",s.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1497
+ WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function tT(e){let t=f(),s={},n="1=1 AND s.message_count > 2";return n+=" 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&&(n+=" AND p.name = @project",s.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1498
1498
  s.first_user_message, s.auto_title,
1499
1499
  NULLIF(sa.alias, '') AS alias,
1500
1500
  s.file_path
@@ -1502,27 +1502,27 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1502
1502
  JOIN projects p ON p.id = s.project_id
1503
1503
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1504
1504
  WHERE ${n}
1505
- ORDER BY p.name ASC, s.started_at ASC`).all(s)}function SS(e,t,s){if(!e.started_at)return null;let n=Date.parse(e.started_at);if(!Number.isFinite(n))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=Ks(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:n,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:s?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function Hu(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 qu(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??Ce,s=e.rescore.cap??Et,n=e.rescore.model??ht,r=new Map(e.scannables.map(b=>[b.id,b])),o=Fu(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>s)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 Uu(T,{model:n,signal:e.signal});S?(a.set(Bu(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:$u({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 Xu(e){let t=f(),s=new Map(e.rows.map(d=>[d.id,d])),n=Mc(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of n){if(c++,e.signal?.aborted)break;let m=s.get(d.rootId),h=Hu(m);if(!e.llmNames){a.set(d.rootId,h),e.onProgress?.({phase:"naming",current:c,total:n.length,thread_name:h});continue}let T=await ES({rootRow:m,sessionIds:d.sessionIds,rowById:s,signal:e.signal,model:e.model})??h;a.set(d.rootId,T),e.onProgress?.({phase:"naming",current:c,total:n.length,thread_name:T})}return t.transaction(()=>{for(let d of n){if(!a.has(d.rootId))continue;let m=s.get(d.rootId),h=`auto-scan-${hS()}`,b=a.get(d.rootId)??Hu(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(h,b,T,r),t.prepare(`INSERT INTO thread_edges
1505
+ ORDER BY p.name ASC, s.started_at ASC`).all(s)}function sT(e,t,s){if(!e.started_at)return null;let n=Date.parse(e.started_at);if(!Number.isFinite(n))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=ln(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:n,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:s?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function ld(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 dd(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??Pe,s=e.rescore.cap??Ct,n=e.rescore.model??Lt,r=new Map(e.scannables.map(b=>[b.id,b])),o=rd(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>s)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 id(T,{model:n,signal:e.signal});S?(a.set(cd(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:ad({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 pd(e){let t=f(),s=new Map(e.rows.map(d=>[d.id,d])),n=tl(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of n){if(c++,e.signal?.aborted)break;let m=s.get(d.rootId),h=ld(m);if(!e.llmNames){a.set(d.rootId,h),e.onProgress?.({phase:"naming",current:c,total:n.length,thread_name:h});continue}let T=await eT({rootRow:m,sessionIds:d.sessionIds,rowById:s,signal:e.signal,model:e.model})??h;a.set(d.rootId,T),e.onProgress?.({phase:"naming",current:c,total:n.length,thread_name:T})}return t.transaction(()=>{for(let d of n){if(!a.has(d.rootId))continue;let m=s.get(d.rootId),h=`auto-scan-${QS()}`,b=a.get(d.rootId)??ld(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(h,b,T,r),t.prepare(`INSERT INTO thread_edges
1506
1506
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1507
1507
  VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(h,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 R=S.get(w);R&&t.prepare(`INSERT INTO thread_edges
1508
1508
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1509
- VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(h,w,R.parent_id,R.confidence,r)}o.push({thread_id:h,name:b,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var St=new Map,ns=new Map,yS=300*1e3;function bt(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function wS(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{St.delete(e.jobId),ns.get(e.project)===e.jobId&&ns.delete(e.project)},yS),e.cleanupTimer.unref?.())}function fn(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 RS=500,AS=30,To="claude-haiku-4-5-20251001",Ju={"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 Yu(e){return Ju[e]??Ju[To]}function Gu(e){let t=typeof e.threshold=="number"?e.threshold:Gs;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let s=e.model??To,n="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Ce.lo,hi:e.llm_rescore?.band_hi??Ce.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??ht,c=bo({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:Me(0,s),accuracy_caveat:n,model:s,llm_rescore:r?{enabled:!0,band:o,cap:Et,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:Me(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=So(c),m=u.get(e.project)??[],h=d.get(e.project)??[],b=r?Math.min(t,o.lo):t,T=ot(h,b),S=r?T.filter(i=>i.confidence>=t):T,w=zu(S),R=w*RS,D=w*AS,O=Yu(s),Y=R/1e6*O.in,M=D/1e6*O.out,W=Y+M,oe=Me(w,s),$=Wu(e.project),se=null;if(r){let i=Pu(T,o),l=Yu(a),p=i*Mu,g=i*Du,_=p/1e6*l.in+g/1e6*l.out;se={enabled:!0,band:o,cap:Et,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:Me(i,a),exceeds_cap:i>Et,model:a}}return{eligible_sessions:m.length,proposed_edges:S.length,estimated_threads:w,estimated_llm_calls:w,estimated_input_tokens_max:R,estimated_output_tokens_max:D,estimated_cost_usd_max:Math.round(W*1e4)/1e4,existing_auto_scan_threads:$,plan_window_estimate:oe,accuracy_caveat:n,model:s,llm_rescore:se}}function zu(e){let t=new Map,s=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},n=(o,a)=>{let c=s(o),u=s(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),n(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(s(o));return r.size}function Ku(e){let t=typeof e.threshold=="number"?e.threshold:Gs;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let s=e.llm_names??!0,n=e.model??To,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Ce.lo,hi:e.llm_rescore?.band_hi??Ce.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??ht,c=ns.get(e.project);if(c){let W=St.get(c);if(W&&W.status==="running")return{jobId:c,reused:!0};ns.delete(e.project)}let u=bo({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:m}=So(u),h=d.get(e.project),b=m.get(e.project);if(!h||!b)return{error:`project "${e.project}" not found`};let T=r?Math.min(t,o.lo):t,S=ot(b,T),w=r?S.filter(W=>W.confidence>=t):S;if(S.length===0||w.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let R=TS(),D=new AbortController,O=zu(w),Y=new Date().toISOString(),M={jobId:R,project:e.project,threshold:t,llmNames:s,status:"running",startedAt:Y,endedAt:null,events:[],waiters:new Set,controller:D,totalThreads:O,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 St.set(R,M),ns.set(e.project,R),(async()=>{await Promise.resolve();try{bt(M,"progress",{phase:"linking",edges_proposed:S.length,estimated_threads:O});let W=S;if(r){let $=await qu({proposals:S,scannables:b,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:D.signal,onProgress:se=>{se.phase==="rescoring"&&bt(M,"progress",{phase:"rescoring",current:se.current,total:se.total,verdict:se.verdict})}});W=$.proposals,M.rescore={enabled:!0,considered:$.considered,promoted:$.promoted,demoted:$.demoted,unsure:$.unsure,failed:$.failed,capped:$.capped}}else W=S.filter($=>$.confidence>=t);if(W.length===0){M.status=D.signal.aborted?"cancelled":"done",M.endedAt=new Date().toISOString(),bt(M,"done",{...fn(M),threads:[]});return}let oe=await Xu({project:e.project,rows:h,edges:W,scannables:b,llmNames:s,model:n,signal:D.signal,onProgress:$=>{$.phase==="naming"&&(M.threadsNamed=$.current,M.currentThreadName=$.thread_name??null,bt(M,"progress",{phase:"naming",current:$.current,total:$.total,thread_name:$.thread_name}))}});M.threadsCreated=oe.threads.length,M.edgesWritten=W.length+oe.threads.length,M.status=D.signal.aborted?"cancelled":"done",M.endedAt=new Date().toISOString(),bt(M,"done",{...fn(M),threads:oe.threads})}catch(W){M.status="failed",M.endedAt=new Date().toISOString(),M.error=W instanceof Error?W.message:String(W??"unknown error"),bt(M,"done",fn(M))}finally{wS(M)}})(),{jobId:R,reused:!1}}async function*Vu(e,t=0){let s=St.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function Zu(e){let t=St.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function yo(e){let t=St.get(e);return t?fn(t):null}bs();import{randomUUID as kS}from"node:crypto";var hn=new Map;function Qu(e){let t={id:kS(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return hn.set(t.id,t),t}function En(e){return hn.get(e)}function Tt(e,t){for(let s of e.listeners)s(t)}function ed(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function td(e){let t=hn.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),Tt(t,{type:"status",status:"cancelled"}),!0):!1}function sd(e){return hn.delete(e)}Qe();function bn(e){let{session:t,knownTags:s,minTags:n,maxTags:r}=e,o=s.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 ${n}-${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:",nd(t.first_user_message," ")," message_sample:",nd(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${n}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1510
- `)}function nd(e,t){return e.split(`
1509
+ VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(h,w,R.parent_id,R.confidence,r)}o.push({thread_id:h,name:b,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var vt=new Map,ms=new Map,rT=300*1e3;function It(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function oT(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{vt.delete(e.jobId),ms.get(e.project)===e.jobId&&ms.delete(e.project)},rT),e.cleanupTimer.unref?.())}function Nn(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 iT=500,aT=30,Do="claude-haiku-4-5-20251001",md={"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 gd(e){return md[e]??md[Do]}function _d(e){let t=typeof e.threshold=="number"?e.threshold:an;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let s=e.model??Do,n="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Pe.lo,hi:e.llm_rescore?.band_hi??Pe.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??Lt,c=jo({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:We(0,s),accuracy_caveat:n,model:s,llm_rescore:r?{enabled:!0,band:o,cap:Ct,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:We(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=Mo(c),m=u.get(e.project)??[],h=d.get(e.project)??[],b=r?Math.min(t,o.lo):t,T=Et(h,b),S=r?T.filter(i=>i.confidence>=t):T,w=fd(S),R=w*iT,j=w*aT,x=gd(s),q=R/1e6*x.in,D=j/1e6*x.out,H=q+D,ie=We(w,s),$=ud(e.project),se=null;if(r){let i=od(T,o),l=gd(a),p=i*sd,g=i*nd,_=p/1e6*l.in+g/1e6*l.out;se={enabled:!0,band:o,cap:Ct,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:We(i,a),exceeds_cap:i>Ct,model:a}}return{eligible_sessions:m.length,proposed_edges:S.length,estimated_threads:w,estimated_llm_calls:w,estimated_input_tokens_max:R,estimated_output_tokens_max:j,estimated_cost_usd_max:Math.round(H*1e4)/1e4,existing_auto_scan_threads:$,plan_window_estimate:ie,accuracy_caveat:n,model:s,llm_rescore:se}}function fd(e){let t=new Map,s=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},n=(o,a)=>{let c=s(o),u=s(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),n(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(s(o));return r.size}function hd(e){let t=typeof e.threshold=="number"?e.threshold:an;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let s=e.llm_names??!0,n=e.model??Do,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Pe.lo,hi:e.llm_rescore?.band_hi??Pe.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??Lt,c=ms.get(e.project);if(c){let H=vt.get(c);if(H&&H.status==="running")return{jobId:c,reused:!0};ms.delete(e.project)}let u=jo({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:m}=Mo(u),h=d.get(e.project),b=m.get(e.project);if(!h||!b)return{error:`project "${e.project}" not found`};let T=r?Math.min(t,o.lo):t,S=Et(b,T),w=r?S.filter(H=>H.confidence>=t):S;if(S.length===0||w.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let R=nT(),j=new AbortController,x=fd(w),q=new Date().toISOString(),D={jobId:R,project:e.project,threshold:t,llmNames:s,status:"running",startedAt:q,endedAt:null,events:[],waiters:new Set,controller:j,totalThreads:x,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 vt.set(R,D),ms.set(e.project,R),(async()=>{await Promise.resolve();try{It(D,"progress",{phase:"linking",edges_proposed:S.length,estimated_threads:x});let H=S;if(r){let $=await dd({proposals:S,scannables:b,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:j.signal,onProgress:se=>{se.phase==="rescoring"&&It(D,"progress",{phase:"rescoring",current:se.current,total:se.total,verdict:se.verdict})}});H=$.proposals,D.rescore={enabled:!0,considered:$.considered,promoted:$.promoted,demoted:$.demoted,unsure:$.unsure,failed:$.failed,capped:$.capped}}else H=S.filter($=>$.confidence>=t);if(H.length===0){D.status=j.signal.aborted?"cancelled":"done",D.endedAt=new Date().toISOString(),It(D,"done",{...Nn(D),threads:[]});return}let ie=await pd({project:e.project,rows:h,edges:H,scannables:b,llmNames:s,model:n,signal:j.signal,onProgress:$=>{$.phase==="naming"&&(D.threadsNamed=$.current,D.currentThreadName=$.thread_name??null,It(D,"progress",{phase:"naming",current:$.current,total:$.total,thread_name:$.thread_name}))}});D.threadsCreated=ie.threads.length,D.edgesWritten=H.length+ie.threads.length,D.status=j.signal.aborted?"cancelled":"done",D.endedAt=new Date().toISOString(),It(D,"done",{...Nn(D),threads:ie.threads})}catch(H){D.status="failed",D.endedAt=new Date().toISOString(),D.error=H instanceof Error?H.message:String(H??"unknown error"),It(D,"done",Nn(D))}finally{oT(D)}})(),{jobId:R,reused:!1}}async function*Ed(e,t=0){let s=vt.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function bd(e){let t=vt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Fo(e){let t=vt.get(e);return t?Nn(t):null}Cs();import{randomUUID as cT}from"node:crypto";var On=new Map;function Sd(e){let t={id:cT(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return On.set(t.id,t),t}function Ln(e){return On.get(e)}function jt(e,t){for(let s of e.listeners)s(t)}function Td(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function yd(e){let t=On.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),jt(t,{type:"status",status:"cancelled"}),!0):!1}function wd(e){return On.delete(e)}pt();function Cn(e){let{session:t,knownTags:s,minTags:n,maxTags:r}=e,o=s.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 ${n}-${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:",Rd(t.first_user_message," ")," message_sample:",Rd(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${n}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1510
+ `)}function Rd(e,t){return e.split(`
1511
1511
  `).map(s=>t+s).join(`
1512
- `)}Qe();import{z as rs}from"zod";var xS=rs.object({tags:rs.array(rs.string()).min(1),confidence:rs.number().min(0).max(1),rationale:rs.string().min(1).max(500)});function NS(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function Sn(e,t={}){let s=t.maxTags??10,n;try{n=JSON.parse(NS(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=xS.safeParse(n);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>Be(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,s);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 OS from"@anthropic-ai/sdk";async function Tn(e){let n=(await new OS({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(!n||n.type!=="text")throw new Error("Anthropic response contained no text block");return n.text}function LS(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 yn(e,t={}){let s=t.maxAttempts??3,n=t.baseDelayMs??500,r;for(let o=0;o<s;o++)try{return await e()}catch(a){if(r=a,!LS(a)||o===s-1)throw a;await new Promise(c=>setTimeout(c,n*Math.pow(2,o)))}throw r}async function rd(e,t){e.status="running",Tt(e,{type:"status",status:"running"});let s=Ze();for(let n of t.sessions){if(e.controller.signal.aborted)break;let r=bn({session:n,knownTags:s,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:n.id,project:n.project,alias:n.alias,first_user_message:n.first_user_message,current_tags:n.current_tags,suggestion:null,error:null,applied:!1};try{let a=await yn(()=>Tn({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=Sn(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,Tt(e,{type:"result",result:o}),Tt(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let n=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;Tt(e,{type:"done",summary:{ok:n,failed:r}})}}function od(e,t){let s=0,n=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}=Ve(r.sessionId,a);c&&(s+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),n+=1}o.applied=!0}}return{applied:s,failed:n}}Qe();bs();var z={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},wo=new Set;function is(){return{status:z.status,processed:z.processed,total:z.total,currentSessionId:z.currentSessionId,lastError:z.lastError,lastRunAt:z.lastRunAt}}function id(e){return wo.add(e),()=>{wo.delete(e)}}function os(){let e=is();for(let t of wo)t(e)}async function wn(){let e=Le();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,os();try{let t=et({untaggedOnly:!0,limit:200});if(z.total=t.length,os(),t.length===0){z.status="idle",z.lastRunAt=new Date().toISOString();return}let s=Ze();for(let n of t){if(z.controller.signal.aborted)break;let r=Le();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;z.currentSessionId=n.id,os();let o=bn({session:n,knownTags:s,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await yn(()=>Tn({apiKey:r.apiKey,model:r.model,prompt:o,signal:z.controller.signal})),c=Sn(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{Ve(n.id,u)}catch(d){console.error("[autopilot] addTag failed:",n.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",n.id,a)}z.processed+=1,os(),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,os()}}}import{execFileSync as CS}from"node:child_process";import{existsSync as ad,readFileSync as vS,writeFileSync as cd}from"node:fs";import{homedir as IS}from"node:os";import{dirname as jS,join as MS,resolve as DS}from"node:path";import{fileURLToPath as FS}from"node:url";var yt=MS(IS(),".claude.json"),PS=jS(FS(import.meta.url)),US=DS(PS,"..","mcp","server.js");function Ao(){if(!ad(yt))return{};try{let e=vS(yt,"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 Ro=new Map;function $S(e){let t=Ro.get(e);if(t!==void 0)return t;try{return CS("command",["-v",e],{stdio:"ignore"}),Ro.set(e,!0),!0}catch{return Ro.set(e,!1),!1}}function BS(){return $S("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[US]}}function De(){let t=Ao().mcpServers?.recall;return{configPath:yt,configExists:ad(yt),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function ld(){let e=Ao(),t=e.mcpServers??{},s=BS(),n=t.recall;if(n?.command===s.command&&JSON.stringify(n?.args??[])===JSON.stringify(s.args))return De();let o={command:s.command};s.args.length>0&&(o.args=s.args);let a={...e,mcpServers:{...t,recall:o}};return cd(yt,JSON.stringify(a,null,2)),De()}function ud(){let e=Ao(),t=e.mcpServers??{};if(!t.recall)return De();let{recall:s,...n}=t,r={...e,mcpServers:n};return cd(yt,JSON.stringify(r,null,2)),De()}import{existsSync as dd,mkdirSync as HS,readFileSync as WS,writeFileSync as pd}from"node:fs";import{homedir as qS}from"node:os";import{join as md}from"node:path";import{z as Fe}from"zod";function gd(){return process.env.RECALL_HOME??md(qS(),".recall")}function _d(){let e=gd();dd(e)||HS(e,{recursive:!0})}function xo(){return md(gd(),"onboarding.json")}var Rn=Fe.object({version:Fe.literal(1).default(1),completed:Fe.boolean().default(!1),skipped:Fe.boolean().default(!1),finishedAt:Fe.string().nullable().default(null),completedSteps:Fe.array(Fe.string().max(100)).max(50).default([]),threadsIntroSeen:Fe.boolean().default(!1)}),ko={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function An(){let e=xo();if(!dd(e))return{...ko};try{let t=JSON.parse(WS(e,"utf8")),s=Rn.safeParse(t);return s.success?s.data:{...ko}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...ko}}}function fd(e){_d();let t=An(),s=Rn.parse({...t,...e,completedSteps:XS([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return pd(xo(),JSON.stringify(s,null,2)),s}function hd(){_d();let e=An(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return pd(xo(),JSON.stringify(t,null,2)),t}function XS(e){let t=new Set,s=[];for(let n of e)t.has(n)||(t.add(n),s.push(n));return s}he();nr();sr();H();H();import{createReadStream as JS}from"node:fs";import{createInterface as YS}from"node:readline";function kn(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Lo(e){let t=e?.usage;if(!t||typeof t!="object")return null;let s={inputTokens:kn(t.input_tokens),outputTokens:kn(t.output_tokens),cacheCreateTokens:kn(t.cache_creation_input_tokens),cacheReadTokens:kn(t.cache_read_input_tokens)};return s.inputTokens===0&&s.outputTokens===0&&s.cacheCreateTokens===0&&s.cacheReadTokens===0?null:s}var GS=/\x1B\[[0-9;]*[a-zA-Z]/g;function No(e){return e.replace(GS,"")}var Oo=12e3;function Ed(e,t){if(e.length<=Oo)return e;let s=e.slice(0,Oo),n=e.length-Oo;return`${s}
1512
+ `)}pt();import{z as gs}from"zod";var lT=gs.object({tags:gs.array(gs.string()).min(1),confidence:gs.number().min(0).max(1),rationale:gs.string().min(1).max(500)});function uT(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function In(e,t={}){let s=t.maxTags??10,n;try{n=JSON.parse(uT(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=lT.safeParse(n);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>Qe(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,s);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 dT from"@anthropic-ai/sdk";async function vn(e){let n=(await new dT({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(!n||n.type!=="text")throw new Error("Anthropic response contained no text block");return n.text}function pT(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 jn(e,t={}){let s=t.maxAttempts??3,n=t.baseDelayMs??500,r;for(let o=0;o<s;o++)try{return await e()}catch(a){if(r=a,!pT(a)||o===s-1)throw a;await new Promise(c=>setTimeout(c,n*Math.pow(2,o)))}throw r}async function kd(e,t){e.status="running",jt(e,{type:"status",status:"running"});let s=dt();for(let n of t.sessions){if(e.controller.signal.aborted)break;let r=Cn({session:n,knownTags:s,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:n.id,project:n.project,alias:n.alias,first_user_message:n.first_user_message,current_tags:n.current_tags,suggestion:null,error:null,applied:!1};try{let a=await jn(()=>vn({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=In(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,jt(e,{type:"result",result:o}),jt(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let n=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;jt(e,{type:"done",summary:{ok:n,failed:r}})}}function Ad(e,t){let s=0,n=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}=ut(r.sessionId,a);c&&(s+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),n+=1}o.applied=!0}}return{applied:s,failed:n}}pt();Cs();var Z={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},Po=new Set;function fs(){return{status:Z.status,processed:Z.processed,total:Z.total,currentSessionId:Z.currentSessionId,lastError:Z.lastError,lastRunAt:Z.lastRunAt}}function xd(e){return Po.add(e),()=>{Po.delete(e)}}function _s(){let e=fs();for(let t of Po)t(e)}async function Mn(){let e=Fe();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,_s();try{let t=mt({untaggedOnly:!0,limit:200});if(Z.total=t.length,_s(),t.length===0){Z.status="idle",Z.lastRunAt=new Date().toISOString();return}let s=dt();for(let n of t){if(Z.controller.signal.aborted)break;let r=Fe();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;Z.currentSessionId=n.id,_s();let o=Cn({session:n,knownTags:s,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await jn(()=>vn({apiKey:r.apiKey,model:r.model,prompt:o,signal:Z.controller.signal})),c=In(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{ut(n.id,u)}catch(d){console.error("[autopilot] addTag failed:",n.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",n.id,a)}Z.processed+=1,_s(),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,_s()}}}import{execFileSync as mT}from"node:child_process";import{existsSync as Nd,readFileSync as gT,writeFileSync as Od}from"node:fs";import{homedir as _T}from"node:os";import{dirname as fT,join as hT,resolve as ET}from"node:path";import{fileURLToPath as bT}from"node:url";var Mt=hT(_T(),".claude.json"),ST=fT(bT(import.meta.url)),TT=ET(ST,"..","mcp","server.js");function $o(){if(!Nd(Mt))return{};try{let e=gT(Mt,"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 Uo=new Map;function yT(e){let t=Uo.get(e);if(t!==void 0)return t;try{return mT("command",["-v",e],{stdio:"ignore"}),Uo.set(e,!0),!0}catch{return Uo.set(e,!1),!1}}function wT(){return yT("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[TT]}}function qe(){let t=$o().mcpServers?.recall;return{configPath:Mt,configExists:Nd(Mt),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function Ld(){let e=$o(),t=e.mcpServers??{},s=wT(),n=t.recall;if(n?.command===s.command&&JSON.stringify(n?.args??[])===JSON.stringify(s.args))return qe();let o={command:s.command};s.args.length>0&&(o.args=s.args);let a={...e,mcpServers:{...t,recall:o}};return Od(Mt,JSON.stringify(a,null,2)),qe()}function Cd(){let e=$o(),t=e.mcpServers??{};if(!t.recall)return qe();let{recall:s,...n}=t,r={...e,mcpServers:n};return Od(Mt,JSON.stringify(r,null,2)),qe()}import{existsSync as Id,mkdirSync as RT,readFileSync as kT,writeFileSync as vd}from"node:fs";import{homedir as AT}from"node:os";import{join as jd}from"node:path";import{z as Xe}from"zod";function Md(){return process.env.RECALL_HOME??jd(AT(),".recall")}function Dd(){let e=Md();Id(e)||RT(e,{recursive:!0})}function Bo(){return jd(Md(),"onboarding.json")}var Dn=Xe.object({version:Xe.literal(1).default(1),completed:Xe.boolean().default(!1),skipped:Xe.boolean().default(!1),finishedAt:Xe.string().nullable().default(null),completedSteps:Xe.array(Xe.string().max(100)).max(50).default([]),threadsIntroSeen:Xe.boolean().default(!1)}),Ho={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function Fn(){let e=Bo();if(!Id(e))return{...Ho};try{let t=JSON.parse(kT(e,"utf8")),s=Dn.safeParse(t);return s.success?s.data:{...Ho}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...Ho}}}function Fd(e){Dd();let t=Fn(),s=Dn.parse({...t,...e,completedSteps:xT([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return vd(Bo(),JSON.stringify(s,null,2)),s}function Pd(){Dd();let e=Fn(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return vd(Bo(),JSON.stringify(t,null,2)),t}function xT(e){let t=new Set,s=[];for(let n of e)t.has(n)||(t.add(n),s.push(n));return s}ye();Er();hr();U();U();import{createReadStream as NT}from"node:fs";import{createInterface as OT}from"node:readline";function Pn(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Xo(e){let t=e?.usage;if(!t||typeof t!="object")return null;let s={inputTokens:Pn(t.input_tokens),outputTokens:Pn(t.output_tokens),cacheCreateTokens:Pn(t.cache_creation_input_tokens),cacheReadTokens:Pn(t.cache_read_input_tokens)};return s.inputTokens===0&&s.outputTokens===0&&s.cacheCreateTokens===0&&s.cacheReadTokens===0?null:s}var LT=/\x1B\[[0-9;]*[a-zA-Z]/g;function Wo(e){return e.replace(LT,"")}var qo=12e3;function Ud(e,t){if(e.length<=qo)return e;let s=e.slice(0,qo),n=e.length-qo;return`${s}
1513
1513
 
1514
- \u27E8\u2026 ${n.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function zS(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function KS(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let s of e.content)if(s&&typeof s=="object"){let n=s;n.type==="text"&&typeof n.text=="string"?t.push(n.text):n.type==="image"&&t.push("[image]")}return t.join(`
1515
- `)}return""}function VS(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:No(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],s=[];for(let n of e.content)if(!(!n||typeof n!="object")){if(n.type==="text"&&typeof n.text=="string"){t.push(No(n.text));continue}if(n.type==="tool_use"&&typeof n.name=="string"){s.push(n.name);let r=n.input!=null?zS(n.input):"",o=Ed(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${n.name}\`**
1514
+ \u27E8\u2026 ${n.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function CT(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function IT(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let s of e.content)if(s&&typeof s=="object"){let n=s;n.type==="text"&&typeof n.text=="string"?t.push(n.text):n.type==="image"&&t.push("[image]")}return t.join(`
1515
+ `)}return""}function vT(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Wo(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],s=[];for(let n of e.content)if(!(!n||typeof n!="object")){if(n.type==="text"&&typeof n.text=="string"){t.push(Wo(n.text));continue}if(n.type==="tool_use"&&typeof n.name=="string"){s.push(n.name);let r=n.input!=null?CT(n.input):"",o=Ud(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${n.name}\`**
1516
1516
 
1517
1517
  \`\`\`json
1518
1518
  ${o}
1519
- \`\`\``);continue}if(n.type==="tool_result"){let r=No(KS(n));if(r){let o=Ed(r,"tool result");t.push(`**Tool result**
1519
+ \`\`\``);continue}if(n.type==="tool_result"){let r=Wo(IT(n));if(r){let o=Ud(r,"tool result");t.push(`**Tool result**
1520
1520
 
1521
1521
  \`\`\`
1522
1522
  ${o}
1523
1523
  \`\`\``)}else t.push("_(tool result was empty or image-only)_");continue}if(n.type==="image"){t.push("_(image)_");continue}t.push(`_(unknown block: ${n.type})_`)}return{text:t.join(`
1524
1524
 
1525
- `),toolNames:s}}async function*bd(e){let t=JS(e,{encoding:"utf8"}),s=YS({input:t,crlfDelay:1/0});for await(let n of s){if(!n.trim())continue;let r;try{r=JSON.parse(n)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:a}=VS(r.message);yield{uuid:r.uuid,parentUuid:r.parentUuid??null,sessionId:r.sessionId,type:r.type??"unknown",role:r.message?.role??null,timestamp:r.timestamp??null,isSidechain:r.isSidechain===!0,cwd:r.cwd??null,gitBranch:r.gitBranch??null,version:r.version??null,contentText:o,toolNames:a,raw:n,usage:Lo(r.message),model:r.message?.model??null}}}function Sd(e,t,s){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let n=e.prepare(`
1525
+ `),toolNames:s}}async function*$d(e){let t=NT(e,{encoding:"utf8"}),s=OT({input:t,crlfDelay:1/0});for await(let n of s){if(!n.trim())continue;let r;try{r=JSON.parse(n)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:a}=vT(r.message);yield{uuid:r.uuid,parentUuid:r.parentUuid??null,sessionId:r.sessionId,type:r.type??"unknown",role:r.message?.role??null,timestamp:r.timestamp??null,isSidechain:r.isSidechain===!0,cwd:r.cwd??null,gitBranch:r.gitBranch??null,version:r.version??null,contentText:o,toolNames:a,raw:n,usage:Xo(r.message),model:r.message?.model??null}}}function Hd(e,t,s){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let n=e.prepare(`
1526
1526
  INSERT INTO message_usage (
1527
1527
  message_uuid, session_id, model,
1528
1528
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1539,7 +1539,7 @@ ${o}
1539
1539
  cache_create_tokens = excluded.cache_create_tokens,
1540
1540
  cache_read_tokens = excluded.cache_read_tokens,
1541
1541
  timestamp = excluded.timestamp
1542
- `);for(let r of s)r.usage&&r.role==="assistant"&&n.run({uuid:r.uuid,session_id:t,model:r.model,input:r.usage.inputTokens,output:r.usage.outputTokens,cc:r.usage.cacheCreateTokens,cr:r.usage.cacheReadTokens,ts:r.timestamp})}function xn(e,t){let s=e.prepare(`SELECT
1542
+ `);for(let r of s)r.usage&&r.role==="assistant"&&n.run({uuid:r.uuid,session_id:t,model:r.model,input:r.usage.inputTokens,output:r.usage.outputTokens,cc:r.usage.cacheCreateTokens,cr:r.usage.cacheReadTokens,ts:r.timestamp})}function Un(e,t){let s=e.prepare(`SELECT
1543
1543
  COALESCE(SUM(input_tokens), 0) AS input_tokens,
1544
1544
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
1545
1545
  COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
@@ -1554,14 +1554,14 @@ ${o}
1554
1554
  total_cache_create_tokens = @cc,
1555
1555
  total_cache_read_tokens = @cr,
1556
1556
  primary_model = @model
1557
- WHERE id = @id`).run({id:t,input:s.input_tokens,output:s.output_tokens,cc:s.cache_create_tokens,cr:s.cache_read_tokens,model:n?.model??null})}var ZS=500,Nn=new Set,Td=null,On=!1;function yd(){return Nn.size}function QS(){return`
1557
+ WHERE id = @id`).run({id:t,input:s.input_tokens,output:s.output_tokens,cc:s.cache_create_tokens,cr:s.cache_read_tokens,model:n?.model??null})}var jT=500,$n=new Set,Bd=null,Hn=!1;function Wd(){return $n.size}function MT(){return`
1558
1558
  SELECT m.uuid, m.session_id, m.timestamp, m.raw_json
1559
1559
  FROM messages m
1560
1560
  LEFT JOIN message_usage mu ON mu.message_uuid = m.uuid
1561
1561
  WHERE m.role = 'assistant' AND mu.message_uuid IS NULL
1562
1562
  AND m.uuid NOT IN (SELECT value FROM json_each(?))
1563
1563
  LIMIT ?
1564
- `}function wd(e,t){let s=t.limit??Number.MAX_SAFE_INTEGER,n=Math.max(1,t.chunkSize??ZS),r=e.prepare(QS()),o=e.prepare(`
1564
+ `}function qd(e,t){let s=t.limit??Number.MAX_SAFE_INTEGER,n=Math.max(1,t.chunkSize??jT),r=e.prepare(MT()),o=e.prepare(`
1565
1565
  INSERT INTO message_usage (
1566
1566
  message_uuid, session_id, model,
1567
1567
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1571,7 +1571,7 @@ ${o}
1571
1571
  @input, @output, @cc, @cr, @ts
1572
1572
  )
1573
1573
  ON CONFLICT(message_uuid) DO NOTHING
1574
- `),a=0,c=0,u=new Set;for(;a<s;){let d=Math.min(n,s-a),m=JSON.stringify([...Nn]),h=r.all(m,d);if(h.length===0)break;let b=new Set;if(e.transaction(()=>{for(let S of h){let w;try{w=JSON.parse(S.raw_json)}catch{Nn.add(S.uuid);continue}let R=Lo(w.message);if(!R){Nn.add(S.uuid);continue}o.run({uuid:S.uuid,session_id:S.session_id,model:w.message?.model??null,input:R.inputTokens,output:R.outputTokens,cc:R.cacheCreateTokens,cr:R.cacheReadTokens,ts:S.timestamp}),c+=1,b.add(S.session_id)}for(let S of b)xn(e,S),u.add(S)})(),a+=h.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:h.length<d}),h.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function Rd(e={}){return wd(f(),e)}function Ad(e={}){return On?!1:(On=!0,queueMicrotask(()=>{try{let t=wd(f(),e);Td={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{On=!1}}),!0)}function kd(){return On}function Co(){return Td}var eT=[[/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}]],xd={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function Pe(e){if(!e)return xd;for(let[t,s]of eT)if(t.test(e))return s;return xd}function ye(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 h=Pe(d);a.input+=m.inputTokens/1e6*h.inputCentsPerMtok,a.output+=m.outputTokens/1e6*h.outputCentsPerMtok,a.cacheCreate+=m.cacheCreateTokens/1e6*h.cacheCreateCentsPerMtok,a.cacheRead+=m.cacheReadTokens/1e6*h.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 s=Pe(t),n={input:e.inputTokens/1e6*s.inputCentsPerMtok,output:e.outputTokens/1e6*s.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*s.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*s.cacheReadCentsPerMtok},r=n.input+n.output+n.cacheCreate+n.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:n}}function wt(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 Rt(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 vo(e){let t=new Map;for(let n of e){let r=n.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=n.input_tokens,o.outputTokens+=n.output_tokens,o.cacheCreateTokens+=n.cache_create_tokens,o.cacheReadTokens+=n.cache_read_tokens,o.messageCount+=n.n,t.set(r,o)}let s=[];for(let[n,r]of t.entries()){let o=ye({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},n);s.push({model:n,modelLabel:Pe(n).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return s.sort((n,r)=>r.cost.cents-n.cost.cents)}function Io(e){let t={};for(let s of e)t[s.model??"__unknown__"]={inputTokens:s.inputTokens,outputTokens:s.outputTokens,cacheCreateTokens:s.cacheCreateTokens,cacheReadTokens:s.cacheReadTokens};return{byModel:t}}function Nd(e){let t=f(),s=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1574
+ `),a=0,c=0,u=new Set;for(;a<s;){let d=Math.min(n,s-a),m=JSON.stringify([...$n]),h=r.all(m,d);if(h.length===0)break;let b=new Set;if(e.transaction(()=>{for(let S of h){let w;try{w=JSON.parse(S.raw_json)}catch{$n.add(S.uuid);continue}let R=Xo(w.message);if(!R){$n.add(S.uuid);continue}o.run({uuid:S.uuid,session_id:S.session_id,model:w.message?.model??null,input:R.inputTokens,output:R.outputTokens,cc:R.cacheCreateTokens,cr:R.cacheReadTokens,ts:S.timestamp}),c+=1,b.add(S.session_id)}for(let S of b)Un(e,S),u.add(S)})(),a+=h.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:h.length<d}),h.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function Xd(e={}){return qd(f(),e)}function Jd(e={}){return Hn?!1:(Hn=!0,queueMicrotask(()=>{try{let t=qd(f(),e);Bd={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{Hn=!1}}),!0)}function Gd(){return Hn}function Jo(){return Bd}var DT=[[/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}]],Yd={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function Je(e){if(!e)return Yd;for(let[t,s]of DT)if(t.test(e))return s;return Yd}function Ae(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 h=Je(d);a.input+=m.inputTokens/1e6*h.inputCentsPerMtok,a.output+=m.outputTokens/1e6*h.outputCentsPerMtok,a.cacheCreate+=m.cacheCreateTokens/1e6*h.cacheCreateCentsPerMtok,a.cacheRead+=m.cacheReadTokens/1e6*h.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 s=Je(t),n={input:e.inputTokens/1e6*s.inputCentsPerMtok,output:e.outputTokens/1e6*s.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*s.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*s.cacheReadCentsPerMtok},r=n.input+n.output+n.cacheCreate+n.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:n}}function Dt(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 Ft(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 Go(e){let t=new Map;for(let n of e){let r=n.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=n.input_tokens,o.outputTokens+=n.output_tokens,o.cacheCreateTokens+=n.cache_create_tokens,o.cacheReadTokens+=n.cache_read_tokens,o.messageCount+=n.n,t.set(r,o)}let s=[];for(let[n,r]of t.entries()){let o=Ae({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},n);s.push({model:n,modelLabel:Je(n).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return s.sort((n,r)=>r.cost.cents-n.cost.cents)}function Yo(e){let t={};for(let s of e)t[s.model??"__unknown__"]={inputTokens:s.inputTokens,outputTokens:s.outputTokens,cacheCreateTokens:s.cacheCreateTokens,cacheReadTokens:s.cacheReadTokens};return{byModel:t}}function zd(e){let t=f(),s=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1575
1575
  s.message_count,
1576
1576
  s.total_input_tokens, s.total_output_tokens,
1577
1577
  s.total_cache_create_tokens, s.total_cache_read_tokens,
@@ -1586,7 +1586,7 @@ ${o}
1586
1586
  COUNT(*) AS n
1587
1587
  FROM message_usage
1588
1588
  WHERE session_id = ?
1589
- GROUP BY model`).all(e),r=vo(n),o=s.total_input_tokens??0,a=s.total_output_tokens??0,c=s.total_cache_create_tokens??0,u=s.total_cache_read_tokens??0,d=ye({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...Io(r)},s.primary_model);return{sessionId:s.id,project:s.project,startedAt:s.started_at,endedAt:s.ended_at,messageCount:s.message_count,primaryModel:s.primary_model,primaryModelLabel:Pe(s.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:wt(d.cents),tokens:Rt(d.totalTokens),model:Pe(s.primary_model).label}}}function Od(e){let t=f(),s=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT mu.model,
1589
+ GROUP BY model`).all(e),r=Go(n),o=s.total_input_tokens??0,a=s.total_output_tokens??0,c=s.total_cache_create_tokens??0,u=s.total_cache_read_tokens??0,d=Ae({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...Yo(r)},s.primary_model);return{sessionId:s.id,project:s.project,startedAt:s.started_at,endedAt:s.ended_at,messageCount:s.message_count,primaryModel:s.primary_model,primaryModelLabel:Je(s.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:Dt(d.cents),tokens:Ft(d.totalTokens),model:Je(s.primary_model).label}}}function Kd(e){let t=f(),s=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT mu.model,
1590
1590
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1591
1591
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1592
1592
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1595,12 +1595,12 @@ ${o}
1595
1595
  FROM message_usage mu
1596
1596
  JOIN sessions s ON s.id = mu.session_id
1597
1597
  WHERE s.project_id = ?
1598
- GROUP BY mu.model`).all(s.id),r=vo(n),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1598
+ GROUP BY mu.model`).all(s.id),r=Go(n),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1599
1599
  COALESCE(SUM(total_output_tokens), 0) AS output_tokens,
1600
1600
  COALESCE(SUM(total_cache_create_tokens), 0) AS cache_create_tokens,
1601
1601
  COALESCE(SUM(total_cache_read_tokens), 0) AS cache_read_tokens,
1602
1602
  COUNT(*) AS session_count
1603
- FROM sessions WHERE project_id = ?`).get(s.id),a=ye({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...Io(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1603
+ FROM sessions WHERE project_id = ?`).get(s.id),a=Ae({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...Yo(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1604
1604
  s.total_input_tokens, s.total_output_tokens,
1605
1605
  s.total_cache_create_tokens, s.total_cache_read_tokens,
1606
1606
  s.primary_model
@@ -1611,7 +1611,7 @@ ${o}
1611
1611
  + COALESCE(s.total_output_tokens,0)
1612
1612
  + COALESCE(s.total_cache_create_tokens,0)
1613
1613
  + COALESCE(s.total_cache_read_tokens,0)) DESC
1614
- LIMIT 10`).all(s.id).map(d=>{let m=ye({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:s.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:wt(a.cents),tokens:Rt(a.totalTokens)}}}function Ld(e="all"){let t=f(),s=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,n=s?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=s?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=s?{since:s}:{},a=l=>s?t.prepare(l).get(o):t.prepare(l).get(),c=l=>s?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1614
+ LIMIT 10`).all(s.id).map(d=>{let m=Ae({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:s.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:Dt(a.cents),tokens:Ft(a.totalTokens)}}}function Vd(e="all"){let t=f(),s=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,n=s?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=s?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=s?{since:s}:{},a=l=>s?t.prepare(l).get(o):t.prepare(l).get(),c=l=>s?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1615
1615
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1616
1616
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1617
1617
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1620,7 +1620,7 @@ ${o}
1620
1620
  FROM message_usage mu
1621
1621
  JOIN sessions s ON s.id = mu.session_id
1622
1622
  ${n}
1623
- GROUP BY mu.model`),d=vo(u),m=0,h=0,b=0,T=0;for(let l of d)m+=l.inputTokens,h+=l.outputTokens,b+=l.cacheCreateTokens,T+=l.cacheReadTokens;let S=ye({inputTokens:m,outputTokens:h,cacheCreateTokens:b,cacheReadTokens:T,...Io(d)},null),w=s?a(`SELECT
1623
+ GROUP BY mu.model`),d=Go(u),m=0,h=0,b=0,T=0;for(let l of d)m+=l.inputTokens,h+=l.outputTokens,b+=l.cacheCreateTokens,T+=l.cacheReadTokens;let S=Ae({inputTokens:m,outputTokens:h,cacheCreateTokens:b,cacheReadTokens:T,...Yo(d)},null),w=s?a(`SELECT
1624
1624
  (SELECT COUNT(DISTINCT m.session_id)
1625
1625
  FROM messages m
1626
1626
  JOIN sessions s2 ON s2.id = m.session_id
@@ -1644,7 +1644,7 @@ ${o}
1644
1644
  JOIN sessions s ON s.id = mu.session_id
1645
1645
  ${n}
1646
1646
  GROUP BY day, mu.model
1647
- ORDER BY day ASC`),D=new Map;for(let l of R){if(!l.day)continue;let p=ye({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.model),g=D.get(l.day)??{tokens:0,cents:0};g.tokens+=p.totalTokens,g.cents+=p.cents,D.set(l.day,g)}let O=[...D.entries()].map(([l,p])=>({day:l,tokens:p.tokens,cents:p.cents})).sort((l,p)=>l.day.localeCompare(p.day)),M=c(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1647
+ ORDER BY day ASC`),j=new Map;for(let l of R){if(!l.day)continue;let p=Ae({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.model),g=j.get(l.day)??{tokens:0,cents:0};g.tokens+=p.totalTokens,g.cents+=p.cents,j.set(l.day,g)}let x=[...j.entries()].map(([l,p])=>({day:l,tokens:p.tokens,cents:p.cents})).sort((l,p)=>l.day.localeCompare(p.day)),D=c(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1648
1648
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1649
1649
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1650
1650
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1660,7 +1660,7 @@ ${o}
1660
1660
  + COALESCE(SUM(mu.output_tokens),0)
1661
1661
  + COALESCE(SUM(mu.cache_create_tokens),0)
1662
1662
  + COALESCE(SUM(mu.cache_read_tokens),0)) DESC
1663
- LIMIT 10`).map(l=>{let p=ye({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.primary_model);return{sessionId:l.id,project:l.project,alias:l.alias,startedAt:l.started_at,totalTokens:p.totalTokens,cost:p}}),W=c(`SELECT p.id AS project_id,
1663
+ LIMIT 10`).map(l=>{let p=Ae({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.primary_model);return{sessionId:l.id,project:l.project,alias:l.alias,startedAt:l.started_at,totalTokens:p.totalTokens,cost:p}}),H=c(`SELECT p.id AS project_id,
1664
1664
  p.name AS project,
1665
1665
  mu.model,
1666
1666
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
@@ -1672,24 +1672,24 @@ ${o}
1672
1672
  JOIN sessions s ON s.id = mu.session_id
1673
1673
  LEFT JOIN projects p ON p.id = s.project_id
1674
1674
  ${n}
1675
- GROUP BY p.id, mu.model`),oe=new Map;for(let l of W){let p=l.project_id??"__none__",g=oe.get(p);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},oe.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 $=[...oe.values()].map(l=>{let p=0,g=0,_=0,E=0;for(let A of Object.values(l.byModel))p+=A.inputTokens,g+=A.outputTokens,_+=A.cacheCreateTokens,E+=A.cacheReadTokens;let y=ye({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 se=$.slice(0,20),i=t.prepare(`SELECT
1675
+ GROUP BY p.id, mu.model`),ie=new Map;for(let l of H){let p=l.project_id??"__none__",g=ie.get(p);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},ie.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 $=[...ie.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=Ae({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 se=$.slice(0,20),i=t.prepare(`SELECT
1676
1676
  (SELECT COUNT(*) FROM messages WHERE role='assistant') AS assistant_messages,
1677
- (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:h,cacheCreateTokens:b,cacheReadTokens:T,totalTokens:S.totalTokens,cost:S,daily:O,byModel:d,topSessions:M,topRepos:se,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min(yd(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:wt(S.cents),tokens:Rt(S.totalTokens)}}}H();function as(e){return Math.max(0,Math.min(1,e))}function jo(e){let t=f(),s=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT COUNT(*) AS cnt,
1677
+ (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:h,cacheCreateTokens:b,cacheReadTokens:T,totalTokens:S.totalTokens,cost:S,daily:x,byModel:d,topSessions:D,topRepos:se,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min(Wd(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:Dt(S.cents),tokens:Ft(S.totalTokens)}}}U();function hs(e){return Math.max(0,Math.min(1,e))}function zo(e){let t=f(),s=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT COUNT(*) AS cnt,
1678
1678
  MAX(started_at) AS latest
1679
- FROM sessions WHERE project_id = ?`).get(e),r=n.cnt;if(r===0)return{projectId:e,projectName:s.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=as(r/10),a=n.latest?(Date.now()-new Date(n.latest).getTime())/(1e3*60*60*24):90,c=as(1-a/90),d=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1680
- FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,m=as((d-2)/3),h=t.prepare(`SELECT COUNT(*) AS total,
1679
+ FROM sessions WHERE project_id = ?`).get(e),r=n.cnt;if(r===0)return{projectId:e,projectName:s.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=hs(r/10),a=n.latest?(Date.now()-new Date(n.latest).getTime())/(1e3*60*60*24):90,c=hs(1-a/90),d=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1680
+ FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,m=hs((d-2)/3),h=t.prepare(`SELECT COUNT(*) AS total,
1681
1681
  SUM(CASE WHEN m.content_text IS NOT NULL AND m.content_text != '' THEN 1 ELSE 0 END) AS covered
1682
1682
  FROM messages m
1683
1683
  JOIN sessions s ON s.id = m.session_id
1684
- WHERE s.project_id = ?`).get(e),b=h.total>0?h.covered/h.total:.5,T=as(b),S=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
1684
+ WHERE s.project_id = ?`).get(e),b=h.total>0?h.covered/h.total:.5,T=hs(b),S=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
1685
1685
  COUNT(DISTINCT st.session_id) AS tagged
1686
1686
  FROM sessions s
1687
1687
  LEFT JOIN session_tags st ON st.session_id = s.id
1688
- WHERE s.project_id = ?`).get(e),w=S.total>0?S.tagged/S.total:0,R=as(w),D=Math.round((o*.2+c*.25+m*.15+T*.2+R*.2)*100);return{projectId:e,projectName:s.name,score:D,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:R,weight:.2}}}}function Cd(){let t=f().prepare("SELECT id FROM projects ORDER BY name").all(),s=[];for(let n of t){let r=jo(n.id);r&&s.push(r)}return s}H();import{execFile as tT}from"node:child_process";import{promisify as sT}from"node:util";import{stat as nT}from"node:fs/promises";var vd=sT(tT),Id=1e4,rT="%H%x09%aI%x09%s";async function oT(e){try{let{stdout:t}=await vd("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:Id});return t.trim()==="true"}catch{return!1}}async function iT(e,t,s){let n=["--no-pager","log","--all","--no-color","--since",t,"--until",s,`--pretty=format:${rT}`],{stdout:r}=await vd("git",n,{cwd:e,timeout:Id,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1689
- `)){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 aT(e){return f().prepare(`SELECT id, cwd, started_at, ended_at
1690
- FROM sessions WHERE id = ?`).get(e)??null}function cT(e,t,s){if(s.length===0)return 0;let n=f(),r=new Date().toISOString(),o=n.prepare(`INSERT OR IGNORE INTO session_commits
1688
+ WHERE s.project_id = ?`).get(e),w=S.total>0?S.tagged/S.total:0,R=hs(w),j=Math.round((o*.2+c*.25+m*.15+T*.2+R*.2)*100);return{projectId:e,projectName:s.name,score:j,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:R,weight:.2}}}}function Zd(){let t=f().prepare("SELECT id FROM projects ORDER BY name").all(),s=[];for(let n of t){let r=zo(n.id);r&&s.push(r)}return s}U();import{execFile as FT}from"node:child_process";import{promisify as PT}from"node:util";import{stat as UT}from"node:fs/promises";var Qd=PT(FT),ep=1e4,$T="%H%x09%aI%x09%s";async function HT(e){try{let{stdout:t}=await Qd("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:ep});return t.trim()==="true"}catch{return!1}}async function BT(e,t,s){let n=["--no-pager","log","--all","--no-color","--since",t,"--until",s,`--pretty=format:${$T}`],{stdout:r}=await Qd("git",n,{cwd:e,timeout:ep,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1689
+ `)){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 WT(e){return f().prepare(`SELECT id, cwd, started_at, ended_at
1690
+ FROM sessions WHERE id = ?`).get(e)??null}function qT(e,t,s){if(s.length===0)return 0;let n=f(),r=new Date().toISOString(),o=n.prepare(`INSERT OR IGNORE INTO session_commits
1691
1691
  (session_id, commit_sha, committed_at, subject, cwd_snapshot, correlated_at)
1692
- VALUES (?, ?, ?, ?, ?, ?)`),a=0;return n.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(s),a}async function Mo(e){let t=aT(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 s=t.started_at,n=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await nT(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 oT(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await iT(t.cwd,s,n),a=cT(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 Ln(e){let t=f(),s=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(s))return[];let n=`${s.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1692
+ VALUES (?, ?, ?, ?, ?, ?)`),a=0;return n.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(s),a}async function Ko(e){let t=WT(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 s=t.started_at,n=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await UT(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 HT(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await BT(t.cwd,s,n),a=qT(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 Bn(e){let t=f(),s=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(s))return[];let n=`${s.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1693
1693
  NULLIF(sa.alias, '') AS alias,
1694
1694
  p.name AS project,
1695
1695
  s.started_at AS startedAt,
@@ -1703,20 +1703,20 @@ ${o}
1703
1703
  LEFT JOIN session_aliases sa ON sa.session_id = sc.session_id
1704
1704
  WHERE lower(sc.commit_sha) = lower(?)
1705
1705
  OR lower(sc.commit_sha) LIKE ?
1706
- ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(s,n)}function Do(e){return f().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1706
+ ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(s,n)}function Vo(e){return f().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1707
1707
  FROM session_commits
1708
1708
  WHERE session_id = ?
1709
- ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var lT=3e4;function jd(e){try{let s=f().prepare(`SELECT MAX(correlated_at) AS last_at
1710
- FROM session_commits WHERE session_id = ?`).get(e),n=s?.last_at?Date.parse(s.last_at):0;if(n&&Date.now()-n<lT)return}catch{}Mo(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}H();import{execFile as uT}from"node:child_process";import{promisify as dT}from"node:util";import{stat as pT}from"node:fs/promises";var mT=dT(uT),gT=60,_T=7,fT=7,hT=5e3;function ET(){let e=f(),t=s=>{try{return!!e.prepare(s).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1709
+ ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var XT=3e4;function tp(e){try{let s=f().prepare(`SELECT MAX(correlated_at) AS last_at
1710
+ FROM session_commits WHERE session_id = ?`).get(e),n=s?.last_at?Date.parse(s.last_at):0;if(n&&Date.now()-n<XT)return}catch{}Ko(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}U();import{execFile as JT}from"node:child_process";import{promisify as GT}from"node:util";import{stat as YT}from"node:fs/promises";var zT=GT(JT),KT=60,VT=7,ZT=7,QT=5e3;function ey(){let e=f(),t=s=>{try{return!!e.prepare(s).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1711
1711
  WHERE (COALESCE(total_input_tokens,0)
1712
1712
  + COALESCE(total_output_tokens,0)
1713
1713
  + COALESCE(total_cache_create_tokens,0)
1714
1714
  + COALESCE(total_cache_read_tokens,0)) > 0
1715
- LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function Fo(e){if(!e)return[];let t=new Set,s=[];for(let n of e.split(",")){let r=n.trim().toLowerCase();!r||t.has(r)||(t.add(r),s.push(r))}return s}function Md(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 bT(){let e=f(),t=e.prepare(`SELECT ss.keywords
1715
+ LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function Zo(e){if(!e)return[];let t=new Set,s=[];for(let n of e.split(",")){let r=n.trim().toLowerCase();!r||t.has(r)||(t.add(r),s.push(r))}return s}function sp(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 ty(){let e=f(),t=e.prepare(`SELECT ss.keywords
1716
1716
  FROM session_semantic ss
1717
1717
  JOIN sessions s ON s.id = ss.session_id
1718
1718
  WHERE s.started_at IS NOT NULL
1719
- AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:_T});if(t.length===0)return null;let s=new Set;for(let o of t)for(let a of Fo(o.keywords))s.add(a);if(s.size===0)return null;let n=e.prepare(`SELECT ss.session_id AS session_id,
1719
+ AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:VT});if(t.length===0)return null;let s=new Set;for(let o of t)for(let a of Zo(o.keywords))s.add(a);if(s.size===0)return null;let n=e.prepare(`SELECT ss.session_id AS session_id,
1720
1720
  ss.summary AS summary,
1721
1721
  ss.keywords AS keywords,
1722
1722
  p.name AS project,
@@ -1732,7 +1732,7 @@ ${o}
1732
1732
  WHERE s.started_at IS NOT NULL
1733
1733
  AND s.message_count > 2
1734
1734
  AND julianday('now') - julianday(s.started_at) >= @ageDays
1735
- ORDER BY s.started_at ASC`).all({ageDays:gT});if(n.length===0)return null;let r=null;for(let o of n){let c=Fo(o.keywords).filter(u=>s.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{...Md(r.row),summary:r.row.summary,keywords:Fo(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function ST(){let t=f().prepare(`SELECT s.id AS session_id,
1735
+ ORDER BY s.started_at ASC`).all({ageDays:KT});if(n.length===0)return null;let r=null;for(let o of n){let c=Zo(o.keywords).filter(u=>s.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{...sp(r.row),summary:r.row.summary,keywords:Zo(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function sy(){let t=f().prepare(`SELECT s.id AS session_id,
1736
1736
  p.name AS project,
1737
1737
  NULLIF(sa.alias, '') AS alias,
1738
1738
  s.started_at AS started_at,
@@ -1751,28 +1751,28 @@ ${o}
1751
1751
  AND (COALESCE(s.total_input_tokens, 0)
1752
1752
  + COALESCE(s.total_output_tokens, 0)
1753
1753
  + COALESCE(s.total_cache_create_tokens, 0)
1754
- + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:fT});if(t.length===0)return null;let s=null;for(let n of t){let r=ye({inputTokens:n.input_tokens,outputTokens:n.output_tokens,cacheCreateTokens:n.cache_create_tokens,cacheReadTokens:n.cache_read_tokens},n.primary_model);r.cents<=0||(!s||r.cents>s.cents)&&(s={row:n,cents:r.cents,totalTokens:r.totalTokens})}return s?{...Md(s.row),totalTokens:s.totalTokens,costCents:s.cents,costDisplay:wt(s.cents),tokensDisplay:Rt(s.totalTokens),primaryModel:s.row.primary_model,primaryModelLabel:Pe(s.row.primary_model).label}:null}async function TT(e){try{if(!(await pT(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await mT("git",["rev-parse","HEAD"],{cwd:e,timeout:hT}),s=t.trim();return/^[0-9a-f]{40}$/.test(s)?s:null}catch{return null}}async function yT(){let e=f(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1754
+ + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:ZT});if(t.length===0)return null;let s=null;for(let n of t){let r=Ae({inputTokens:n.input_tokens,outputTokens:n.output_tokens,cacheCreateTokens:n.cache_create_tokens,cacheReadTokens:n.cache_read_tokens},n.primary_model);r.cents<=0||(!s||r.cents>s.cents)&&(s={row:n,cents:r.cents,totalTokens:r.totalTokens})}return s?{...sp(s.row),totalTokens:s.totalTokens,costCents:s.cents,costDisplay:Dt(s.cents),tokensDisplay:Ft(s.totalTokens),primaryModel:s.row.primary_model,primaryModelLabel:Je(s.row.primary_model).label}:null}async function ny(e){try{if(!(await YT(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await zT("git",["rev-parse","HEAD"],{cwd:e,timeout:QT}),s=t.trim();return/^[0-9a-f]{40}$/.test(s)?s:null}catch{return null}}async function ry(){let e=f(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1755
1755
  FROM sessions s
1756
1756
  WHERE s.cwd IS NOT NULL AND s.started_at IS NOT NULL
1757
1757
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1758
- LIMIT 1`).get();if(!t?.cwd)return null;let s=await TT(t.cwd);if(!s)return null;let n=Ln(s);if(n.length===0)return null;let r=n[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1758
+ LIMIT 1`).get();if(!t?.cwd)return null;let s=await ny(t.cwd);if(!s)return null;let n=Bn(s);if(n.length===0)return null;let r=n[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1759
1759
  FROM sessions s
1760
- 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 Dd(){let e=ET(),t=e.semantic?Promise.resolve().then(()=>{try{return bT()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),s=e.cost?Promise.resolve().then(()=>{try{return ST()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),n=e.git?yT().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,s,n]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}ze();H();ze();async function Fd(e,t=50){let s=await qn(e),n=f(),r=Buffer.from(s.buffer,s.byteOffset,s.byteLength);return n.prepare(`SELECT v.rowid, v.distance, cm.session_id, cm.text, cm.message_uuids
1760
+ 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 np(){let e=ey(),t=e.semantic?Promise.resolve().then(()=>{try{return ty()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),s=e.cost?Promise.resolve().then(()=>{try{return sy()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),n=e.git?ry().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,s,n]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}Ve();U();Ve();async function rp(e,t=50){let s=await or(e),n=f(),r=Buffer.from(s.buffer,s.byteOffset,s.byteLength);return n.prepare(`SELECT v.rowid, v.distance, cm.session_id, cm.text, cm.message_uuids
1761
1761
  FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
1762
- WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(r,t).map(a=>({sessionId:a.session_id,chunkRowid:a.rowid,distance:a.distance,text:a.text,messageUuids:JSON.parse(a.message_uuids)}))}async function Pd(e,t=10,s=.65){let n=f(),r=n.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=n.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=n.prepare(`SELECT v.rowid, v.distance, cm.session_id FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
1763
- WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(o.embedding,t*5),c=new Map;for(let d of a){if(d.session_id===e)continue;let m=c.get(d.session_id);(m===void 0||d.distance<m)&&c.set(d.session_id,d.distance)}let u=[];for(let[d,m]of c){let h=1-m;h>=s&&u.push({sessionId:d,similarity:h})}return u.sort((d,m)=>m.similarity-d.similarity),u.slice(0,t)}function wT(){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 Ud(e){let t=wT(),s=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=s.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):s.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let n=[];for(let[r,o]of s)n.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return n.sort((r,o)=>o.score-r.score),n}H();ze();H();function $d(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function RT(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=$d(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",h=$d(d.content_text??"");return`[${m}] ${h}`}).join(`
1762
+ WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(r,t).map(a=>({sessionId:a.session_id,chunkRowid:a.rowid,distance:a.distance,text:a.text,messageUuids:JSON.parse(a.message_uuids)}))}async function op(e,t=10,s=.65){let n=f(),r=n.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=n.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=n.prepare(`SELECT v.rowid, v.distance, cm.session_id FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
1763
+ WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(o.embedding,t*5),c=new Map;for(let d of a){if(d.session_id===e)continue;let m=c.get(d.session_id);(m===void 0||d.distance<m)&&c.set(d.session_id,d.distance)}let u=[];for(let[d,m]of c){let h=1-m;h>=s&&u.push({sessionId:d,similarity:h})}return u.sort((d,m)=>m.similarity-d.similarity),u.slice(0,t)}function oy(){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 ip(e){let t=oy(),s=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=s.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):s.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let n=[];for(let[r,o]of s)n.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return n.sort((r,o)=>o.score-r.score),n}U();Ve();U();function ap(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function iy(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=ap(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",h=ap(d.content_text??"");return`[${m}] ${h}`}).join(`
1764
1764
 
1765
- `);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 Bd(e){let s=f().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 RT(s)}var Hd=2e3,AT=1e4,Cn=null,Po=!1,Wd=null;function kT(){return f().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function xT(){let e=f(),t=e.prepare("SELECT DISTINCT session_id FROM chunk_queue ORDER BY id LIMIT 1").get();if(!t)return!1;let s=t.session_id;try{e.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(s),e.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(s);let n=Bd(s);if(n.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s),!0;let r=n.map(d=>d.text),o=await Ct(r),a=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, 'bge-base-en-v1.5', 768, 0, datetime('now'))"),c=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)");e.transaction(()=>{for(let d=0;d<n.length;d++){let m=a.run(s,JSON.stringify(n[d].messageUuids),n[d].text),h=Buffer.from(o[d].buffer,o[d].byteOffset,o[d].byteLength);c.run(BigInt(m.lastInsertRowid),h)}e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s)})(),Wd=new Date().toISOString()}catch(n){console.error("[vector-worker] failed for session",s,n),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s)}return!0}async function qd(){if(!Ne().loaded)return;let e=await xT();Cn!==null&&clearTimeout(Cn),Cn=setTimeout(()=>{qd()},e?Hd:AT)}function Xd(){if(!Po){if(!Ne().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}Po=!0,Cn=setTimeout(()=>{qd()},Hd)}}function Jd(){return{running:Po,queueDepth:kT(),lastProcessedAt:Wd}}Q();import{existsSync as zd,mkdirSync as Yd,rmSync as Gd,createWriteStream as NT,statSync as OT}from"node:fs";import{join as vn}from"node:path";import{createHash as LT}from"node:crypto";import{readFile as CT}from"node:fs/promises";var vT="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",Kd=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function Vd(){return vn(U,"models","BAAI","bge-base-en-v1.5")}function Uo(){let e=Vd();return Kd.every(t=>zd(vn(e,t.path)))}async function Zd(e){let t=Vd();Yd(t,{recursive:!0}),Yd(vn(t,"onnx"),{recursive:!0});for(let s of Kd){let n=vn(t,s.path),r=vT+s.path,o=0;zd(n)&&(o=OT(n).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 ${s.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 ${s.path}`);let h=NT(n,{flags:o>0?"a":"w"}),b=m.getReader(),T=o;for(;;){let{done:R,value:D}=await b.read();if(R)break;h.write(Buffer.from(D)),T+=D.byteLength,e?.(s.path,T,d)}if(h.end(),await new Promise((R,D)=>{h.on("finish",R),h.on("error",D)}),s.sha256==="TODO_PLACEHOLDER")throw Gd(n),new Error(`Refusing to install: SHA-256 not pinned for ${s.path}. Update model-download.ts.`);let S=await CT(n);if(LT("sha256").update(S).digest("hex")!==s.sha256)throw Gd(n),new Error(`SHA-256 mismatch for ${s.path}`)}}H();var IT=[/\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],jT=1440*60*1e3;function MT(e){let t=f(),s=t.prepare(`SELECT content_text, tool_names FROM messages
1765
+ `);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 cp(e){let s=f().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 iy(s)}var mp=2e3,ay=1e4,at=null,Wn=!1,gp=null,Qo=0;function _p(e){Qo=Math.max(0,Math.floor(e))}var Ie=new Set,fp=-1;function hp(e){Ie.add(e)}function Ep(){Ie.add(fp)}function lp(e){if(Ie.has(fp))return!0;if(Ie.size===0)return!1;let t=f().prepare("SELECT project_id FROM sessions WHERE id = ?").get(e);return t?Ie.has(t.project_id):!1}var up=10,cy=200,ly=2e3;function uy(){let e=Number(process.env.RECALL_VECTOR_HARD_CAP??"");return Number.isFinite(e)&&e>0?Math.min(ly,Math.floor(e)):cy}var dy=3,dp=1e3,Es=new Map,ei=new Set;function pp(e,t,s,n){if(e.size>=dp&&(e instanceof Map,!e.has(t))){let r=e.keys().next();r.done||(e.delete(r.value),console.warn(`[vector-worker] ${n??"tracking-map"} hit ${dp}-entry cap; evicted oldest entry`))}e instanceof Map?e.set(t,s):e.add(t)}var bs=[],py=50;function ti(){if(bs.length<8)return null;let e=bs.slice(-30),t=e.reduce((n,r)=>n+r.chunks,0),s=e.reduce((n,r)=>n+r.durationMs,0);return s<=0||t===0?null:{samples:e.length,chunksPerSec:t/(s/1e3),sessionsPerSec:e.length/(s/1e3),avgChunksPerSession:t/e.length}}function my(){return f().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function gy(){let e=f(),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(lp(t.session_id)||ei.has(t.session_id))return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(t.session_id),!0;let s=t.session_id,n=Date.now();try{e.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(s),e.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(s);let r=cp(s),o=Qo>0?Qo:1/0,a=Math.min(o,uy()),c=a<r.length?r.slice(0,a):r;if(c.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s),!0;let u=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, 'bge-base-en-v1.5', 768, 0, datetime('now'))"),d=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)"),m=0,h=!1;for(let b=0;b<c.length;b+=up){if(lp(s)){h=!0;break}let T=Math.min(c.length,b+up),S=c.slice(b,T),w=S.map(x=>x.text),R=await ct(w);e.transaction(()=>{for(let x=0;x<S.length;x++){let q=u.run(s,JSON.stringify(S[x].messageUuids),S[x].text),D=Buffer.from(R[x].buffer,R[x].byteOffset,R[x].byteLength);d.run(BigInt(q.lastInsertRowid),D)}})(),m+=S.length,await new Promise(x=>setImmediate(x))}h&&(e.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(s),e.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(s)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s),gp=new Date().toISOString(),bs.push({ts:Date.now(),chunks:m,durationMs:Date.now()-n}),bs.length>py&&bs.shift(),Es.has(s)&&Es.delete(s)}catch(r){console.error("[vector-worker] failed for session",s,r);let o=(Es.get(s)??0)+1;pp(Es,s,o,"failureCounts"),o>=dy&&(pp(ei,s,void 0,"blacklist"),Es.delete(s),console.error(`[vector-worker] blacklisted session ${s} after ${o} failures \u2014 will not retry until daemon restart`)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s)}finally{if(Ie.size>0)try{let r=e.prepare("SELECT project_id FROM sessions WHERE id = ?").get(s);r!==void 0&&Ie.has(r.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(r.project_id)||Ie.delete(r.project_id))}catch(r){console.error("[vector-worker] cancelledProjects cleanup failed:",r)}}return!0}async function bp(){if(!_e().loaded)return;let e=await gy();at!==null&&clearTimeout(at),at=setTimeout(()=>{bp()},e?mp:ay)}function Ss(){if(!Wn){if(!_e().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}Wn=!0,at=setTimeout(()=>{bp()},mp)}}function Sp(){Wn=!1,at!==null&&(clearTimeout(at),at=null)}function ve(){return{running:Wn,queueDepth:my(),lastProcessedAt:gp,blacklistedCount:ei.size}}ee();import{existsSync as wp,mkdirSync as Tp,rmSync as yp,createWriteStream as _y,statSync as fy}from"node:fs";import{join as qn}from"node:path";import{createHash as hy}from"node:crypto";import{readFile as Ey}from"node:fs/promises";var by="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",Rp=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function kp(){return qn(B,"models","BAAI","bge-base-en-v1.5")}function Ge(){let e=kp();return Rp.every(t=>wp(qn(e,t.path)))}async function Ap(e){let t=kp();Tp(t,{recursive:!0}),Tp(qn(t,"onnx"),{recursive:!0});for(let s of Rp){let n=qn(t,s.path),r=by+s.path,o=0;wp(n)&&(o=fy(n).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 ${s.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 ${s.path}`);let h=_y(n,{flags:o>0?"a":"w"}),b=m.getReader(),T=o;for(;;){let{done:R,value:j}=await b.read();if(R)break;h.write(Buffer.from(j)),T+=j.byteLength,e?.(s.path,T,d)}if(h.end(),await new Promise((R,j)=>{h.on("finish",R),h.on("error",j)}),s.sha256==="TODO_PLACEHOLDER")throw yp(n),new Error(`Refusing to install: SHA-256 not pinned for ${s.path}. Update model-download.ts.`);let S=await Ey(n);if(hy("sha256").update(S).digest("hex")!==s.sha256)throw yp(n),new Error(`SHA-256 mismatch for ${s.path}`)}}U();var Sy=[/\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],Ty=1440*60*1e3;function yy(e){let t=f(),s=t.prepare(`SELECT content_text, tool_names FROM messages
1766
1766
  WHERE session_id = ? AND role = 'assistant'
1767
- ORDER BY timestamp DESC LIMIT 5`).all(e),n=!1;for(let u of s)if(u.content_text&&IT.some(d=>d.test(u.content_text))){n=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1768
- 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 n?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:n}:{status:"neutral",evidence:o,claimFound:n}}function DT(e){let t=MT(e);return f().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function Qd(e){let s=f().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(s?.verification_status&&s.verification_computed_at&&Date.now()-s.verification_computed_at<jT){let n={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:s.verification_status,evidence:n,claimFound:s.verification_status!=="neutral"}}return DT(e)}import{readFileSync as FT,writeFileSync as PT,mkdirSync as UT,chmodSync as $T}from"node:fs";import{join as ep}from"node:path";import{homedir as tp}from"node:os";function sp(){return ep(tp(),".recall","config.json")}function np(){try{return JSON.parse(FT(sp(),"utf-8"))}catch{return{}}}function BT(e){let t=sp();UT(ep(tp(),".recall"),{recursive:!0}),PT(t,JSON.stringify(e,null,2)+`
1769
- `,"utf-8"),$T(t,384)}function $o(){let t=np().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function rp(e){let t=np();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},BT(t)}var KT=5e3,Go={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},Bo=0,Ho=Go,Wo=null;async function VT(){return Date.now()-Bo>=KT&&!Wo&&(Wo=cn().then(s=>(Ho=s,Bo=Date.now(),s)).catch(()=>(Bo=Date.now(),Ho=Go,Go)).finally(()=>{Wo=null})),Ho}var ZT=2e3,QT=6,jn=new Map;function cs(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function ey(e,t){let s=Date.now(),n=(jn.get(e)??[]).filter(a=>s-a.ts<ZT);return jn.set(e,n),n.length<2||n[n.length-1].name===t?!1:n.slice(0,-1).some(a=>a.name===t)}function ty(e,t){let s=jn.get(e)??[];for(s.push({name:t,ts:Date.now()});s.length>QT;)s.shift();jn.set(e,s)}function ip(e,t){let s=t.trim();if(!s)return 0;if(ie(s)){let o=lt(s);if(!o)return 0;s=o}if(ce(s))return 0;if(ey(e,s))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${s}", flap signature (competing editor sync sources)`),0;ty(e,s);let n=v.sessionsFor(e),r=0;for(let o of n)try{if(Ae(o)===s)continue;be(o,s),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${s}" propagated to ${r} session(s)`),r}var ap=(()=>{try{let e=Mn(Ko(Vo(import.meta.url)),"..","..","package.json");return JSON.parse(Yo(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),qo=!1,Xo=!1,Jo=!1,ny=Ko(Vo(import.meta.url)),zo=Mn(ny,"..","web"),up=Mn(zo,"index.html"),ry=qT(up);function cp(){return f().prepare(`SELECT
1767
+ ORDER BY timestamp DESC LIMIT 5`).all(e),n=!1;for(let u of s)if(u.content_text&&Sy.some(d=>d.test(u.content_text))){n=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1768
+ 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 n?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:n}:{status:"neutral",evidence:o,claimFound:n}}function wy(e){let t=yy(e);return f().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function xp(e){let s=f().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(s?.verification_status&&s.verification_computed_at&&Date.now()-s.verification_computed_at<Ty){let n={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:s.verification_status,evidence:n,claimFound:s.verification_status!=="neutral"}}return wy(e)}import{readFileSync as Ry,writeFileSync as ky,mkdirSync as Ay,chmodSync as xy}from"node:fs";import{join as Np}from"node:path";import{homedir as Op}from"node:os";function Lp(){return Np(Op(),".recall","config.json")}function Cp(){try{return JSON.parse(Ry(Lp(),"utf-8"))}catch{return{}}}function Ny(e){let t=Lp();Ay(Np(Op(),".recall"),{recursive:!0}),ky(t,JSON.stringify(e,null,2)+`
1769
+ `,"utf-8"),xy(t,384)}function si(){let t=Cp().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function Ip(e){let t=Cp();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},Ny(t)}var Fy=5e3,ui={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},ni=0,ri=ui,oi=null;async function Py(){return Date.now()-ni>=Fy&&!oi&&(oi=Sn().then(s=>(ri=s,ni=Date.now(),s)).catch(()=>(ni=Date.now(),ri=ui,ui)).finally(()=>{oi=null})),ri}var Uy=2e3,$y=6,Gn=new Map;function Ts(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function Hy(e,t){let s=Date.now(),n=(Gn.get(e)??[]).filter(a=>s-a.ts<Uy);return Gn.set(e,n),n.length<2||n[n.length-1].name===t?!1:n.slice(0,-1).some(a=>a.name===t)}function By(e,t){let s=Gn.get(e)??[];for(s.push({name:t,ts:Date.now()});s.length>$y;)s.shift();Gn.set(e,s)}function jp(e,t){let s=t.trim();if(!s)return 0;if(ce(s)){let o=yt(s);if(!o)return 0;s=o}if(de(s))return 0;if(Hy(e,s))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${s}", flap signature (competing editor sync sources)`),0;By(e,s);let n=v.sessionsFor(e),r=0;for(let o of n)try{if(Te(o)===s)continue;he(o,s),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${s}" propagated to ${r} session(s)`),r}var Mp=(()=>{try{let e=Yn(pi(mi(import.meta.url)),"..","..","package.json");return JSON.parse(li(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),ii=!1,ai=!1,ci=!1,qy=pi(mi(import.meta.url)),di=Yn(qy,"..","web"),Pp=Yn(di,"index.html"),Xy=Cy(Pp);function Dp(){return f().prepare(`SELECT
1770
1770
  (SELECT COUNT(*) FROM projects) AS projects,
1771
1771
  (SELECT COUNT(*) FROM sessions) AS sessions,
1772
1772
  (SELECT COUNT(*) FROM messages) AS messages,
1773
1773
  (SELECT MIN(started_at) FROM sessions WHERE started_at IS NOT NULL) AS earliest,
1774
- (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var oy=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,iy=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function ls(e,t){if((await xt()).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 In=new Map,dp=0,lp=0,pp=null,ay=6e4;function cy(){dp+=1;let e=Date.now();e-lp<ay||(lp=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 ly(){pp=new Date().toISOString()}function uy(){let e=Ba();return{silentTerminalRejections:dp,lastTerminalSyncAt:pp,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns}}}function dy(e){let t=new HT;if(t.use("*",sy({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let p=i.req.raw.headers.get("host")??"";if(!oy.test(p))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!iy.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")??"",_=!1;if(g.length===e.length)try{_=GT(Buffer.from(g,"utf8"),i)}catch{_=!1}return _?p():(l.req.path.startsWith("/api/terminal/")&&cy(),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:ap,uptimeSeconds:Math.round(process.uptime()),pipeline:uy()})),t.get("/api/stats",i=>i.json(cp())),t.get("/api/stats/session/:id",i=>{let l=Nd(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=Od(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(Ld(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 _=Ad({limit:p});return i.json({mode:"background",started:_,alreadyRunning:!_&&kd(),limit:p,lastRun:Co()})}let g=Rd({limit:p});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:p,result:g,lastRun:Co()})}),t.get("/api/stats/health",i=>i.json(Cd())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),p=jo(l);return p?i.json(p):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:$o()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&rp(l.enabled),i.json({enabled:$o()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),p=Qd(l);return i.json(p)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=f().prepare(`SELECT tool_names, raw_json FROM messages
1775
- 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 A=y.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(A)for(let k of A){let x=k.match(/":\s*"([^"]+)"/);x&&E.add(x[1])}}return i.json({filesReferenced:E.size,toolCallCount:_})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),p=Do(l);if(p.length>0||i.req.query("refresh")!=="1")return i.json({commits:p});let g=await Mo(l);return i.json({commits:Do(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:Ln(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await xt();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 xt(),E=kt(),y=g&&_.tier==="pro"&&E?E.license_jwt:null,A=(()=>{try{let N=Mn(Ko(Vo(import.meta.url)),"..","..","package.json");return JSON.parse(Yo(N,"utf8")).version}catch{return"unknown"}})(),k=l,x={score:k.score,comment:k.comment??null,surface:"web",version:typeof k.version=="string"?k.version:A,os:typeof k.os=="string"?k.os:process.platform,trigger_kind:typeof k.trigger_kind=="string"?k.trigger_kind:"manual",license_jwt:y};try{let N=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(x)}),I=await N.json().catch(()=>({}));return i.json(I,N.status)}catch(N){let I=N instanceof Error?N.message:"network error";return i.json({error:"upstream_unreachable",detail:I},502)}}),t.get("/api/discover/today",ls,async i=>{try{return i.json(await Dd())}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:ro(),orphan_projects:eu()})),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=cu({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=lu(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=oo(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)?(uu(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let s=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=s.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=tu({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 n=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=n.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{let _=su(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)?(nu(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 ru(l,g.data.project_id),i.json({macro_repo:pt(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):(ou(l,p),i.json({macro_repo:pt(l)}))}),t.get("/api/projects",i=>{let l=f(),g=i.req.query("system")==="1"||i.req.query("system")==="true"?"":" 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]%'",_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1774
+ (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var Jy=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,Gy=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function je(e,t){if((await Ke()).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 Xn=new Map,Up=0,Fp=0,$p=null,Yy=6e4;function zy(){Up+=1;let e=Date.now();e-Fp<Yy||(Fp=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 Ky(){$p=new Date().toISOString()}function Vy(){let e=ac();return{silentTerminalRejections:Up,lastTerminalSyncAt:$p,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns}}}function Jn(e,t){if(t)return"";let s=e;return` 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]%' AND COALESCE(${s}.auto_title, '') NOT LIKE 'You are summarizing a Claude Code session%' AND COALESCE(${s}.auto_title, '') NOT LIKE 'You are extracting a structured Output Index%' AND COALESCE(${s}.title_quality, '') != 'programmatic'`}function Zy(e){let t=new Oy;if(t.use("*",Wy({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let p=i.req.raw.headers.get("host")??"";if(!Jy.test(p))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!Gy.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{_=My(Buffer.from(g,"utf8"),i)}catch{_=!1}return _?p():(l.req.path.startsWith("/api/terminal/")&&zy(),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:Mp,uptimeSeconds:Math.round(process.uptime()),pipeline:Vy()})),t.get("/api/stats",i=>i.json(Dp())),t.get("/api/stats/session/:id",i=>{let l=zd(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=Kd(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(Vd(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 _=Jd({limit:p});return i.json({mode:"background",started:_,alreadyRunning:!_&&Gd(),limit:p,lastRun:Jo()})}let g=Xd({limit:p});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:p,result:g,lastRun:Jo()})}),t.get("/api/stats/health",i=>i.json(Zd())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),p=zo(l);return p?i.json(p):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:si()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&Ip(l.enabled),i.json({enabled:si()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),p=xp(l);return i.json(p)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=f().prepare(`SELECT tool_names, raw_json FROM messages
1775
+ 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 N of k){let A=N.match(/":\s*"([^"]+)"/);A&&E.add(A[1])}}return i.json({filesReferenced:E.size,toolCallCount:_})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),p=Vo(l);if(p.length>0||i.req.query("refresh")!=="1")return i.json({commits:p});let g=await Ko(l);return i.json({commits:Vo(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:Bn(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await Ke();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 Ke(),E=Pt(),y=g&&_.tier==="pro"&&E?E.license_jwt:null,k=(()=>{try{let L=Yn(pi(mi(import.meta.url)),"..","..","package.json");return JSON.parse(li(L,"utf8")).version}catch{return"unknown"}})(),N=l,A={score:N.score,comment:N.comment??null,surface:"web",version:typeof N.version=="string"?N.version:k,os:typeof N.os=="string"?N.os:process.platform,trigger_kind:typeof N.trigger_kind=="string"?N.trigger_kind:"manual",license_jwt:y};try{let L=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(A)}),C=await L.json().catch(()=>({}));return i.json(C,L.status)}catch(L){let C=L instanceof Error?L.message:"network error";return i.json({error:"upstream_unreachable",detail:C},502)}}),t.get("/api/discover/today",je,async i=>{try{return i.json(await np())}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:So(),orphan_projects:Tu()})),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=Ou({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=Lu(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=To(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)?(Cu(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let s=M.object({name:M.string().min(1).max(100),description:M.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),p=s.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=yu({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 n=M.object({name:M.string().min(1).max(100).optional(),description:M.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=n.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{let _=wu(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)?(Ru(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=M.object({project_id:M.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 ku(l,g.data.project_id),i.json({macro_repo:kt(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):(Au(l,p),i.json({macro_repo:kt(l)}))}),t.get("/api/projects",i=>{let l=f(),p=i.req.query("system")==="1"||i.req.query("system")==="true",g=Jn("s",p),_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1776
1776
  COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count,
1777
1777
  COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count,
1778
1778
  MAX(COALESCE(s.ended_at, s.started_at)) AS latest
@@ -1780,7 +1780,7 @@ ${o}
1780
1780
  LEFT JOIN sessions s ON s.project_id = p.id
1781
1781
  GROUP BY p.id
1782
1782
  ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all();return i.json(_)}),t.get("/api/graph/:project",i=>{let l=f(),p=i.req.param("project"),g=l.prepare(`SELECT id, name, decoded_path FROM projects
1783
- WHERE name = ? LIMIT 1`).get(p);if(!g)return i.json({error:`project "${p}" not found`},404);let _=l.prepare(`SELECT s.id,
1783
+ 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,
1784
1784
  s.auto_title,
1785
1785
  s.auto_title_source,
1786
1786
  s.first_user_message,
@@ -1790,12 +1790,12 @@ ${o}
1790
1790
  sa.alias AS alias
1791
1791
  FROM sessions s
1792
1792
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1793
- WHERE s.project_id = ?
1794
- ORDER BY s.started_at`).all(g.id),E=_.map(k=>k.id),y=[];if(E.length>0){let k=E.map(()=>"?").join(",");y=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1793
+ WHERE s.project_id = ?${Jn("s",_)}
1794
+ ORDER BY s.started_at`).all(g.id),y=E.map(A=>A.id),k=[];if(y.length>0){let A=y.map(()=>"?").join(",");k=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1795
1795
  FROM thread_edges
1796
- WHERE session_id IN (${k})
1796
+ WHERE session_id IN (${A})
1797
1797
  AND (parent_session_id IS NULL
1798
- OR parent_session_id IN (${k}))`).all(...E,...E)}let A=_.map(k=>{let x=k.alias??k.auto_title??k.first_user_message??k.id.slice(0,8),N=null,I=null;if(k.auto_title?.startsWith("/")){let C=k.auto_title.split(" \xB7 ");N=C[0],I=C.length>1?C.slice(1).join(" \xB7 "):null}return{id:k.id.slice(0,8),full_id:k.id,title:x,alias:k.alias,auto_title:k.auto_title,auto_title_source:k.auto_title_source,title_quality:k.title_quality,started_at:k.started_at,msgs:k.message_count,skill:N,brand:I}});return i.json({project:g,sessions:A,thread_edges:y})});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 A=_==="1"||_==="true",k=E?Number(E):void 0;if(k!==void 0&&(!Number.isFinite(k)||k<1))return i.json({error:"invalid limit"},400);try{let x=hs({sourceSessionId:l,targetSessionId:p,linkType:y,approvedOnly:A,limit:k});return i.json({links:x})}catch(x){return i.json({error:x.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 A;if(_){if(!c.has(_))return i.json({error:`invalid inferred_by: ${_}`},400);A=_}let k=E?Number(E):void 0;if(k!==void 0&&(!Number.isFinite(k)||k<1))return i.json({error:"invalid limit"},400);try{let x=Ke({status:y,sourceSessionId:p,targetSessionId:g,inferredBy:A,limit:k}),N=new Set;for(let L of x)N.add(L.source_session_id),N.add(L.target_session_id);let I=new Map;if(N.size>0){let L=Array.from(N),F=L.map(()=>"?").join(","),ee=f().prepare(`SELECT s.id,
1798
+ OR parent_session_id IN (${A}))`).all(...y,...y)}let N=E.map(A=>{let L=A.alias??A.auto_title??A.first_user_message??A.id.slice(0,8),C=null,I=null;if(A.auto_title?.startsWith("/")){let O=A.auto_title.split(" \xB7 ");C=O[0],I=O.length>1?O.slice(1).join(" \xB7 "):null}return{id:A.id.slice(0,8),full_id:A.id,title:L,alias:A.alias,auto_title:A.auto_title,auto_title_source:A.auto_title_source,title_quality:A.title_quality,started_at:A.started_at,msgs:A.message_count,skill:C,brand:I}});return i.json({project:g,sessions:N,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",N=E?Number(E):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<1))return i.json({error:"invalid limit"},400);try{let A=Os({sourceSessionId:l,targetSessionId:p,linkType:y,approvedOnly:k,limit:N});return i.json({links:A})}catch(A){return i.json({error:A.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 N=E?Number(E):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<1))return i.json({error:"invalid limit"},400);try{let A=lt({status:y,sourceSessionId:p,targetSessionId:g,inferredBy:k,limit:N}),L=new Set;for(let O of A)L.add(O.source_session_id),L.add(O.target_session_id);let C=new Map;if(L.size>0){let O=Array.from(L),F=O.map(()=>"?").join(","),K=f().prepare(`SELECT s.id,
1799
1799
  NULLIF(sa.alias, '') AS alias,
1800
1800
  s.auto_title,
1801
1801
  s.first_user_message,
@@ -1803,7 +1803,7 @@ ${o}
1803
1803
  FROM sessions s
1804
1804
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1805
1805
  LEFT JOIN projects p ON p.id = s.project_id
1806
- WHERE s.id IN (${F})`).all(...L);for(let Z of ee){let q=Z.first_user_message?Z.first_user_message.slice(0,80):null,G=Z.alias??Z.auto_title??q??Z.id.slice(0,8);I.set(Z.id,{title:G,project:Z.project})}}let C=x.map(L=>{let F=I.get(L.source_session_id),B=I.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:B?.title??L.target_session_id.slice(0,8),target_project:B?.project??null}});return i.json({suggestions:C})}catch(x){return i.json({error:x.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=$e(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"),A=y!==void 0?Number(y):2;if(!Number.isFinite(A)||A<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let k,x=i.req.query("edge_types");if(x){let F=x.split(",").map(B=>B.trim()).filter(Boolean);for(let B of F)if(!o.has(B))return i.json({error:`invalid edge_type: ${B}`},400);k=F}let N=i.req.query("include_wiki_links"),I=N===void 0?!0:!(N==="0"||N==="false"),C=i.req.query("include_suggestions"),L=C==="1"||C==="true";try{let F=en(l,{budget:g,scoring:E,maxDepth:A,edgeTypes:k,includeWikiLinks:I,includeSuggestions:L});return i.json(F)}catch(F){let B=F instanceof Error?F.message:"unknown error",ee=/not found/.test(B)?404:500;return i.json({error:B},ee)}}),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 A;if(p==="open")A=!1;else if(p==="resolved")A=!0;else if(p&&p!=="all")return i.json({error:`invalid status: ${p}; valid: open, resolved, all`},400);let k=_?Number(_):void 0;if(k!==void 0&&(!Number.isFinite(k)||k<1))return i.json({error:"invalid limit"},400);let x=E?Number(E):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<0))return i.json({error:"invalid offset"},400);try{let N=Ni({minOccurrenceCount:y,hasResolved:A,project:g,limit:k,offset:x});return i.json(N)}catch(N){return i.json({error:N.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=f(),g=l.prepare(`SELECT p.name AS project,
1806
+ WHERE s.id IN (${F})`).all(...O);for(let G of K){let P=G.first_user_message?G.first_user_message.slice(0,80):null,X=G.alias??G.auto_title??P??G.id.slice(0,8);C.set(G.id,{title:X,project:G.project})}}let I=A.map(O=>{let F=C.get(O.source_session_id),W=C.get(O.target_session_id);return{...O,source_title:F?.title??O.source_session_id.slice(0,8),source_project:F?.project??null,target_title:W?.title??O.target_session_id.slice(0,8),target_project:W?.project??null}});return i.json({suggestions:I})}catch(A){return i.json({error:A.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=Ze(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 N,A=i.req.query("edge_types");if(A){let F=A.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);N=F}let L=i.req.query("include_wiki_links"),C=L===void 0?!0:!(L==="0"||L==="false"),I=i.req.query("include_suggestions"),O=I==="1"||I==="true";try{let F=mn(l,{budget:g,scoring:E,maxDepth:k,edgeTypes:N,includeWikiLinks:C,includeSuggestions:O});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 N=_?Number(_):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<1))return i.json({error:"invalid limit"},400);let A=E?Number(E):void 0;if(A!==void 0&&(!Number.isFinite(A)||A<0))return i.json({error:"invalid offset"},400);try{let L=Xi({minOccurrenceCount:y,hasResolved:k,project:g,limit:N,offset:A});return i.json(L)}catch(L){return i.json({error:L.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=f(),g=l.prepare(`SELECT p.name AS project,
1807
1807
  COUNT(s.id) AS total_sessions,
1808
1808
  SUM(CASE WHEN oi.session_id IS NOT NULL THEN 1 ELSE 0 END) AS extracted_sessions,
1809
1809
  MAX(oi.extracted_at) AS last_extracted_at
@@ -1811,20 +1811,20 @@ ${o}
1811
1811
  LEFT JOIN sessions s ON s.project_id = p.id
1812
1812
  LEFT JOIN session_output_index oi ON oi.session_id = s.id
1813
1813
  GROUP BY p.id
1814
- 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,A)=>(y.total_sessions+=A.total_sessions,y.extracted_sessions+=A.extracted_sessions,y.remaining_sessions+=A.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=xe(i);if(l)return l;let p=Ar();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??qe,limit:_.data.limit??200,force:_.data.force??!1},y=po();if(E.limit>y.sessionCeiling)return ne({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 A=Kt(E.project);if(!A)return i.json({error:`project "${E.project}" not found`},404);let x=tt({projectId:A.id,limit:E.limit,force:E.force}).eligible.length,N=mo(x),I=Me(x,E.model),C=mt(),L=N.estimated_input_tokens_max+N.estimated_output_tokens_max>C.remaining_tokens_24h;if(ne({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:x}),x===0)return i.json({eligible_session_count:0,...N,plan_window_estimate:I,budget:C,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:B}=ao(E);return i.json({preflight_token:F,expires_at:new Date(B).toISOString(),eligible_session_count:x,...N,plan_window_estimate:I,budget:C,would_exceed_budget:L})});let m=j.object({preflight_token:j.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=xe(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 _=co(g.data.preflight_token);if(!_)return ne({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=mt(),A=(()=>{let I=Kt(_.project);return I?tt({projectId:I.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,k=mo(A),x=k.estimated_input_tokens_max+k.estimated_output_tokens_max;if(x>E.remaining_tokens_24h)return ne({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${x} 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:x},429);let N=Su({project:_.project,model:_.model,limit:_.limit,force:_.force,origin:i.req.header("origin")??null});return"error"in N?i.json({error:N.error},400):i.json({jobId:N.jobId,reused:N.reused},N.reused?409:200)}),t.get("/api/extract-outputs/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!fo(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ue(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let A of Tu(l,g))if(E||(await _.writeSSE({id:String(A.id),event:A.kind,data:JSON.stringify(A.data)}),A.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=fo(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=xe(i);return l||(yu(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(mt()));let h=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 B=Ar();if(B>0&&B<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(B/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:B},507)}let l=xe(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=h.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??wu},E=_n(_);if(!E)return ne({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=go({scope:_.scope,mode:_.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),A=y.estimated_input_tokens_max+y.estimated_output_tokens_max,k=bu(A),x=Me(k,_.model),N=mt(),C=y.estimated_input_tokens_max+y.estimated_output_tokens_max>N.remaining_tokens_24h;ne({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}=ao({project:_.target_id,model:_.model,limit:1,force:!1});return In.set(L,_),setTimeout(()=>In.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:x,budget:N,would_exceed_budget:C,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=xe(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 _=co(g.data.preflight_token),E=In.get(g.data.preflight_token)??null;if(In.delete(g.data.preflight_token),!E||!_)return ne({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=mt(),A=_n(E);if(!A)return i.json({error:E.scope==="cluster"?`cluster "${E.target_id}" no longer exists`:`project "${E.target_id}" has no findings`},404);let k=go({scope:E.scope,mode:E.mode,member_session_count:A.context_summary.session_count,cluster_count:A.context_summary.cluster_count}),x=k.estimated_input_tokens_max+k.estimated_output_tokens_max;if(x>y.remaining_tokens_24h)return ne({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 ${x} 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:x},429);let N=xu({intent:E,origin:i.req.header("origin")??null});return"error"in N?i.json({error:N.error},400):i.json({jobId:N.jobId,reused:N.reused},N.reused?409:200)}),t.get("/api/bug-patterns/synthesize/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!ho(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ue(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let A of Nu(l,g))if(E||(await _.writeSSE({id:String(A.id),event:A.kind,data:JSON.stringify(A.data)}),A.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=ho(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=xe(i);return l||(Ou(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=f(),_=["oi.bug_signatures IS NOT NULL"],E=[];l&&(_.push("p.name = ?"),E.push(l));let A=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1814
+ 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=M.object({project:M.string().min(1),model:M.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:M.number().int().positive().optional(),force:M.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=Ce(i);if(l)return l;let p=$r();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??st,limit:_.data.limit??200,force:_.data.force??!1},y=xo();if(E.limit>y.sessionCeiling)return ne({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=is(E.project);if(!k)return i.json({error:`project "${E.project}" not found`},404);let A=gt({projectId:k.id,limit:E.limit,force:E.force}).eligible.length,L=No(A),C=We(A,E.model),I=At(),O=L.estimated_input_tokens_max+L.estimated_output_tokens_max>I.remaining_tokens_24h;if(ne({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:A}),A===0)return i.json({eligible_session_count:0,...L,plan_window_estimate:C,budget:I,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}=wo(E);return i.json({preflight_token:F,expires_at:new Date(W).toISOString(),eligible_session_count:A,...L,plan_window_estimate:C,budget:I,would_exceed_budget:O})});let m=M.object({preflight_token:M.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=Ce(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 _=Ro(g.data.preflight_token);if(!_)return ne({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=At(),k=(()=>{let C=is(_.project);return C?gt({projectId:C.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,N=No(k),A=N.estimated_input_tokens_max+N.estimated_output_tokens_max;if(A>E.remaining_tokens_24h)return ne({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${A} 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:A},429);let L=Hu({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(!Co(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ye(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let k of Bu(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=Co(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=Ce(i);return l||(Wu(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(At()));let h=M.object({scope:M.enum(["cluster","project"]),target_id:M.string().min(1),mode:M.enum(["synopsis","priorities","root_cause"]),model:M.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let W=$r();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=Ce(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=h.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??qu},E=xn(_);if(!E)return ne({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=Oo({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,N=$u(k),A=We(N,_.model),L=At(),I=y.estimated_input_tokens_max+y.estimated_output_tokens_max>L.remaining_tokens_24h;ne({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:O,expiresAt:F}=wo({project:_.target_id,model:_.model,limit:1,force:!1});return Xn.set(O,_),setTimeout(()=>Xn.delete(O),9e4).unref?.(),i.json({preflight_token:O,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:A,budget:L,would_exceed_budget:I,context_summary:E.context_summary})});let b=M.object({preflight_token:M.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=Ce(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 _=Ro(g.data.preflight_token),E=Xn.get(g.data.preflight_token)??null;if(Xn.delete(g.data.preflight_token),!E||!_)return ne({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=At(),k=xn(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 N=Oo({scope:E.scope,mode:E.mode,member_session_count:k.context_summary.session_count,cluster_count:k.context_summary.cluster_count}),A=N.estimated_input_tokens_max+N.estimated_output_tokens_max;if(A>y.remaining_tokens_24h)return ne({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 ${A} 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:A},429);let L=Yu({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(!Io(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ye(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let k of zu(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=Io(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=Ce(i);return l||(Ku(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=f(),_=["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,
1815
1815
  s.started_at, oi.extracted_at, oi.bug_signatures
1816
1816
  FROM session_output_index oi
1817
1817
  JOIN sessions s ON s.id = oi.session_id
1818
1818
  JOIN projects p ON p.id = s.project_id
1819
1819
  WHERE ${_.join(" AND ")}
1820
1820
  ORDER BY oi.extracted_at DESC
1821
- LIMIT ?`).all(...E,p).map(C=>{let L=[];try{let F=JSON.parse(C.bug_signatures);Array.isArray(F)&&(L=F)}catch{L=[]}return{session_id:C.session_id,project:C.project,auto_title:C.auto_title,started_at:C.started_at,extracted_at:C.extracted_at,rawSignatures:L}}),k=A.flatMap(C=>C.rawSignatures.map(L=>L.message_hash).filter(L=>typeof L=="string")),x=so(k),N=A.map(C=>({session_id:C.session_id,project:C.project,auto_title:C.auto_title,started_at:C.started_at,extracted_at:C.extracted_at,signatures:C.rawSignatures.map(L=>{let F=L.message_hash?x.get(L.message_hash)??null:null;return{...L,resolved:no(F),resolution:F}}),signature_count:C.rawSignatures.length})),I=N.reduce((C,L)=>(C.sessions_total+=1,L.signature_count>0?(C.sessions_with_findings+=1,C.total_findings+=L.signature_count):C.sessions_empty+=1,C),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:N,totals:I})}),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=Zl({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):(Ql(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=f(),_=["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,
1821
+ LIMIT ?`).all(...E,p).map(I=>{let O=[];try{let F=JSON.parse(I.bug_signatures);Array.isArray(F)&&(O=F)}catch{O=[]}return{session_id:I.session_id,project:I.project,auto_title:I.auto_title,started_at:I.started_at,extracted_at:I.extracted_at,rawSignatures:O}}),N=k.flatMap(I=>I.rawSignatures.map(O=>O.message_hash).filter(O=>typeof O=="string")),A=Eo(N),L=k.map(I=>({session_id:I.session_id,project:I.project,auto_title:I.auto_title,started_at:I.started_at,extracted_at:I.extracted_at,signatures:I.rawSignatures.map(O=>{let F=O.message_hash?A.get(O.message_hash)??null:null;return{...O,resolved:bo(F),resolution:F}}),signature_count:I.rawSignatures.length})),C=L.reduce((I,O)=>(I.sessions_total+=1,O.signature_count>0?(I.sessions_with_findings+=1,I.total_findings+=O.signature_count):I.sessions_empty+=1,I),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:L,totals:C})}),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=bu({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):(Su(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=f(),_=["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,
1822
1822
  s.started_at, oi.extracted_at, oi.bug_signatures
1823
1823
  FROM session_output_index oi
1824
1824
  JOIN sessions s ON s.id = oi.session_id
1825
1825
  JOIN projects p ON p.id = s.project_id
1826
1826
  WHERE ${_.join(" AND ")}
1827
- ORDER BY oi.extracted_at DESC`).all(...E),A=[];for(let q of y){let G=[];try{let K=JSON.parse(q.bug_signatures);Array.isArray(K)&&(G=K)}catch{continue}for(let K of G)A.push({sig:K,session_id:q.session_id,project:q.project,auto_title:q.auto_title})}let k=new Map;for(let q of A){let G=q.sig.message_hash??`nohash:${(q.sig.snippet??"").slice(0,64)}`,K=k.get(G);K?K.push(q):k.set(G,[q])}let x=Array.from(k.keys()).filter(q=>!q.startsWith("nohash:")),N=so(x),I=[],C=new Map,L=[];for(let[q,G]of k){let K=G[0],P=K.sig.message_hash??null,X=P?N.get(P)??null:null,fe=no(X);if(!p&&fe)continue;let we=Array.from(new Set(G.map(V=>V.project))).sort(),At=Array.from(new Set(G.map(V=>V.session_id))),us={id:P??q,message_hash:P,error_type:K.sig.error_type??null,snippet:(K.sig.snippet??"").slice(0,200),file:K.sig.file??null,occurrence_count:G.length,projects:we,resolved:fe,fix_summary:X?.fix_summary??null,member_session_ids:At};I.push(us);for(let V of G)C.has(V.session_id)||C.set(V.session_id,{session_id:V.session_id,project:V.project,auto_title:V.auto_title}),L.push({cluster_id:us.id,session_id:V.session_id})}let F=[],B=4,ee=new Map;function Z(q){let G=ee.get(q)??0;return G>=B?!1:(ee.set(q,G+1),!0)}for(let q=0;q<I.length;q+=1)for(let G=q+1;G<I.length;G+=1){let K=I[q],P=I[G],X=null;K.error_type&&K.error_type!=="unknown"&&K.error_type===P.error_type?X="same_error_type":K.file&&P.file&&K.file===P.file&&(X="same_file"),X&&(!Z(K.id)||!Z(P.id)||F.push({a:K.id,b:P.id,reason:X}))}return i.json({clusters:I,sessions:Array.from(C.values()),member_edges:L,related_edges:F,totals:{cluster_count:I.length,singleton_count:I.filter(q=>q.occurrence_count===1).length,recurring_count:I.filter(q=>q.occurrence_count>1).length,session_count:C.size,resolved_count:I.filter(q=>q.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=Oi(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=Li(l,g,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",A=/not found/.test(y)?404:(/not a member/.test(y),400);return i.json({error:y},A)}}),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=Ci(l,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",A=/not found/.test(y)?404:(/cannot split|none of the supplied/.test(y),400);return i.json({error:y},A)}}),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=f();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 k=hs({sourceSessionId:g,targetSessionId:p,linkType:"wiki_link"});if(k.length>0)return i.json({link:k[0]});try{let x=Gi({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:x})}catch(x){return i.json({error:x.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=zi(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=f();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=vt(l).filter(C=>C.link_type===E),A=Kc(l,y);if(A.length===0)return i.json({links:[]});let k=A.map(C=>C.otherSessionId),x=k.map(()=>"?").join(","),N=p.prepare(`SELECT s.id,
1827
+ ORDER BY oi.extracted_at DESC`).all(...E),k=[];for(let P of y){let X=[];try{let Y=JSON.parse(P.bug_signatures);Array.isArray(Y)&&(X=Y)}catch{continue}for(let Y of X)k.push({sig:Y,session_id:P.session_id,project:P.project,auto_title:P.auto_title})}let N=new Map;for(let P of k){let X=P.sig.message_hash??`nohash:${(P.sig.snippet??"").slice(0,64)}`,Y=N.get(X);Y?Y.push(P):N.set(X,[P])}let A=Array.from(N.keys()).filter(P=>!P.startsWith("nohash:")),L=Eo(A),C=[],I=new Map,O=[];for(let[P,X]of N){let Y=X[0],Q=Y.sig.message_hash??null,V=Q?L.get(Q)??null:null,J=bo(V);if(!p&&J)continue;let re=Array.from(new Set(X.map(me=>me.project))).sort(),ze=Array.from(new Set(X.map(me=>me.session_id))),Me={id:Q??P,message_hash:Q,error_type:Y.sig.error_type??null,snippet:(Y.sig.snippet??"").slice(0,200),file:Y.sig.file??null,occurrence_count:X.length,projects:re,resolved:J,fix_summary:V?.fix_summary??null,member_session_ids:ze};C.push(Me);for(let me of X)I.has(me.session_id)||I.set(me.session_id,{session_id:me.session_id,project:me.project,auto_title:me.auto_title}),O.push({cluster_id:Me.id,session_id:me.session_id})}let F=[],W=4,K=new Map;function G(P){let X=K.get(P)??0;return X>=W?!1:(K.set(P,X+1),!0)}for(let P=0;P<C.length;P+=1)for(let X=P+1;X<C.length;X+=1){let Y=C[P],Q=C[X],V=null;Y.error_type&&Y.error_type!=="unknown"&&Y.error_type===Q.error_type?V="same_error_type":Y.file&&Q.file&&Y.file===Q.file&&(V="same_file"),V&&(!G(Y.id)||!G(Q.id)||F.push({a:Y.id,b:Q.id,reason:V}))}return i.json({clusters:C,sessions:Array.from(I.values()),member_edges:O,related_edges:F,totals:{cluster_count:C.length,singleton_count:C.filter(P=>P.occurrence_count===1).length,recurring_count:C.filter(P=>P.occurrence_count>1).length,session_count:I.size,resolved_count:C.filter(P=>P.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=Ji(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=Gi(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=Yi(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=f();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 N=Os({sourceSessionId:g,targetSessionId:p,linkType:"wiki_link"});if(N.length>0)return i.json({link:N[0]});try{let A=ga({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:A})}catch(A){return i.json({error:A.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=_a(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=f();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=Bt(l).filter(I=>I.link_type===E),k=fl(l,y);if(k.length===0)return i.json({links:[]});let N=k.map(I=>I.otherSessionId),A=N.map(()=>"?").join(","),L=p.prepare(`SELECT s.id,
1828
1828
  NULLIF(sa.alias, '') AS alias,
1829
1829
  s.auto_title,
1830
1830
  s.first_user_message,
@@ -1832,9 +1832,10 @@ ${o}
1832
1832
  FROM sessions s
1833
1833
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1834
1834
  LEFT JOIN projects p ON p.id = s.project_id
1835
- WHERE s.id IN (${x})`).all(...k),I=new Map(N.map(C=>[C.id,C]));return i.json({links:A.map(C=>{let L=I.get(C.otherSessionId),F=L?.alias?.trim()||L?.auto_title?.trim()||(L?.first_user_message?L.first_user_message.slice(0,80):"")||C.otherSessionId.slice(0,8);return{linkId:C.linkId,otherSessionId:C.otherSessionId,direction:C.direction,updatedAt:C.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 _=zn(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 A of p){let k=Number(A);if(!Number.isInteger(k)||k<=0){y.push({id:Number.isFinite(Number(A))?Number(A):-1,error:"invalid id"});continue}try{zn(k,g),_+=1}catch(x){let N=x.message;/already decided/.test(N)?E+=1:y.push({id:k,error:N})}}return i.json({decided:_,skipped:E,errors:y})}),t.get("/api/sessions",i=>{let l=f(),p=i.req.query("project"),g=i.req.query("since"),_=i.req.query("until"),E=i.req.queries("tag")??[],y=i.req.query("collection"),A=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),k=i.req.query("system")==="1"||i.req.query("system")==="true",x={limit:A},N="s.message_count > 2";if(k||(N+=" 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]%'"),p&&(N+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",x.proj=`%${cs(p)}%`),g&&(N+=" AND s.started_at >= @since",x.since=g),_&&(N+=" 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(F=>Be(F)).filter(Boolean).forEach((F,B)=>{N+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${B})`,x[`tag_${B}`]=F}),y){let L=Nr(y);if(L.length===0)return i.json([]);let F=L.map((B,ee)=>`@col_${ee}`).join(",");N+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${F}))`,L.forEach((B,ee)=>{x[`col_${ee}`]=B})}let C=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1835
+ WHERE s.id IN (${A})`).all(...N),C=new Map(L.map(I=>[I.id,I]));return i.json({links:k.map(I=>{let O=C.get(I.otherSessionId),F=O?.alias?.trim()||O?.auto_title?.trim()||(O?.first_user_message?O.first_user_message.slice(0,80):"")||I.otherSessionId.slice(0,8);return{linkId:I.linkId,otherSessionId:I.otherSessionId,direction:I.direction,updatedAt:I.updatedAt,title:F,project:O?.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 _=ur(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 N=Number(k);if(!Number.isInteger(N)||N<=0){y.push({id:Number.isFinite(Number(k))?Number(k):-1,error:"invalid id"});continue}try{ur(N,g),_+=1}catch(A){let L=A.message;/already decided/.test(L)?E+=1:y.push({id:N,error:L})}}return i.json({decided:_,skipped:E,errors:y})}),t.get("/api/sessions",i=>{let l=f(),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))),N=i.req.query("cursor"),A=null;if(N)try{let K=Buffer.from(N,"base64url").toString("utf8"),G=JSON.parse(K);typeof G.ts=="string"&&typeof G.id=="string"&&(A={ts:G.ts,id:G.id})}catch{}let L=i.req.query("system")==="1"||i.req.query("system")==="true",C={limit:k},I="s.message_count > 2"+Jn("s",L);if(p&&(I+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",C.proj=`%${Ts(p)}%`),g&&(I+=" AND s.started_at >= @since",C.since=g),_&&(I+=" AND s.started_at <= @until",/^\d{4}-\d{2}-\d{2}$/.test(_)?C.until=`${_}T23:59:59.999Z`:C.until=_),E.length>0&&E.map(G=>Qe(G)).filter(Boolean).forEach((G,P)=>{I+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${P})`,C[`tag_${P}`]=G}),y){let K=Wr(y);if(K.length===0)return i.json({items:[],nextCursor:null});let G=K.map((P,X)=>`@col_${X}`).join(",");I+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${G}))`,K.forEach((P,X)=>{C[`col_${X}`]=P})}A&&(I+=" 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))",C.cursor_ts=A.ts,C.cursor_id=A.id);let O=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1836
1836
  s.message_count, s.first_user_message, s.git_branch,
1837
1837
  s.auto_title, s.auto_title_source, s.verification_status,
1838
+ COALESCE(s.ended_at, s.started_at, '') AS _cursor_ts,
1838
1839
  NULLIF(sa.alias, '') AS alias,
1839
1840
  CASE
1840
1841
  WHEN (sn.content IS NOT NULL AND sn.content != '')
@@ -1850,17 +1851,17 @@ ${o}
1850
1851
  JOIN projects p ON p.id = s.project_id
1851
1852
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1852
1853
  LEFT JOIN session_notes sn ON sn.session_id = s.id
1853
- WHERE ${N}
1854
- ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1855
- LIMIT @limit`).all(x).map(({tags_csv:L,...F})=>{let B=F.id,ee=v.getOrigin(B),Z=F.alias,q=Z==null?null:v.isSessionAutoLinked(B)?"auto":"manual",G=Xt({auto_title:F.auto_title,auto_title_source:F.auto_title_source??null,has_alias:Z!=null&&q==="manual"});return{...F,tags:L?L.split(","):[],origin:ee?{editor:ee.editor,label:ee.label}:null,alias_source:q,title_quality:G}});return i.json(C)}),t.get("/api/sessions/:id",i=>{let l=f(),p=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
1854
+ WHERE ${I}
1855
+ ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC, s.id DESC
1856
+ LIMIT @limit`).all(C),F=O.map(({tags_csv:K,_cursor_ts:G,...P})=>{let X=P.id,Y=v.getOrigin(X),Q=P.alias,V=Q==null?null:v.isSessionAutoLinked(X)?"auto":"manual",J=ts({auto_title:P.auto_title,auto_title_source:P.auto_title_source??null,has_alias:Q!=null&&V==="manual"});return{...P,tags:K?K.split(","):[],origin:Y?{editor:Y.editor,label:Y.label}:null,alias_source:V,title_quality:J}}),W=null;if(O.length===k&&O.length>0){let K=O[O.length-1],G=JSON.stringify({ts:K._cursor_ts??"",id:K.id});W=Buffer.from(G,"utf8").toString("base64url")}return i.json({items:F,nextCursor:W})}),t.get("/api/sessions/:id",i=>{let l=f(),p=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
1856
1857
  NULLIF(sa.alias, '') AS alias
1857
1858
  FROM sessions s
1858
1859
  JOIN projects p ON p.id = s.project_id
1859
1860
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1860
- WHERE s.id = ?`).get(p);if(!g)return i.json({error:"not found"},404);let _=Mt(p),E=v.getOrigin(p),y=E?{editor:E.editor,label:E.label}:null,A=g.alias==null?null:v.isSessionAutoLinked(p)?"auto":"manual",k=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1861
+ WHERE s.id = ?`).get(p);if(!g)return i.json({error:"not found"},404);let _=Xt(p),E=v.getOrigin(p),y=E?{editor:E.editor,label:E.label}:null,k=g.alias==null?null:v.isSessionAutoLinked(p)?"auto":"manual",N=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1861
1862
  FROM messages
1862
1863
  WHERE session_id = ?
1863
- ORDER BY COALESCE(timestamp, ''), rowid`).all(p);return i.json({session:{...g,tags:_,origin:y,alias_source:A},messages:k})}),t.get("/api/tags",i=>i.json(Ze())),t.get("/api/sessions/:id/tags",i=>i.json({tags:Mt(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=Ve(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(oa(l,p))}),t.get("/api/config/auto-tag",i=>i.json(Qr(Le()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),p=un.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 _=Hl(g);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&wn(),i.json(Qr(_))}),t.get("/api/onboarding",i=>{let p=f().prepare(`SELECT s.id,
1864
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(p);return i.json({session:{...g,tags:_,origin:y,alias_source:k},messages:N})}),t.get("/api/tags",i=>i.json(dt())),t.get("/api/sessions/:id/tags",i=>i.json({tags:Xt(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=ut(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(ka(l,p))}),t.get("/api/config/auto-tag",i=>i.json(_o(Fe()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),p=yn.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 _=lu(g);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&Mn(),i.json(_o(_))}),t.get("/api/onboarding",i=>{let p=f().prepare(`SELECT s.id,
1864
1865
  p.name AS project,
1865
1866
  s.started_at,
1866
1867
  s.ended_at,
@@ -1872,7 +1873,7 @@ ${o}
1872
1873
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1873
1874
  WHERE s.message_count > 2
1874
1875
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1875
- LIMIT 1`).get();return i.json({state:An(),mostRecentSession:p??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),p=Rn.partial().safeParse(l);return p.success?i.json(fd(p.data)):i.json({error:"invalid onboarding state",issues:p.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(hd())),t.get("/api/config/mcp-install",i=>i.json({...De(),claudeCliAvailable:le()})),t.post("/api/config/mcp-install",i=>i.json({...ld(),claudeCliAvailable:le()})),t.delete("/api/config/mcp-install",i=>i.json({...ud(),claudeCliAvailable:le()}));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(qo)return i.json({error:"a scan is already running"},409);if(!le())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!De().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=Le(),y=g.data.model??E.model,A=f(),k=()=>A.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,x=k();qo=!0;let N;try{let I=g.data.scanId;N=await rr(_,{model:y,scanId:I});let C=k(),L=Math.max(0,C-x);return I&&Ss(I,{type:"done",result:{success:N.success,exitCode:N.exitCode,tagsAdded:L}}),i.json({success:N.success,exitCode:N.exitCode,tagsAdded:L,model:y,stdout:_e(N.stdout.slice(0,2e3)).redacted,stderrTail:_e(N.stderr.slice(-2e3)).redacted})}finally{qo=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return Ue(i,async p=>{let g=[],_={resolve:()=>{}},E=new Promise(x=>{_.resolve=x}),y=ca(l,x=>{g.push(x);let N=_.resolve;E=new Promise(I=>{_.resolve=I}),N()}),A=!1,k=setInterval(()=>{A||p.writeSSE({event:"heartbeat",data:""}).catch(()=>{A=!0})},15e3);try{for(;!A;){g.length===0&&await E;let x=g.shift();if(x&&(await p.writeSSE({event:x.type,data:JSON.stringify(x)}),x.type==="done"))break}}finally{A=!0,clearInterval(k),y()}})}),t.get("/api/prompts",i=>i.json({prompts:tr.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:le()})),t.post("/api/prompts/run",async i=>{if(!le())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!De().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=aa(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let y=E.build(_.data.args??{}),A=Le(),k=_.data.model??A.model,x=await He(y,E.allowedTools,{model:k});return i.json({success:x.success,exitCode:x.exitCode,promptName:E.name,model:k,stdout:x.stdout,stderrTail:x.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(is())),t.get("/api/autopilot/events",i=>Ue(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(is())});let p=[],g=()=>{},_=new Promise(y=>g=y),E=id(y=>{p.push(y);let A=g;_=new Promise(k=>g=k),A()});try{for(;;){if(p.length===0){let A=new Promise(x=>setTimeout(()=>x("tick"),3e4));if(await Promise.race([_.then(()=>"event"),A])==="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=>(wn(),i.json({ok:!0,snapshot:is()})));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=Le();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 _=et(g.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=Qu(_.length);return rd(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=En(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=En(i.req.param("id"));return l?Ue(i,async p=>{await p.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let A of l.results)await p.writeSSE({event:"result",data:JSON.stringify(A)});let g=[],_={resolve:()=>{}},E=new Promise(A=>{_.resolve=A}),y=ed(l,A=>{g.push(A);let k=_.resolve;E=new Promise(x=>{_.resolve=x}),k()});try{for(;l.status==="running"||l.status==="pending";){g.length===0&&await E;let A=g.shift();if(A&&(await p.writeSSE({event:A.type,data:JSON.stringify(A)}),A.type==="done"||A.type==="status"&&(A.status==="cancelled"||A.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=En(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 _=od(l,g.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return td(l),sd(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=be(l,p.alias);if(p.pin===!0)v.unlinkSession(l);else{let _=f().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=_?.cwd?_.cwd.replace(/\/+$/,""):null,y=!1;if(E&&_?.started_at){let A=Date.parse(_.started_at),k=_.started_at,x=v.all().filter(N=>N.cwd&&N.cwd.replace(/\/+$/,"")===E&&$t({sessionStartedAt:k,terminalOpenedAt:N.opened_at??null}).allowed);if(Number.isFinite(A)&&x.length>0){let I=x.map(C=>({t:C,gap:A-Date.parse(C.opened_at??"")})).filter(C=>Number.isFinite(C.gap)).sort((C,L)=>C.gap-L.gap)[0];I&&(v.linkSession(l,I.t.shell_pid),y=!0)}}y||v.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),v.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:Ae(l)})}),t.get("/api/config/auto-title",i=>i.json(Ge())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),p=pn.partial().safeParse(l);return p.success?i.json(Gl(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=ke(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(!Ge().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!f().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await _l(l);return me(l,E,"agent"),i.json(ke(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=ke(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 me(l,_.title,"agent"),i.json(ke(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??mn;try{let _=await to(l,{model:g,force:p.force===!0,budget:typeof p.budget=="number"?p.budget:void 0,signal:i.req.raw.signal}),E=ke(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 dt)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:mn,A=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,k=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,N=f().prepare(`SELECT s.id,
1876
+ LIMIT 1`).get();return i.json({state:Fn(),mostRecentSession:p??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),p=Dn.partial().safeParse(l);return p.success?i.json(Fd(p.data)):i.json({error:"invalid onboarding state",issues:p.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(Pd())),t.get("/api/config/mcp-install",i=>i.json({...qe(),claudeCliAvailable:pe()})),t.post("/api/config/mcp-install",i=>i.json({...Ld(),claudeCliAvailable:pe()})),t.delete("/api/config/mcp-install",i=>i.json({...Cd(),claudeCliAvailable:pe()}));let T=M.object({scope:M.object({untaggedOnly:M.boolean().optional(),project:M.string().optional(),collectionId:M.string().optional(),sessionIds:M.array(M.string()).optional(),limit:M.number().int().min(1).max(500).optional()}).default({}),model:M.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:M.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(ii)return i.json({error:"a scan is already running"},409);if(!pe())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!qe().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=Fe(),y=g.data.model??E.model,k=f(),N=()=>k.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,A=N();ii=!0;let L;try{let C=g.data.scanId;L=await br(_,{model:y,scanId:C});let I=N(),O=Math.max(0,I-A);return C&&Is(C,{type:"done",result:{success:L.success,exitCode:L.exitCode,tagsAdded:O}}),i.json({success:L.success,exitCode:L.exitCode,tagsAdded:O,model:y,stdout:Se(L.stdout.slice(0,2e3)).redacted,stderrTail:Se(L.stderr.slice(-2e3)).redacted})}finally{ii=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return Ye(i,async p=>{let g=[],_={resolve:()=>{}},E=new Promise(A=>{_.resolve=A}),y=Na(l,A=>{g.push(A);let L=_.resolve;E=new Promise(C=>{_.resolve=C}),L()}),k=!1,N=setInterval(()=>{k||p.writeSSE({event:"heartbeat",data:""}).catch(()=>{k=!0})},15e3);try{for(;!k;){g.length===0&&await E;let A=g.shift();if(A&&(await p.writeSSE({event:A.type,data:JSON.stringify(A)}),A.type==="done"))break}}finally{k=!0,clearInterval(N),y()}})}),t.get("/api/prompts",i=>i.json({prompts:fr.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:pe()})),t.post("/api/prompts/run",async i=>{if(!pe())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!qe().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(()=>({})),_=M.object({name:M.string(),args:M.record(M.string(),M.unknown()).optional(),model:M.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=xa(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let y=E.build(_.data.args??{}),k=Fe(),N=_.data.model??k.model,A=await et(y,E.allowedTools,{model:N});return i.json({success:A.success,exitCode:A.exitCode,promptName:E.name,model:N,stdout:A.stdout,stderrTail:A.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(fs())),t.get("/api/autopilot/events",i=>Ye(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(fs())});let p=[],g=()=>{},_=new Promise(y=>g=y),E=xd(y=>{p.push(y);let k=g;_=new Promise(N=>g=N),k()});try{for(;;){if(p.length===0){let k=new Promise(A=>setTimeout(()=>A("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=>(Mn(),i.json({ok:!0,snapshot:fs()})));let S=M.object({scope:M.object({untaggedOnly:M.boolean().optional(),project:M.string().optional(),collectionId:M.string().optional(),sessionIds:M.array(M.string()).optional(),limit:M.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=Fe();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 _=mt(g.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=Sd(_.length);return kd(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=Ln(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=Ln(i.req.param("id"));return l?Ye(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=Td(l,k=>{g.push(k);let N=_.resolve;E=new Promise(A=>{_.resolve=A}),N()});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 w=M.object({selection:M.array(M.object({sessionId:M.string(),tags:M.array(M.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=Ln(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 _=Ad(l,g.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return yd(l),wd(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=he(l,p.alias);if(p.pin===!0)v.unlinkSession(l);else{let _=f().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),N=_.started_at,A=v.all().filter(L=>L.cwd&&L.cwd.replace(/\/+$/,"")===E&&Kt({sessionStartedAt:N,terminalOpenedAt:L.opened_at??null}).allowed);if(Number.isFinite(k)&&A.length>0){let C=A.map(I=>({t:I,gap:k-Date.parse(I.opened_at??"")})).filter(I=>Number.isFinite(I.gap)).sort((I,O)=>I.gap-O.gap)[0];C&&(v.linkSession(l,C.t.shell_pid),y=!0)}}y||v.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 Xs(l),v.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:Te(l)})}),t.get("/api/config/auto-title",i=>i.json(it())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),p=Rn.partial().safeParse(l);return p.success?i.json(_u(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=Le(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(!it().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!f().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await Dl(l);return Ee(l,E,"agent"),i.json(Le(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=Le(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 Ee(l,_.title,"agent"),i.json(Le(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??kn;try{let _=await ho(l,{model:g,force:p.force===!0,budget:typeof p.budget=="number"?p.budget:void 0,signal:i.req.raw.signal}),E=Le(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 Rt)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 O of g){if(typeof O!="string")return i.json({error:`invalid quality_filter entry: ${O}`},400);if(!_.has(O))return i.json({error:`quality_filter must be a subset of ${[..._].join(",")}; got ${O}`},400);E.push(O)}let y=typeof l.model=="string"&&l.model.length>0?l.model:kn,k=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,N=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,L=f().prepare(`SELECT s.id,
1876
1877
  s.auto_title,
1877
1878
  s.auto_title_source,
1878
1879
  NULLIF(sa.alias, '') AS alias
@@ -1881,7 +1882,29 @@ ${o}
1881
1882
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1882
1883
  WHERE p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\'
1883
1884
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1884
- LIMIT @limit`).all({proj:`%${cs(p)}%`,limit:A}),I=new Set(E),C=N.filter(L=>{let F=L.alias==null?null:v.isSessionAutoLinked(L.id)?"auto":"manual",B=Xt({auto_title:L.auto_title,auto_title_source:L.auto_title_source??null,has_alias:L.alias!=null&&F==="manual"});return I.has(B)});return Ue(i,async L=>{let F=C.length,B=[],ee=[],Z=[],q=0,G=async(P,X)=>{q+=1;try{await L.writeSSE({id:String(q),event:P,data:JSON.stringify(X)})}catch{}};await G("start",{total:F,model:y});let K=0;for(let P of C){if(i.req.raw.signal.aborted)break;K+=1;try{let X=await to(P.id,{model:y,budget:k,signal:i.req.raw.signal});X.written?(B.push(P.id),await G("progress",{sessionId:P.id,title:X.title,evidence:X.evidence,confidence:X.confidence,current:K,total:F})):(ee.push({sessionId:P.id,reason:X.skipped??"unknown"}),await G("skipped",{sessionId:P.id,reason:X.skipped??"unknown",current:K,total:F}))}catch(X){let fe=X instanceof Error?X.message:String(X),we=X instanceof dt?"no-context-available":"failed";Z.push({sessionId:P.id,error:fe}),await G("error",{sessionId:P.id,error:fe,code:we,current:K,total:F})}}await G("done",{generated:B,skipped:ee,failed:Z,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),p=js(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=Ka(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(!Ge().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await Va(l),_=Za(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(ar())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),p=ys.partial().safeParse(l);return p.success?(ws(p.data),i.json(ar())):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",async i=>{if(Jo)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 Jo=!0,ks({limit:g,force:!!p.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{Jo=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",async i=>{if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let p=i.req.param("id"),g=await As(p);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=Ne(),p=Jd(),g=Uo();return i.json({embedder:l,worker:p,modelInstalled:g})}),t.post("/api/semantic/install",ls,async i=>{if(Uo())return i.json({ok:!0,status:"already_installed"});if(Xo)return i.json({error:"a scan is already running"},409);Xo=!0;try{return await Zd(),await gs(),Xd(),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{Xo=!1}}),t.get("/api/sessions/:id/similar",ls,async i=>{if(!Ne().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 Pd(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",ls,async i=>{let l=f(),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(P=>P.length>0),E=_.filter(P=>P.startsWith("#")).map(P=>Be(P)).filter(Boolean),y=_.filter(P=>!P.startsWith("#")),A=y.length>20,x=(A?y.slice(0,20):y).map(P=>`"${P.replace(/"/g,"")}"`),N=x.join(" "),I=Math.max(1,Math.min(200,Number(i.req.query("limit")??30)));if(x.length===0&&E.length>0){let P=`
1885
+ LIMIT @limit`).all({proj:`%${Ts(p)}%`,limit:k}),C=new Set(E),I=L.filter(O=>{let F=O.alias==null?null:v.isSessionAutoLinked(O.id)?"auto":"manual",W=ts({auto_title:O.auto_title,auto_title_source:O.auto_title_source??null,has_alias:O.alias!=null&&F==="manual"});return C.has(W)});return Ye(i,async O=>{let F=I.length,W=[],K=[],G=[],P=0,X=async(Q,V)=>{P+=1;try{await O.writeSSE({id:String(P),event:Q,data:JSON.stringify(V)})}catch{}};await X("start",{total:F,model:y});let Y=0;for(let Q of I){if(i.req.raw.signal.aborted)break;Y+=1;try{let V=await ho(Q.id,{model:y,budget:N,signal:i.req.raw.signal});V.written?(W.push(Q.id),await X("progress",{sessionId:Q.id,title:V.title,evidence:V.evidence,confidence:V.confidence,current:Y,total:F})):(K.push({sessionId:Q.id,reason:V.skipped??"unknown"}),await X("skipped",{sessionId:Q.id,reason:V.skipped??"unknown",current:Y,total:F}))}catch(V){let J=V instanceof Error?V.message:String(V),re=V instanceof Rt?"no-context-available":"failed";G.push({sessionId:Q.id,error:J}),await X("error",{sessionId:Q.id,error:J,code:re,current:Y,total:F})}}await X("done",{generated:W,skipped:K,failed:G,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),p=Js(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=fc(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(!it().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await hc(l),_=Ec(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(yr())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),p=js.partial().safeParse(l);return p.success?(Ms(p.data),i.json(yr())):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",je,async i=>{if(ci)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 ci=!0,Ps({limit:g,force:!!p.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{ci=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",je,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 Fs(p);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=_e(),p=ve(),g=Ge(),_=ti(),E=_?.chunksPerSec??0,y=E>0?Math.ceil(p.queueDepth/E):0;return i.json({embedder:l,worker:p,modelInstalled:g,throughput:{chunksPerSec:E,samples:_?.samples??0,source:_?"local-measured":"no-samples-yet"},etaSeconds:y})}),t.post("/api/semantic/install",je,async i=>{if(Ge())return i.json({ok:!0,status:"already_installed"});if(ai)return i.json({error:"a scan is already running"},409);ai=!0;try{return await Ap(),await $e(),Ss(),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{ai=!1}}),t.get("/api/semantic/reindex-preview",je,async i=>{let l=(i.req.query("project")??"").trim();if(!l)return i.json({error:"project name required"},400);let p=f(),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
1886
+ COUNT(*) AS total,
1887
+ SUM(CASE WHEN s.message_count >= 3 THEN 1 ELSE 0 END) AS eligible,
1888
+ SUM(CASE WHEN s.message_count >= 3 AND EXISTS
1889
+ (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id) THEN 1 ELSE 0 END)
1890
+ AS indexed
1891
+ FROM sessions s WHERE s.project_id = ?`).get(g.id),E=_.eligible??0,y=_.indexed??0,k=Math.max(0,E-y),N=p.prepare(`SELECT COUNT(*) AS n FROM chunk_queue
1892
+ WHERE session_id NOT IN (SELECT id FROM sessions WHERE project_id = ?)`).get(g.id).n,A=ti(),L=2,C=30,I=L,O=C,F=L*C,W="fallback-baseline",K=0;if(A)I=A.sessionsPerSec,O=A.avgChunksPerSession,F=A.chunksPerSec,W="local-measured",K=A.samples;else if(Ge()){if(!_e().loaded)try{await $e()}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"],Q=Date.now();await ct(Y);let V=Date.now()-Q;V>0&&(F=Y.length*1e3/V,I=F/C,W="live-benchmark")}catch{}}let G=Y=>p.prepare(`SELECT
1893
+ COALESCE(SUM(CASE WHEN ec > 5 THEN 5 ELSE ec END), 0) AS total_quick,
1894
+ COALESCE(SUM(CASE WHEN ec > 80 THEN 80 ELSE ec END), 0) AS total_standard,
1895
+ COALESCE(SUM(CASE WHEN ec > 200 THEN 200 ELSE ec END), 0) AS total_full,
1896
+ COALESCE(SUM(ec), 0) AS total_uncapped
1897
+ FROM (
1898
+ SELECT
1899
+ s.id,
1900
+ CASE WHEN s.message_count < 4 THEN 1
1901
+ ELSE (s.message_count + 3) / 4
1902
+ END AS ec
1903
+ FROM sessions s
1904
+ WHERE s.project_id = ? AND s.message_count >= 3 ${Y}
1905
+ )`).get(g.id),P=G("AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)"),X=G("");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:_e().loaded,workerRunning:ve().running,queueDepthOther:N,sessionsPerSec:I,avgChunksPerSession:O,chunksPerSec:F,throughputSource:W,throughputSamples:K,estimatedChunksByDepth:{new:{quick:P.total_quick,standard:P.total_standard,full:P.total_full,uncapped:P.total_uncapped},force:{quick:X.total_quick,standard:X.total_standard,full:X.total_full,uncapped:X.total_uncapped}}})}),t.post("/api/semantic/cancel-reindex",je,async i=>{let l={};try{l=await i.req.json()}catch{}let p=(l.project??"").trim(),g=f(),_=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?hp(E):Ep(),ve().queueDepth===0&&Sp(),i.json({cleared:_,project:p||null,queueDepth:ve().queueDepth})}),t.post("/api/semantic/reindex-project",je,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)));_p(_);let E=f(),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
1906
+ WHERE s.project_id = ? AND s.message_count >= 3
1907
+ AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)`,N=E.prepare(k).all(y.id);if(N.length===0)return i.json({enqueued:0,queueDepth:ve().queueDepth,message:"nothing to do \u2014 every session in this project is already vectorized (use force:true to re-embed)"});let A=E.prepare("INSERT INTO chunk_queue(session_id, action) VALUES (?, 'embed')");if(E.transaction(()=>{for(let C of N)A.run(C.id)})(),!_e().loaded)try{await $e()}catch(C){let I=C instanceof Error?C.message:"unknown error";return i.json({error:`embedder load failed: ${I}`},500)}return ve().running||Ss(),i.json({enqueued:N.length,queueDepth:ve().queueDepth,project:p,appliedMaxChunks:_})}),t.get("/api/sessions/:id/similar",je,async i=>{if(!_e().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 op(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",je,async i=>{let l=f(),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(J=>J.length>0),E=_.filter(J=>J.startsWith("#")).map(J=>Qe(J)).filter(Boolean),y=_.filter(J=>!J.startsWith("#")),k=y.length>20,A=(k?y.slice(0,20):y).map(J=>`"${J.replace(/"/g,"")}"`),L=A.join(" "),C=Math.max(1,Math.min(200,Number(i.req.query("limit")??30))),I=i.req.query("system")==="1"||i.req.query("system")==="true",O=Jn("s",I);if(A.length===0&&E.length>0){let J=`
1885
1908
  SELECT s.id AS session_id,
1886
1909
  s.id AS message_uuid,
1887
1910
  p.name AS project,
@@ -1893,8 +1916,8 @@ ${o}
1893
1916
  FROM sessions s
1894
1917
  JOIN projects p ON p.id = s.project_id
1895
1918
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1896
- WHERE 1=1
1897
- `,X={limit:I};g&&(P+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",X.proj=`%${cs(g)}%`),E.forEach((we,At)=>{P+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${At})`,X[`tag_${At}`]=we}),P+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let fe=l.prepare(P).all(X);return i.json({query:p,hits:fe,tags:E,truncated:A})}let C=`
1919
+ WHERE 1=1${O}
1920
+ `,re={limit:C};g&&(J+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",re.proj=`%${Ts(g)}%`),E.forEach((Me,me)=>{J+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${me})`,re[`tag_${me}`]=Me}),J+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let ze=l.prepare(J).all(re);return i.json({query:p,hits:ze,tags:E,truncated:k})}let F=`
1898
1921
  SELECT m.session_id AS session_id,
1899
1922
  m.uuid AS message_uuid,
1900
1923
  p.name AS project,
@@ -1908,8 +1931,8 @@ ${o}
1908
1931
  JOIN sessions s ON s.id = m.session_id
1909
1932
  JOIN projects p ON p.id = s.project_id
1910
1933
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1911
- WHERE messages_fts MATCH @fts
1912
- `,L={fts:N,limit:I};g&&(C+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",L.proj=`%${cs(g)}%`),E.forEach((P,X)=>{C+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${X})`,L[`tag_${X}`]=P}),C+=" ORDER BY bm25(messages_fts) LIMIT @limit";let B=l.prepare(C).all(L).map(P=>({...P,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:p,hits:B,tags:E,truncated:A});let Z=[];try{let P=`
1934
+ WHERE messages_fts MATCH @fts${O}
1935
+ `,W={fts:L,limit:C};g&&(F+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",W.proj=`%${Ts(g)}%`),E.forEach((J,re)=>{F+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${re})`,W[`tag_${re}`]=J}),F+=" ORDER BY bm25(messages_fts) LIMIT @limit";let G=l.prepare(F).all(W).map(J=>({...J,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:p,hits:G,tags:E,truncated:k});let X=[];try{let J=`
1913
1936
  SELECT s.id AS session_id,
1914
1937
  s.id AS message_uuid,
1915
1938
  p.name AS project,
@@ -1924,16 +1947,16 @@ ${o}
1924
1947
  JOIN sessions s ON s.id = ss.session_id
1925
1948
  JOIN projects p ON p.id = s.project_id
1926
1949
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1927
- WHERE sessions_fts MATCH @fts
1928
- `,X={fts:N,limit:I};g&&(P+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",X.proj=`%${cs(g)}%`),E.forEach((fe,we)=>{P+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${we})`,X[`tag_${we}`]=fe}),P+=" ORDER BY rank LIMIT @limit",Z=l.prepare(P).all(X)}catch(P){console.error("[search.semantic] failed:",P)}if(Ne().loaded)try{let P=await Fd(p,I),X=B.map(V=>({id:String(V.session_id),data:V,lane:"bm25"})),fe=Z.map(V=>({id:String(V.session_id),data:V,lane:"summary"})),we=P.map(V=>({id:V.sessionId,data:{session_id:V.sessionId,snippet:V.text,matched_via:"vector"},lane:"vector"})),us=Ud([X,fe,we]).slice(0,I).map(V=>({...V.data,session_id:V.id,rrf_score:V.score,lanes:V.lanes,matched_via:V.lanes.length>1?"fused":V.lanes[0]}));return i.json({query:p,hits:us,tags:E,mode:"semantic",fusion:"rrf",truncated:A})}catch(P){console.error("[search.vector] failed, falling back:",P)}let q=new Set(B.map(P=>String(P.session_id))),G=Z.filter(P=>!q.has(String(P.session_id))).map(({rank:P,...X})=>({...X,matched_via:"semantic"})),K=[...B,...G].slice(0,I);return i.json({query:p,hits:K,tags:E,mode:"semantic",truncated:A})}),t.get("/api/sessions/:id/context",ls,i=>{let l=f(),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,
1950
+ WHERE sessions_fts MATCH @fts${O}
1951
+ `,re={fts:L,limit:C};g&&(J+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",re.proj=`%${Ts(g)}%`),E.forEach((ze,Me)=>{J+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${Me})`,re[`tag_${Me}`]=ze}),J+=" ORDER BY rank LIMIT @limit",X=l.prepare(J).all(re)}catch(J){console.error("[search.semantic] failed:",J)}if(_e().loaded)try{let J=await rp(p,C),re=G.map(le=>({id:String(le.session_id),data:le,lane:"bm25"})),ze=X.map(le=>({id:String(le.session_id),data:le,lane:"summary"})),Me=J.map(le=>({id:le.sessionId,data:{session_id:le.sessionId,snippet:le.text,matched_via:"vector"},lane:"vector"})),sm=ip([re,ze,Me]).slice(0,C).map(le=>({...le.data,session_id:le.id,rrf_score:le.score,lanes:le.lanes,matched_via:le.lanes.length>1?"fused":le.lanes[0]}));return i.json({query:p,hits:sm,tags:E,mode:"semantic",fusion:"rrf",truncated:k})}catch(J){console.error("[search.vector] failed, falling back:",J)}let Y=new Set(G.map(J=>String(J.session_id))),Q=X.filter(J=>!Y.has(String(J.session_id))).map(({rank:J,...re})=>({...re,matched_via:"semantic"})),V=[...G,...Q].slice(0,C);return i.json({query:p,hits:V,tags:E,mode:"semantic",truncated:k})}),t.get("/api/sessions/:id/context",je,i=>{let l=f(),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,
1929
1952
  s.started_at, s.ended_at, s.message_count, s.git_branch
1930
1953
  FROM sessions s JOIN projects p ON p.id = s.project_id
1931
- WHERE s.id = ?`).get(p);if(!y)return i.json({error:"not found"},404);let A=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1954
+ 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
1932
1955
  FROM messages
1933
1956
  WHERE session_id = ?
1934
- ORDER BY COALESCE(timestamp, ''), rowid`).all(p),k=qa(y,A,{mode:g,includeSidechain:_,prelude:E});return i.text(k)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:ec(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),p=Ie(l);if(!p)return i.json({error:"not found"},404);let g=tc(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=Bt({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=nc(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=rc(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=oc(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=Ht(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=ic(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:sc(l)})});let R=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:dc()})),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||!R.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let p=jr({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=pc(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=mc(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:Hs({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let p=_c(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 gc(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=Ws();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)),_=Hs({includeDismissed:!1}).find(y=>y.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=lc(_.type,_.pattern,p);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(fc());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Pr({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),p=re(l);if(!p)return i.json({error:"thread not found"},404);let g=p.edges.map(_=>({..._,alias_source:_.alias==null?null:v.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=Xs({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&&kc(l,p.name),p.close&&xc(l),p.reopen&&Nc(l),p.archive&&Oc(l),"folder_id"in p&&zc(l,p.folder_id??null);let g=re(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:Hr()})),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=Wc({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=Xc(l,p.name)),"parent_folder_id"in p&&(g=Jc(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 Gc(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 Yc(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=Js({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=Ac(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 _=Wt(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=Lc(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=Cc({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:Rc(l)})});let D=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(),O=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:D});t.post("/api/threads/scan/preflight",async i=>{let l=xe(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=O.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Gu({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 Y=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:D});t.post("/api/threads/scan/apply",async i=>{let l=xe(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=Y.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Ku({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(!yo(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ue(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let A of Vu(l,g))if(E||(await _.writeSSE({id:String(A.id),event:A.kind,data:JSON.stringify(A.data)}),A.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=yo(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=xe(i);return l||(Zu(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let M=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=M.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=await $c(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 _=Bc(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=re(l);if(!p)return i.json({error:"thread not found"},404);let g=f(),_=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=re(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 _=Le(),E=p.model??_.model,y=wl({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(!zr(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ue(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let A of Rl(l,g))if(E||(await _.writeSSE({id:String(A.id),event:A.kind,data:JSON.stringify(A.data)}),A.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/jobs/:jobId",i=>{let l=zr(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>Al(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=v.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:v.size()});let g=v.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:v.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=v.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=v.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let _=ip(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=v.remove(l.shell_pid);return i.json({ok:!0,removed:p,count:v.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):(v.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:v.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 v.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 v.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=v.claimPidOwnership(L.shell_pid,_);return E.push({shell_pid:L.shell_pid,ownership:F}),F!=="rejected"}),A=v.sync(y),k=0;for(let L of y){let F=p.get(L.shell_pid),B=v.get(L.shell_pid)?.tab_name??L.tab_name,Z=!!B&&!ce(B)&&!ie(B)?B:L.tab_name;F!==void 0&&F!==Z&&(k+=ip(L.shell_pid,Z))}let x=E.filter(L=>L.ownership==="rejected").length;x>0&&console.log(`[terminal/sync] dropped ${x} tab_name update(s), pid(s) owned by a different extension instance`);let N=await VT(),I={resolved:0,expired:0};try{I=Dl()}catch{}let C={rebound:0,ghosts:0,ambiguous:0};try{C=Ml()}catch{}return ly(),i.json({ok:!0,count:v.size(),diff:A,propagated:k,live_sweep:N,deferred_resolved:I,rebound:C})}),t.get("/api/terminal/registry",i=>i.json({terminals:v.all(),count:v.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=v.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=v.all().find(_=>v.sessionsFor(_.shell_pid).includes(l)),g=[];if(!p){let _=f().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(/\/+$/,""),A=300*1e3;for(let k of v.all()){if(!k.cwd||k.cwd.replace(/\/+$/,"")!==y||ce(k.tab_name))continue;let x=Date.parse(k.opened_at),N=Date.parse(k.last_seen_at);!Number.isFinite(x)||!Number.isFinite(N)||x>E||N+A<E||g.push({shell_pid:k.shell_pid,tab_name:k.tab_name,cwd:k.cwd,opened_at:k.opened_at,last_seen_at:k.last_seen_at,reason:"time-overlap"})}g.sort((k,x)=>Date.parse(x.last_seen_at)-Date.parse(k.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(Ae(l))return i.json({applied:!1,reason:"has-alias"});if(v.all().some(y=>v.sessionsFor(y.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=f().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=v.all().filter(y=>y.cwd&&y.cwd.replace(/\/+$/,"")===_&&!ce(y.tab_name));if(E.length===1){let y=E[0],A=$t({sessionStartedAt:g.started_at??null,terminalOpenedAt:y.opened_at??null});if(!A.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:A.reason});let k=v.getOrigin(l),x=ut({tabName:y.tab_name,origin:k??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return x?(be(l,x),v.linkSession(l,y.shell_pid),i.json({applied:!0,alias:x,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 jl(l);if(y){let k=v.get(y.shell_pid),x=$t({sessionStartedAt:g.started_at??null,terminalOpenedAt:k?.opened_at??null});if(!x.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:x.reason});let N=v.getOrigin(l),I=ut({tabName:y.tab_name,origin:N??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(I)return be(l,I),v.linkSession(l,y.shell_pid),i.json({applied:!0,alias:I,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,matched_fingerprints:y.matched_fingerprints,method:"content-match"})}let A=6e4;if(g.started_at){let k=Date.parse(g.started_at);if(Number.isFinite(k)){let x=E.filter(I=>$t({sessionStartedAt:g.started_at,terminalOpenedAt:I.opened_at??null}).allowed).map(I=>({t:I,gap:k-Date.parse(I.opened_at??"")})).filter(I=>Number.isFinite(I.gap)&&I.gap>=0&&I.gap<=A);if(x.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:x.length});let N=x[0];if(N){let I=v.getOrigin(l),C=ut({tabName:N.t.tab_name,origin:I??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(C)return be(l,C),v.linkSession(l,N.t.shell_pid),i.json({applied:!0,alias:C,linked_pid:N.t.shell_pid,linked_tab_name:N.t.tab_name,method:"closest-before-temporal",gap_ms:N.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 v.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=v.get(p.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let _=v.getOrigin(l),E=f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),y=null,A=g.tab_name?.trim()??"";if(A&&!ce(A)&&!ie(A))y=A;else if(A&&ie(A)){let k=lt(A);k&&!ce(k)&&(y=k)}return y?(v.unlinkSession(l),be(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=f().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!p?.file_path)return i.json({error:"session not found"},404);v.unlinkSession(l),Is(l),await an(p.file_path);let g=Ae(l);return i.json({ok:!0,alias:g,linked_pid:v.all().find(_=>v.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 _=f(),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 k=_.prepare(`SELECT content_text FROM messages
1957
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(p),N=uc(y,k,{mode:g,includeSidechain:_,prelude:E});return i.text(N)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:Sc(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),p=He(l);if(!p)return i.json({error:"not found"},404);let g=Tc(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=Vt({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=wc(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=Rc(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=kc(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=Zt(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=Ac(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:yc(l)})});let R=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:Cc()})),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||!R.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let p=zr({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=Ic(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=vc(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:en({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let p=Mc(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 jc(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=tn();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)),_=en({includeDismissed:!1}).find(y=>y.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=Oc(_.type,_.pattern,p);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(Dc());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Qr({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),p=oe(l);if(!p)return i.json({error:"thread not found"},404);let g=p.edges.map(_=>({..._,alias_source:_.alias==null?null:v.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=nn({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&&Jc(l,p.name),p.close&&Gc(l),p.reopen&&Yc(l),p.archive&&zc(l),"folder_id"in p&&_l(l,p.folder_id??null);let g=oe(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:no()})),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=ll({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=dl(l,p.name)),"parent_folder_id"in p&&(g=pl(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 gl(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 ml(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=rn({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=Xc(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 _=Qt(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=Kc(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=Vc({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:qc(l)})});let j=M.object({enabled:M.boolean(),band_lo:M.number().min(0).max(1).optional(),band_hi:M.number().min(0).max(1).optional(),model:M.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),x=M.object({project:M.string().min(1),threshold:M.number().min(0).max(1).optional(),model:M.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:j});t.post("/api/threads/scan/preflight",async i=>{let l=Ce(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 _=_d({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 q=M.object({project:M.string().min(1),threshold:M.number().min(0).max(1).optional(),llm_names:M.boolean().optional(),model:M.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:j});t.post("/api/threads/scan/apply",async i=>{let l=Ce(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=q.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=hd({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(!Fo(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ye(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let k of Ed(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=Fo(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=Ce(i);return l||(bd(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let D=M.object({project_id:M.number().int().positive(),mode:M.enum(["preflight","apply"]),window_hours:M.number().min(.5).max(168).optional(),score_threshold:M.number().min(0).max(1).optional(),use_live_pids:M.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),p=D.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=await il(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 _=al(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=oe(l);if(!p)return i.json({error:"thread not found"},404);let g=f(),_=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=oe(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 _=Fe(),E=p.model??_.model,y=ql({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(!uo(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ye(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let k of Xl(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=uo(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>Jl(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=v.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:v.size()});let g=v.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:v.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=v.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=v.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let _=jp(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=v.remove(l.shell_pid);return i.json({ok:!0,removed:p,count:v.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):(v.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:v.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 v.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 O of v.all())p.set(O.shell_pid,O.tab_name);let g=l.terminals.filter(O=>!!O&&typeof O.shell_pid=="number"&&typeof O.tab_name=="string").map(O=>({shell_pid:O.shell_pid,tab_name:O.tab_name,cwd:O.cwd??null,opened_at:O.opened_at??new Date().toISOString()})),_=l.extension_instance_id??null,E=[],y=g.filter(O=>{let F=v.claimPidOwnership(O.shell_pid,_);return E.push({shell_pid:O.shell_pid,ownership:F}),F!=="rejected"}),k=v.sync(y),N=0;for(let O of y){let F=p.get(O.shell_pid),W=v.get(O.shell_pid)?.tab_name??O.tab_name,G=!!W&&!de(W)&&!ce(W)?W:O.tab_name;F!==void 0&&F!==G&&(N+=jp(O.shell_pid,G))}let A=E.filter(O=>O.ownership==="rejected").length;A>0&&console.log(`[terminal/sync] dropped ${A} tab_name update(s), pid(s) owned by a different extension instance`);let L=await Py(),C={resolved:0,expired:0};try{C=nu()}catch{}let I={rebound:0,ghosts:0,ambiguous:0};try{I=su()}catch{}return Ky(),i.json({ok:!0,count:v.size(),diff:k,propagated:N,live_sweep:L,deferred_resolved:C,rebound:I})}),t.get("/api/terminal/registry",i=>i.json({terminals:v.all(),count:v.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=v.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=v.all().find(_=>v.sessionsFor(_.shell_pid).includes(l)),g=[];if(!p){let _=f().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 N of v.all()){if(!N.cwd||N.cwd.replace(/\/+$/,"")!==y||de(N.tab_name))continue;let A=Date.parse(N.opened_at),L=Date.parse(N.last_seen_at);!Number.isFinite(A)||!Number.isFinite(L)||A>E||L+k<E||g.push({shell_pid:N.shell_pid,tab_name:N.tab_name,cwd:N.cwd,opened_at:N.opened_at,last_seen_at:N.last_seen_at,reason:"time-overlap"})}g.sort((N,A)=>Date.parse(A.last_seen_at)-Date.parse(N.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(Te(l))return i.json({applied:!1,reason:"has-alias"});if(v.all().some(y=>v.sessionsFor(y.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=f().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=v.all().filter(y=>y.cwd&&y.cwd.replace(/\/+$/,"")===_&&!de(y.tab_name));if(E.length===1){let y=E[0],k=Kt({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 N=v.getOrigin(l),A=wt({tabName:y.tab_name,origin:N??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return A?(he(l,A),v.linkSession(l,y.shell_pid),i.json({applied:!0,alias:A,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 tu(l);if(y){let N=v.get(y.shell_pid),A=Kt({sessionStartedAt:g.started_at??null,terminalOpenedAt:N?.opened_at??null});if(!A.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:A.reason});let L=v.getOrigin(l),C=wt({tabName:y.tab_name,origin:L??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(C)return he(l,C),v.linkSession(l,y.shell_pid),i.json({applied:!0,alias:C,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 N=Date.parse(g.started_at);if(Number.isFinite(N)){let A=E.filter(C=>Kt({sessionStartedAt:g.started_at,terminalOpenedAt:C.opened_at??null}).allowed).map(C=>({t:C,gap:N-Date.parse(C.opened_at??"")})).filter(C=>Number.isFinite(C.gap)&&C.gap>=0&&C.gap<=k);if(A.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:A.length});let L=A[0];if(L){let C=v.getOrigin(l),I=wt({tabName:L.t.tab_name,origin:C??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(I)return he(l,I),v.linkSession(l,L.t.shell_pid),i.json({applied:!0,alias:I,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 v.unlinkSession(l),Xs(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=v.get(p.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let _=v.getOrigin(l),E=f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),y=null,k=g.tab_name?.trim()??"";if(k&&!de(k)&&!ce(k))y=k;else if(k&&ce(k)){let N=yt(k);N&&!de(N)&&(y=N)}return y?(v.unlinkSession(l),he(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=f().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!p?.file_path)return i.json({error:"session not found"},404);v.unlinkSession(l),Xs(l),await bn(p.file_path);let g=Te(l);return i.json({ok:!0,alias:g,linked_pid:v.all().find(_=>v.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 _=f(),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 N=_.prepare(`SELECT content_text FROM messages
1935
1958
  WHERE session_id = ? AND rowid > ?
1936
- ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let x of k){let N=x.content_text??"";if(N.includes("**Tool result**")&&N.includes(g))return i.json({source:"tool-result",content:N});if(/^\s*1\t/.test(N)&&N.length>200)return i.json({source:"tool-result",content:N})}try{let x=await YT(g),N=zT();if(!x.startsWith(N+"/")&&!x.startsWith(N+"\\"))return i.json({error:"path outside allowed root"},403);let I=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],L=x.slice(N.length+1).split("/")[0].split("\\")[0];if(I.includes(L))return i.json({error:"path inside sensitive directory"},403);let F=await XT(x),B=2*1024*1024;if(F.size>B)return i.json({error:"file too large",size:F.size,max:B},413);let ee=await JT(x,"utf8");return i.json({source:"disk",content:ee})}catch(x){return i.json({source:"missing",error:x.message})}}),t.get("/api/projects/:name/stats",i=>{let l=f(),p=i.req.param("name"),g=l.prepare(`SELECT
1959
+ ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let A of N){let L=A.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 A=await jy(g),L=Dy();if(!A.startsWith(L+"/")&&!A.startsWith(L+"\\"))return i.json({error:"path outside allowed root"},403);let C=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],O=A.slice(L.length+1).split("/")[0].split("\\")[0];if(C.includes(O))return i.json({error:"path inside sensitive directory"},403);let F=await Iy(A),W=2*1024*1024;if(F.size>W)return i.json({error:"file too large",size:F.size,max:W},413);let K=await vy(A,"utf8");return i.json({source:"disk",content:K})}catch(A){return i.json({source:"missing",error:A.message})}}),t.get("/api/projects/:name/stats",i=>{let l=f(),p=i.req.param("name"),g=l.prepare(`SELECT
1937
1960
  (SELECT COUNT(*) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=? AND s.message_count > 2) AS sessions,
1938
1961
  (SELECT COALESCE(SUM(s.message_count), 0) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=?) AS messages,
1939
1962
  (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,
@@ -1941,7 +1964,7 @@ ${o}
1941
1964
  JOIN projects p ON p.id = s.project_id
1942
1965
  WHERE p.name = ? AND s.git_branch IS NOT NULL
1943
1966
  ORDER BY s.git_branch
1944
- LIMIT 20`).all(p).map(E=>E.git_branch);return i.json({...g,branches:_})});function W(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function oe(i){if(!e)return i;let l=`<meta name="recall-token" content="${W(e)}" />`,p=i.indexOf("</head>");return p!==-1?i.slice(0,p)+l+i.slice(p):l+i}function $(i){let l=cp();return i.html(Wa({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:ap}))}function se(){try{return Yo(up,"utf8")}catch{return null}}return ry?(t.use("/assets/*",op({root:zo})),t.get("/favicon.svg",op({root:zo})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=se();return l===null?$(i):i.html(oe(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=se();return l===null?$(i):i.html(oe(l))})):t.get("/",i=>$(i)),t}function py(){if(wn(),!!Ge().heuristicEnabled){try{let{updated:e}=fl();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}=hl();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}=El();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}=bl();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 mp(e,t){let s=dy(t);return new Promise((n,r)=>{try{let o=WT({fetch:s.fetch,port:e,hostname:"127.0.0.1"},()=>{n(o),setImmediate(()=>{try{py()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}Q();H();import{watch as my}from"chokidar";import{readdirSync as gy,statSync as Qo}from"node:fs";import{basename as _y,join as fy}from"node:path";var hy=1500,Zo=new Map;function Ey(e){let t=e.split("/"),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}async function _p(e){let t=0;try{t=Qo(e).mtimeMs}catch{return}let s=Ey(e);if(!s)return;let n=f(),r=n.prepare("SELECT id, file_mtime FROM sessions WHERE file_path = ? LIMIT 1").get(e);if(r&&r.file_mtime>=t)return;let o=new Map,a=null;for await(let w of bd(e)){let R=o.get(w.sessionId);if(R||(R={sessionId:w.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},o.set(w.sessionId,R)),R.entries.push(w),w.timestamp&&((!R.earliest||w.timestamp<R.earliest)&&(R.earliest=w.timestamp),(!R.latest||w.timestamp>R.latest)&&(R.latest=w.timestamp)),w.role==="user"&&!w.isSidechain){if(R.users+=1,!R.firstUser&&w.contentText){let D=w.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();D&&!/^<local-command-caveat>/.test(D)&&(R.firstUser=_e(D).redacted.slice(0,2e3))}}else w.role==="assistant"&&!w.isSidechain&&(R.assistants+=1);!R.cwd&&w.cwd&&(R.cwd=w.cwd),!R.branch&&w.gitBranch&&(R.branch=w.gitBranch),!R.version&&w.version&&(R.version=w.version),!a&&w.cwd&&(a=w.cwd)}let c=a?_y(a)||a:s,u=a??s.replace(/^-/,"/").replace(/-/g,"/"),d=n.prepare(`INSERT INTO projects (encoded_path, decoded_path, name)
1967
+ LIMIT 20`).all(p).map(E=>E.git_branch);return i.json({...g,branches:_})});function H(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function ie(i){if(!e)return i;let l=`<meta name="recall-token" content="${H(e)}" />`,p=i.indexOf("</head>");return p!==-1?i.slice(0,p)+l+i.slice(p):l+i}function $(i){let l=Dp();return i.html(lc({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:Mp}))}function se(){try{return li(Pp,"utf8")}catch{return null}}return Xy?(t.use("/assets/*",vp({root:di})),t.get("/favicon.svg",vp({root:di})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=se();return l===null?$(i):i.html(ie(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=se();return l===null?$(i):i.html(ie(l))})):t.get("/",i=>$(i)),t}function Qy(){if(Mn(),!!it().heuristicEnabled){try{let{updated:e}=Fl();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}=Pl();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}=Ul();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}=$l();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 Hp(e,t){let s=Zy(t);return new Promise((n,r)=>{try{let o=Ly({fetch:s.fetch,port:e,hostname:"127.0.0.1"},()=>{n(o),setImmediate(()=>{try{Qy()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}ee();U();import{watch as tw}from"chokidar";import{readdirSync as sw,statSync as fi}from"node:fs";import{basename as nw,join as rw}from"node:path";var ew=/^[ \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(ew);if(!t)return{alias:null,stripped:e};let s=t[1].trim();return s?{alias:s.length>200?s.slice(0,200):s,stripped:e.slice(t[0].length)}:{alias:null,stripped:e.slice(t[0].length)}}var ow=1500,_i=new Map;function iw(e){let t=e.split("/"),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}async function Wp(e){let t=0;try{t=fi(e).mtimeMs}catch{return}let s=iw(e);if(!s)return;let n=f(),r=n.prepare("SELECT id, file_mtime FROM sessions WHERE file_path = ? LIMIT 1").get(e);if(r&&r.file_mtime>=t)return;let o=new Map,a=null;for await(let w of $d(e)){let R=o.get(w.sessionId);if(R||(R={sessionId:w.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},o.set(w.sessionId,R)),R.entries.push(w),w.timestamp&&((!R.earliest||w.timestamp<R.earliest)&&(R.earliest=w.timestamp),(!R.latest||w.timestamp>R.latest)&&(R.latest=w.timestamp)),w.role==="user"&&!w.isSidechain){if(R.users+=1,!R.firstUser&&w.contentText){let j=w.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();j&&!/^<local-command-caveat>/.test(j)&&(R.firstUser=Se(j).redacted.slice(0,2e3))}}else w.role==="assistant"&&!w.isSidechain&&(R.assistants+=1);!R.cwd&&w.cwd&&(R.cwd=w.cwd),!R.branch&&w.gitBranch&&(R.branch=w.gitBranch),!R.version&&w.version&&(R.version=w.version),!a&&w.cwd&&(a=w.cwd)}let c=a?nw(a)||a:s,u=a??s.replace(/^-/,"/").replace(/-/g,"/"),d=n.prepare(`INSERT INTO projects (encoded_path, decoded_path, name)
1945
1968
  VALUES (?, ?, ?)
1946
1969
  ON CONFLICT(encoded_path) DO UPDATE SET decoded_path = excluded.decoded_path, name = excluded.name
1947
1970
  RETURNING id`),m=n.prepare(`
@@ -1972,4 +1995,4 @@ ${o}
1972
1995
  VALUES (@uuid, @session_id, @parent_uuid, @type, @role, @timestamp,
1973
1996
  @is_sidechain, @content_text, @tool_names, @raw_json)
1974
1997
  ON CONFLICT(uuid) DO NOTHING
1975
- `),b=n.prepare("DELETE FROM messages WHERE session_id = ?"),T=new Date().toISOString();if(n.transaction(()=>{let{id:w}=d.get(s,u,c);for(let R of o.values()){m.run({id:R.sessionId,project_id:w,file_path:e,file_mtime:t,started_at:R.earliest,ended_at:R.latest,message_count:R.entries.length,user_message_count:R.users,assistant_message_count:R.assistants,first_user_message:R.firstUser,cwd:R.cwd,git_branch:R.branch,version:R.version,indexed_at:T}),b.run(R.sessionId);for(let D of R.entries)h.run(by(D));Sd(n,R.sessionId,R.entries),xn(n,R.sessionId)}})(),Ge().heuristicEnabled)for(let w of o.values()){let R=ct(w.firstUser);R&&me(w.sessionId,R,"heuristic")}}function by(e){let t=_e(e.contentText).redacted,s=_e(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:s}}function gp(e){let t=Zo.get(e);t?.timer&&clearTimeout(t.timer);let s={timer:null};s.timer=setTimeout(()=>{Zo.delete(e),_p(e).then(async()=>{an(e);try{let r=f().prepare("SELECT id FROM sessions WHERE file_path = ?").all(e);for(let o of r){Ea(o.id),jd(o.id);try{uc(o.id)}catch(a){console.error("[watcher] auto-collections apply failed:",a)}}}catch(n){console.error("[watcher] semantic dispatch failed:",n)}}).catch(n=>{console.error(`[watcher] reindex failed for ${e}:`,n)})},hy),Zo.set(e,s)}function fp(){let e=my(Fn,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:t=>{if(t.endsWith(".jsonl"))return!1;try{if(Qo(t).isDirectory())return!1}catch{}return!0}});return e.on("add",t=>t.endsWith(".jsonl")&&gp(t)),e.on("change",t=>t.endsWith(".jsonl")&&gp(t)),e}var Sy=4;function*hp(e){let t;try{t=gy(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let s of t){let n=fy(e,s.name);s.isDirectory()?yield*hp(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}async function Ep(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},s=f(),n=[],r=s.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1");for(let d of hp(Fn)){if(t.scanned+=1,d.includes("/subagents/")){t.skipped+=1;continue}let m;try{m=Qo(d).mtimeMs}catch{t.errors+=1;continue}let h=r.get(d);if(h&&h.file_mtime>=m){t.upToDate+=1;continue}n.push(d)}if(n.length===0)return t.durationMs=Date.now()-e,t;let o=n.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await _p(d),t.reindexed+=1}catch(m){t.errors+=1;let h=m instanceof Error?m.message:String(m);console.error(`[ingestion-sweep] failed for ${d}: ${h}`)}}},c=Math.min(Sy,n.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}import{createServer as Sp}from"node:net";function bp(e){return new Promise(t=>{let s=Sp();s.once("error",()=>t(!1)),s.once("listening",()=>{s.close(()=>t(!0))}),s.listen(e,"127.0.0.1")})}async function Tp(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await bp(t))return t;for(let s=0;s<50;s++){let n=49152+Math.floor(Math.random()*16383);if(!e.has(n)&&await bp(n))return n}return new Promise((s,n)=>{let r=Sp();r.once("error",n),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>s(a))}else r.close(),n(new Error("could not determine a free port"))})})}Q();import{existsSync as Ry,readFileSync as sC,writeFileSync as wp,unlinkSync as Ay}from"node:fs";import{join as ti}from"node:path";Q();import{randomBytes as Ty}from"node:crypto";import{writeFileSync as yy,readFileSync as KL,existsSync as VL}from"node:fs";import{join as wy}from"node:path";var ei=wy(U,"daemon.token");function yp(){J();let e=Ty(32).toString("hex");return yy(ei,e,{encoding:"utf8",mode:384}),e}var Rp=ti(U,"daemon.pid"),Ap=ti(U,"daemon.port"),iC=ti(U,"daemon.log");function kp(e){J(),wp(Rp,JSON.stringify(e),{encoding:"utf8",mode:384}),wp(Ap,String(e.port),{encoding:"utf8",mode:384})}function si(){for(let e of[Rp,Ap,ei])if(Ry(e))try{Ay(e)}catch{}}_r();var ky=360*60*1e3,xy=60*1e3,Ny=1440*60*1e3,Oy=300*1e3,Ly=300*1e3,Cy=10*1e3,vy=1440*60*1e3,Iy=30*1e3,jy=500,My=1500;async function Dy(){let e=await Tp(),t=yp(),s=await mp(e,t);kp({pid:process.pid,port:e,startedAt:new Date().toISOString()}),or(ae().enabled);try{Ut();let O=Oa();O>0&&console.log(`[daemon] archive: migrated ${O} hot row(s) into archive.sqlite`)}catch(O){let Y=O instanceof Error?O.message:String(O);console.error(`[daemon] archive migration failed: ${Y}`)}let n=fp(),r=()=>{try{Ws()}catch(O){console.error("[daemon] suggestion scan failed:",O)}},o=setTimeout(r,xy),a=setInterval(r,ky),c=()=>{try{let O=v.reapStaleLinks();(O.pruned_pids||O.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${O.pruned_pids} pid${O.pruned_pids===1?"":"s"}, ${O.pruned_sessions} session link${O.pruned_sessions===1?"":"s"}`)}catch(O){console.error("[daemon] stale-link reaper failed:",O)}},u=setTimeout(c,Oy),d=setInterval(c,Ny),m=()=>{try{let O=v.gcDeadPids();(O.pruned_pids||O.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${O.pruned_pids} pid${O.pruned_pids===1?"":"s"}, ${O.pruned_sessions} session link${O.pruned_sessions===1?"":"s"}`)}catch(O){console.error("[daemon] dead-pid gc failed:",O)}},h=setTimeout(m,Cy),b=setInterval(m,Ly),T=()=>{gi().then(O=>{O.ran&&O.revoked&&console.log(`[daemon] license check: REVOKED${O.reason?` (${O.reason})`:""}`)}).catch(O=>{console.error("[daemon] license check failed:",O)})},S=setTimeout(T,Iy),w=setInterval(T,vy),R=Ha(),D=O=>{console.log(`[daemon] received ${O}, shutting down`),clearTimeout(o),clearInterval(a),clearTimeout(u),clearInterval(d),clearTimeout(h),clearInterval(b),clearTimeout(S),clearInterval(w),R.stop(),n.close(),s.close(),si(),process.exit(0)};process.on("SIGTERM",()=>D("SIGTERM")),process.on("SIGINT",()=>D("SIGINT")),process.on("SIGHUP",()=>D("SIGHUP")),console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{cn().then(O=>{console.log(`[daemon] boot sweep: scanned ${O.scanned} live claude(s), linked ${O.linked}, renamed ${O.renamed}, ambiguous_cwd ${O.ambiguous_cwd}`)}).catch(O=>{console.error("[daemon] boot sweep failed:",O)})},jy),setTimeout(()=>{Ep().then(O=>{console.log(`[daemon] ingestion sweep: scanned=${O.scanned} reindexed=${O.reindexed} up-to-date=${O.upToDate} skipped=${O.skipped} errors=${O.errors} (${O.durationMs}ms)`)}).catch(O=>{console.error("[daemon] ingestion sweep failed:",O)})},My)}Dy().catch(e=>{console.error("[daemon] fatal:",e),si(),process.exit(1)});
1998
+ `),b=n.prepare("DELETE FROM messages WHERE session_id = ?"),T=new Date().toISOString();if(n.transaction(()=>{let{id:w}=d.get(s,u,c);for(let R of o.values()){if(Ol(R.firstUser)){console.log(`[watcher] skipping daemon-spawn phantom ${R.sessionId} (first message matches autonomous-spawn pattern)`);continue}let j=gi(R.firstUser),x=j.alias?j.stripped:R.firstUser;if(m.run({id:R.sessionId,project_id:w,file_path:e,file_mtime:t,started_at:R.earliest,ended_at:R.latest,message_count:R.entries.length,user_message_count:R.users,assistant_message_count:R.assistants,first_user_message:x,cwd:R.cwd,git_branch:R.branch,version:R.version,indexed_at:T}),j.alias&&!Te(R.sessionId))try{he(R.sessionId,j.alias)}catch(q){console.error(`[watcher] header-alias setAlias failed for ${R.sessionId}:`,q)}b.run(R.sessionId);for(let q of R.entries)h.run(aw(q));Hd(n,R.sessionId,R.entries),Un(n,R.sessionId)}})(),it().heuristicEnabled)for(let w of o.values()){let R=gi(w.firstUser).stripped,j=Tt(R);j&&Ee(w.sessionId,j,"heuristic")}}function aw(e){let t=Se(e.contentText).redacted,s=Se(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:s}}function Bp(e){let t=_i.get(e);t?.timer&&clearTimeout(t.timer);let s={timer:null};s.timer=setTimeout(()=>{_i.delete(e),Wp(e).then(async()=>{bn(e);try{let r=f().prepare("SELECT id FROM sessions WHERE file_path = ?").all(e);for(let o of r){Pa(o.id),tp(o.id);try{Lc(o.id)}catch(a){console.error("[watcher] auto-collections apply failed:",a)}}}catch(n){console.error("[watcher] semantic dispatch failed:",n)}}).catch(n=>{console.error(`[watcher] reindex failed for ${e}:`,n)})},ow),_i.set(e,s)}function qp(){let e=tw(Kn,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:t=>{if(t.endsWith(".jsonl"))return!1;try{if(fi(t).isDirectory())return!1}catch{}return!0}});return e.on("add",t=>t.endsWith(".jsonl")&&Bp(t)),e.on("change",t=>t.endsWith(".jsonl")&&Bp(t)),e}var cw=4;function*Xp(e){let t;try{t=sw(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let s of t){let n=rw(e,s.name);s.isDirectory()?yield*Xp(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}async function Jp(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},s=f(),n=[],r=s.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1");for(let d of Xp(Kn)){if(t.scanned+=1,d.includes("/subagents/")){t.skipped+=1;continue}let m;try{m=fi(d).mtimeMs}catch{t.errors+=1;continue}let h=r.get(d);if(h&&h.file_mtime>=m){t.upToDate+=1;continue}n.push(d)}if(n.length===0)return t.durationMs=Date.now()-e,t;let o=n.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await Wp(d),t.reindexed+=1}catch(m){t.errors+=1;let h=m instanceof Error?m.message:String(m);console.error(`[ingestion-sweep] failed for ${d}: ${h}`)}}},c=Math.min(cw,n.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}import{createServer as Yp}from"node:net";function Gp(e){return new Promise(t=>{let s=Yp();s.once("error",()=>t(!1)),s.once("listening",()=>{s.close(()=>t(!0))}),s.listen(e,"127.0.0.1")})}async function zp(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await Gp(t))return t;for(let s=0;s<50;s++){let n=49152+Math.floor(Math.random()*16383);if(!e.has(n)&&await Gp(n))return n}return new Promise((s,n)=>{let r=Yp();r.once("error",n),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>s(a))}else r.close(),n(new Error("could not determine a free port"))})})}ee();import{existsSync as pw,readFileSync as nI,writeFileSync as Vp,unlinkSync as mw}from"node:fs";import{join as Ei}from"node:path";ee();import{randomBytes as lw}from"node:crypto";import{writeFileSync as uw,readFileSync as VC,existsSync as ZC}from"node:fs";import{join as dw}from"node:path";var hi=dw(B,"daemon.token");function Kp(){z();let e=lw(32).toString("hex");return uw(hi,e,{encoding:"utf8",mode:384}),e}var Zp=Ei(B,"daemon.pid"),Qp=Ei(B,"daemon.port"),aI=Ei(B,"daemon.log");function em(e){z(),Vp(Zp,JSON.stringify(e),{encoding:"utf8",mode:384}),Vp(Qp,String(e.port),{encoding:"utf8",mode:384})}function bi(){for(let e of[Zp,Qp,hi])if(pw(e))try{mw(e)}catch{}}Lr();U();Ve();var _w=Math.max(1,gw().length),tm=String(Math.max(2,Math.floor(_w/2)));process.env.OMP_NUM_THREADS||(process.env.OMP_NUM_THREADS=tm);process.env.ORT_NUM_THREADS||(process.env.ORT_NUM_THREADS=tm);var fw=360*60*1e3,hw=60*1e3,Ew=1440*60*1e3,bw=300*1e3,Sw=300*1e3,Tw=10*1e3,yw=1440*60*1e3,ww=30*1e3,Rw=500,kw=1500;async function Aw(){let e=await zp(),t=Kp();(!t||t.length<32)&&(console.error("[daemon] FATAL: daemon token init returned empty or undersized \u2014 refusing to start"),process.exit(1));let s=await Hp(e,t);em({pid:process.pid,port:e,startedAt:new Date().toISOString()}),Sr(ae().enabled);try{zt();let x=za();x>0&&console.log(`[daemon] archive: migrated ${x} hot row(s) into archive.sqlite`)}catch(x){let q=x instanceof Error?x.message:String(x);console.error(`[daemon] archive migration failed: ${q}`)}let n=qp(),r=()=>{try{tn()}catch(x){console.error("[daemon] suggestion scan failed:",x)}},o=setTimeout(r,hw),a=setInterval(r,fw),c=()=>{try{let x=v.reapStaleLinks();(x.pruned_pids||x.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${x.pruned_pids} pid${x.pruned_pids===1?"":"s"}, ${x.pruned_sessions} session link${x.pruned_sessions===1?"":"s"}`)}catch(x){console.error("[daemon] stale-link reaper failed:",x)}},u=setTimeout(c,bw),d=setInterval(c,Ew),m=()=>{try{let x=v.gcDeadPids();(x.pruned_pids||x.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${x.pruned_pids} pid${x.pruned_pids===1?"":"s"}, ${x.pruned_sessions} session link${x.pruned_sessions===1?"":"s"}`)}catch(x){console.error("[daemon] dead-pid gc failed:",x)}},h=setTimeout(m,Tw),b=setInterval(m,Sw),T=()=>{Ci().then(x=>{x.ran&&x.revoked&&console.log(`[daemon] license check: REVOKED${x.reason?` (${x.reason})`:""}`)}).catch(x=>{console.error("[daemon] license check failed:",x)})},S=setTimeout(T,ww),w=setInterval(T,yw),R=cc();(async()=>{try{if(!Ge())return;let x=f().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get();if(x.n===0)return;if(!ae().autoResumeWorker){console.log(`[daemon] vector-worker dormant: ${x.n} chunk(s) pending in queue. Run \`recall vectorize\` or click Start in the web UI to drain. To restore auto-resume, set semantic.autoResumeWorker=true in ~/.recall/config.json.`);return}let D=await Ke();if(D.tier!=="pro"){console.log(`[daemon] vector-worker auto-resume skipped: ${x.n} chunk(s) pending but license tier is ${D.tier}`);return}_e().loaded||await $e(),ve().running||(Ss(),console.log(`[daemon] vector-worker auto-resumed: ${x.n} chunk(s) pending`))}catch(x){let q=x instanceof Error?x.message:String(x);console.error(`[daemon] vector-worker auto-resume failed: ${q}`)}})();let j=x=>{console.log(`[daemon] received ${x}, shutting down`),clearTimeout(o),clearInterval(a),clearTimeout(u),clearInterval(d),clearTimeout(h),clearInterval(b),clearTimeout(S),clearInterval(w),R.stop(),n.close(),s.close(),bi(),process.exit(0)};process.on("SIGTERM",()=>j("SIGTERM")),process.on("SIGINT",()=>j("SIGINT")),process.on("SIGHUP",()=>j("SIGHUP")),console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{Sn().then(x=>{console.log(`[daemon] boot sweep: scanned ${x.scanned} live claude(s), linked ${x.linked}, renamed ${x.renamed}, ambiguous_cwd ${x.ambiguous_cwd}`)}).catch(x=>{console.error("[daemon] boot sweep failed:",x)})},Rw),setTimeout(()=>{Jp().then(x=>{console.log(`[daemon] ingestion sweep: scanned=${x.scanned} reindexed=${x.reindexed} up-to-date=${x.upToDate} skipped=${x.skipped} errors=${x.errors} (${x.durationMs}ms)`)}).catch(x=>{console.error("[daemon] ingestion sweep failed:",x)})},kw)}Aw().catch(e=>{console.error("[daemon] fatal:",e),bi(),process.exit(1)});