@clauderecallhq/cli 0.93.0 → 0.95.4

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 Vg=Object.defineProperty;var re=(e,t)=>()=>(e&&(t=e(e=0)),t);var _t=(e,t)=>{for(var n in t)Vg(e,n,{get:t[n],enumerable:!0})};import{homedir as Ca}from"node:os";import{join as Pr,basename as kA}from"node:path";import{existsSync as Qg,mkdirSync as Zg,chmodSync as ef,readdirSync as xA,statSync as NA}from"node:fs";function V(){Qg(W)||Zg(W,{recursive:!0,mode:448}),process.platform!=="win32"&&ef(W,448)}var nn,W,st,ne=re(()=>{"use strict";nn=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Pr(Ca(),".claude","projects"),W=process.env.RECALL_HOME?process.env.RECALL_HOME:Pr(Ca(),".recall"),st=Pr(W,"db.sqlite")});import{createRequire as kf}from"node:module";var Af,xf,Nf,Ur,Hr,Kn,Br=re(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...n){let s=n[0];return t==="warning"&&s instanceof Error&&s.name==="ExperimentalWarning"&&/SQLite/i.test(s.message)?!1:e(t,...n)}}Af=kf(import.meta.url),xf=["node","sqlite"].join(":"),Nf=Af(xf),Ur=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let n=t.length===0?this.inner.run():this.inner.run(...t);return{changes:n.changes,lastInsertRowid:n.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},Hr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new Nf.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new Ur(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,n={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(n.simple){let s=this.inner.prepare(`PRAGMA ${t}`).get();return s&&typeof s=="object"?Object.values(s)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...s)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...s);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,n){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),n===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,n)}},Kn=Hr});function qa(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(k=>k.name)),s=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[k,M]of s)n.has(k)||e.exec(`ALTER TABLE sessions ADD COLUMN ${k} ${M}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(k=>k.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[k,M]of a)o.has(k)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${k} ${M}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(k=>k.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[k,M]of d)u.has(k)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${k} ${M}`);let p=e.prepare("PRAGMA table_info(threads)").all();new Set(p.map(k=>k.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let f=e.prepare("PRAGMA table_info(thread_folders)").all(),S=new Set(f.map(k=>k.name));S.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)")),S.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
3
+ var Lf=Object.defineProperty;var se=(e,t)=>()=>(e&&(t=e(e=0)),t);var St=(e,t)=>{for(var n in t)Lf(e,n,{get:t[n],enumerable:!0})};import{homedir as qa}from"node:os";import{join as qr,basename as dx}from"node:path";import{existsSync as Cf,mkdirSync as vf,chmodSync as If,readdirSync as mx,statSync as gx}from"node:fs";function K(){Cf(W)||vf(W,{recursive:!0,mode:448}),process.platform!=="win32"&&If(W,448)}var on,W,ze,Z=se(()=>{"use strict";on=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:qr(qa(),".claude","projects"),W=process.env.RECALL_HOME?process.env.RECALL_HOME:qr(qa(),".recall"),ze=qr(W,"db.sqlite")});import{createRequire as n_}from"node:module";var s_,r_,o_,Gr,Yr,Zn,zr=se(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...n){let s=n[0];return t==="warning"&&s instanceof Error&&s.name==="ExperimentalWarning"&&/SQLite/i.test(s.message)?!1:e(t,...n)}}s_=n_(import.meta.url),r_=["node","sqlite"].join(":"),o_=s_(r_),Gr=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let n=t.length===0?this.inner.run():this.inner.run(...t);return{changes:n.changes,lastInsertRowid:n.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},Yr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new o_.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new Gr(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,n={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(n.simple){let s=this.inner.prepare(`PRAGMA ${t}`).get();return s&&typeof s=="object"?Object.values(s)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...s)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...s);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,n){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),n===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,n)}},Zn=Yr});function rc(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(A=>A.name)),s=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[A,I]of s)n.has(A)||e.exec(`ALTER TABLE sessions ADD COLUMN ${A} ${I}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(A=>A.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[A,I]of a)o.has(A)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${A} ${I}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(A=>A.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[A,I]of d)u.has(A)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${A} ${I}`);let p=e.prepare("PRAGMA table_info(threads)").all();new Set(p.map(A=>A.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let f=e.prepare("PRAGMA table_info(thread_folders)").all(),b=new Set(f.map(A=>A.name));b.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),b.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
4
4
  CREATE TABLE IF NOT EXISTS message_embeddings (
5
5
  message_uuid TEXT PRIMARY KEY REFERENCES messages(uuid) ON DELETE CASCADE,
6
6
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
@@ -12,7 +12,7 @@ var Vg=Object.defineProperty;var re=(e,t)=>()=>(e&&(t=e(e=0)),t);var _t=(e,t)=>{
12
12
  );
13
13
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_session ON message_embeddings(session_id);
14
14
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_generated ON message_embeddings(generated_at DESC);
15
- `);let y=e.prepare("PRAGMA table_info(projects)").all(),b=new Set(y.map(k=>k.name)),R=[["repo_root","TEXT"],["main_repo","TEXT"],["is_repo","INTEGER NOT NULL DEFAULT 0"],["is_worktree","INTEGER NOT NULL DEFAULT 0"]];for(let[k,M]of R)b.has(k)||e.exec(`ALTER TABLE projects ADD COLUMN ${k} ${M}`)}var Wa,Xa=re(()=>{"use strict";Wa=`
15
+ `);let T=e.prepare("PRAGMA table_info(projects)").all(),S=new Set(T.map(A=>A.name)),w=[["repo_root","TEXT"],["main_repo","TEXT"],["is_repo","INTEGER NOT NULL DEFAULT 0"],["is_worktree","INTEGER NOT NULL DEFAULT 0"]];for(let[A,I]of w)S.has(A)||e.exec(`ALTER TABLE projects ADD COLUMN ${A} ${I}`)}var sc,oc=se(()=>{"use strict";sc=`
16
16
  PRAGMA journal_mode = WAL;
17
17
  PRAGMA synchronous = NORMAL;
18
18
  PRAGMA foreign_keys = ON;
@@ -228,10 +228,11 @@ END;
228
228
  -- segments of messages, embedded via local bge-base-en-v1.5 (768d). The vector
229
229
  -- data is a derived cache; if lost, it is recomputable from messages.
230
230
  -- Tables stay empty on Free tier (no model downloaded, no worker started).
231
-
232
- CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
233
- embedding float[768] distance_metric=cosine
234
- );
231
+ --
232
+ -- NOTE: the vec_chunks vec0 virtual table is intentionally NOT created here.
233
+ -- It lives in src/db/vecLoader.ts (ensureVecChunksTable), loaded lazily so a
234
+ -- lean MCP child that only does FTS/metadata never instantiates the native
235
+ -- vec0 module. The daemon/worker/CLI call ensureVecChunksTable at open.
235
236
 
236
237
  CREATE TABLE IF NOT EXISTS chunk_meta (
237
238
  rowid INTEGER PRIMARY KEY,
@@ -351,9 +352,9 @@ INSERT OR IGNORE INTO app_settings(key, value) VALUES ('semantic_enabled', '0');
351
352
  -- still threw "trigger messages_vec_ai already exists". That threw all the
352
353
  -- way out of getDb(), which caused syncSemanticEnabledToDb to silently
353
354
  -- skip the gate flip \u2014 and the live (still-present) triggers fired with
354
- -- the prior semantic_enabled='1' value, enqueueing 90k+ chunk_queue rows
355
- -- in minutes on a daemon the operator had explicitly disabled. Belt and
356
- -- suspenders: drop-then-create-if-not-exists is fully idempotent.
355
+ -- the prior semantic_enabled='1' value, causing spurious chunk_queue
356
+ -- inserts even when semantic search was disabled. Belt and suspenders:
357
+ -- drop-then-create-if-not-exists is fully idempotent.
357
358
  DROP TRIGGER IF EXISTS messages_vec_ai;
358
359
  DROP TRIGGER IF EXISTS messages_vec_ad;
359
360
  DROP TRIGGER IF EXISTS messages_vec_au;
@@ -730,10 +731,31 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_target
730
731
  ON bug_synthesis_results(scope, target_id, created_at DESC);
731
732
  CREATE INDEX IF NOT EXISTS idx_synth_results_created
732
733
  ON bug_synthesis_results(created_at DESC);
733
- `});import*as Ja from"sqlite-vec";function h(){if(fe)return fe;V(),fe=new Kn(st),Ja.load(fe),fe.pragma("cache_size = -64000"),fe.pragma("mmap_size = 268435456"),fe.pragma("temp_store = MEMORY"),fe.pragma("busy_timeout = 5000"),fe.pragma("journal_size_limit = 67108864"),fe.pragma("wal_autocheckpoint = 1000"),fe.exec(Wa),qa(fe);try{fe.exec("PRAGMA optimize")}catch{}return fe}var fe,q=re(()=>{"use strict";Br();ne();Xa();fe=null});import{existsSync as Mf}from"node:fs";import{dirname as oc}from"node:path";import{fileURLToPath as jf}from"node:url";function ht(){if(zn)return zn;let e=oc(jf(import.meta.url));for(;!Mf(`${e}/package.json`);){let t=oc(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return zn=e,zn}var zn,Vn=re(()=>{"use strict";zn=null});function Df(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) - or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
734
- `)}var Qn,xe,on=re(()=>{"use strict";Qn="BAAI/bge-base-en-v1.5",xe=class extends Error{kind;path;underlying;cause;constructor(t){super(Df(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var ts={};_t(ts,{embed:()=>qf,embedQuery:()=>Xf,getEmbedderStatus:()=>Wf,loadEmbedder:()=>Bf,unloadEmbedder:()=>Jf});import{Worker as Pf}from"node:worker_threads";import{join as Ff}from"node:path";import{existsSync as $f}from"node:fs";function Uf(){return Ff(ht(),"dist","daemon","embedder-worker.js")}function ic(e){for(let t of an.values())t.reject(e);an.clear()}function Hf(){if(Ue)return Ue;let e=Uf();if(!$f(e))throw new xe({kind:"bundle-missing",detail:"embedder-worker bundle not found - run `npm run build:cli` to emit it",path:e});let t=new Pf(e);return t.on("message",n=>{let s=an.get(n.id);s&&(an.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker] thread error:",n);let s=n instanceof Error?n:new Error(String(n));ic(s),Ue=null,Ne=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),ic(new Error(`embedder worker exited with code ${n}`))),Ue=null,Ne=!1}),Ue=t,t}function Zn(e){return new Promise((t,n)=>{let s;try{s=Hf()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}an.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function es(){return qr=qr+1>>>0,String(qr)}function Jr(e){if(!e.ok)throw e.unavailable?new xe({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function Bf(){if(!(Ne&&Ue))try{Jr(await Zn({id:es(),type:"load"})),Ne=!0}catch(e){throw Ne=!1,e}}function Wf(){return{loaded:Ne,modelId:Qn,dim:768}}async function qf(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return Jr(await Zn({id:es(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function Xf(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=Jr(await Zn({id:es(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function Jf(){if(!Ue){Ne=!1;return}try{await Zn({id:es(),type:"unload"})}catch{}Ne=!1;let e=Ue;Ue=null;try{await e.terminate()}catch{}}var Ue,an,qr,Ne,ac=re(()=>{"use strict";Vn();on();Ue=null,an=new Map,qr=0,Ne=!1});var Kr={};_t(Kr,{LLAMACPP_MODEL_ID:()=>lc,MODEL_ID:()=>Qn,embed:()=>e_,embedQuery:()=>t_,getEmbedderStatus:()=>Zf,loadEmbedder:()=>Qf,unloadEmbedder:()=>n_});import{Worker as Gf}from"node:worker_threads";import{join as Yf}from"node:path";import{existsSync as Kf}from"node:fs";function zf(){return Yf(ht(),"dist","daemon","embedder-worker-llamacpp.js")}function cc(e){for(let t of cn.values())t.reject(e);cn.clear()}function Vf(){if(He)return He;let e=zf();if(!Kf(e))throw new xe({kind:"bundle-missing",detail:"embedder-worker-llamacpp bundle not found - run `npm run build:cli` to emit it",path:e});let t=new Gf(e);return t.on("message",n=>{let s=cn.get(n.id);s&&(cn.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker-llamacpp] thread error:",n);let s=n instanceof Error?n:new Error(String(n));cc(s),He=null,Oe=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),cc(new Error(`embedder worker exited with code ${n}`))),He=null,Oe=!1}),He=t,t}function ns(e){return new Promise((t,n)=>{let s;try{s=Vf()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}cn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function ss(){return Gr=Gr+1>>>0,String(Gr)}function Yr(e){if(!e.ok)throw e.unavailable?new xe({kind:"platform-unsupported",detail:"embedder worker reports the llama.cpp runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function Qf(){if(!(Oe&&He))try{Yr(await ns({id:ss(),type:"load"})),Oe=!0}catch(e){throw Oe=!1,e}}function Zf(){return{loaded:Oe,modelId:lc,dim:768}}async function e_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return Yr(await ns({id:ss(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function t_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=Yr(await ns({id:ss(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function n_(){if(!He){Oe=!1;return}try{await ns({id:ss(),type:"unload"})}catch{}Oe=!1;let e=He;He=null;try{await e.terminate()}catch{}}var lc,He,cn,Gr,Oe,uc=re(()=>{"use strict";Vn();on();on();lc="BAAI/bge-base-en-v1.5-gguf-q8_0";He=null,cn=new Map,Gr=0,Oe=!1});var dc={};_t(dc,{EmbedderUnavailableError:()=>xe,embed:()=>Et,embedQuery:()=>r_,getEmbedderStatus:()=>pe,loadEmbedder:()=>Ge,unloadEmbedder:()=>o_});function s_(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?Kr:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
735
- `),ts)}var ln,Ge,pe,Et,r_,o_,bt=re(()=>{"use strict";ac();uc();on();ln=s_(),Ge=ln.loadEmbedder,pe=ln.getEmbedderStatus,Et=ln.embed,r_=ln.embedQuery,o_=ln.unloadEmbedder});import{writeFileSync as ih}from"node:fs";import{join as ah}from"node:path";function it(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function yt(e,t){let n=it(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=h(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?{tag:n,added:!1}:(s.transaction(()=>{s.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,n,r),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,n,r)})(),Mc(),{tag:n,added:!0})}function Ic(e,t){let n=it(t);if(!n)return{tag:"",removed:!1};let s=h(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?(s.transaction(()=>{s.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,n),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,n,r)})(),Mc(),{tag:n,removed:!0}):{tag:n,removed:!1}}function mn(e){return h().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function Tt(){return h().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
736
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function Mc(){try{V();let e=h(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),n=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),s={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:n};ih(ch,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var ch,wt=re(()=>{"use strict";q();ne();ch=ah(W,"tags.json")});function lh(e,t){let n=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)s.add(Math.floor(o*r));return Array.from(s).sort((o,a)=>o-a).slice(0,t).map(o=>n[o])}function Rt(e){let t=h(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{n[`sid_${u}`]=c})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
734
+
735
+ -- Incremental-indexing parse cursor. One row per JSONL file (a file may
736
+ -- hold multiple sessions, so the cursor is per-FILE, not per-session).
737
+ -- Lets the watcher stream only newly-appended bytes instead of reparsing
738
+ -- from byte 0. Derived/recomputable state \u2014 NOT subject to the three-layer
739
+ -- durability rule (a wrong/missing cursor just forces a full reparse).
740
+ -- size_bytes and line_count are diagnostic only: decideReadStrategy compares
741
+ -- the LIVE stat size against byte_offset, never the stored size_bytes.
742
+ CREATE TABLE IF NOT EXISTS file_cursor (
743
+ file_path TEXT PRIMARY KEY,
744
+ byte_offset INTEGER NOT NULL,
745
+ size_bytes INTEGER NOT NULL,
746
+ line_count INTEGER NOT NULL DEFAULT 0,
747
+ inode INTEGER,
748
+ prefix_hash TEXT,
749
+ mtime REAL,
750
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
751
+ );
752
+ `});function ic(e,t){let n=t.RECALL_DB_PROFILE;if(n&&i_.has(n))return n;let s=(e??"").split(/[\\/]/).pop()??"";return s==="mcp-server.js"||s==="claude-recall-mcp"?"light":s==="query-worker.js"?"worker":"full"}function ac(e){let t=[["busy_timeout",15e3],["journal_size_limit",67108864],["wal_autocheckpoint",1e3]];return e==="full"?[["cache_size",-64e3],["mmap_size",268435456],...t]:e==="worker"?[["cache_size",-16e3],["mmap_size",0],...t]:[["cache_size",-4e3],["mmap_size",0],...t]}var i_,cc=se(()=>{"use strict";i_=new Set(["light","full","worker"])});import*as uc from"sqlite-vec";function at(e){let t=e;lc.has(t)||(uc.load(e),lc.add(t))}function dc(e){e.prepare("SELECT 1 FROM sqlite_master WHERE name = 'vec_chunks'").get()||(at(e),e.exec(a_))}var lc,a_,es=se(()=>{"use strict";lc=new WeakSet;a_=`
753
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
754
+ embedding float[768] distance_metric=cosine
755
+ );`});function _(){if(Te)return Te;K();let e=ic(process.argv[1],process.env);c_=e,Te=new Zn(ze);for(let[t,n]of ac(e))Te.pragma(`${t} = ${n}`);Te.pragma("temp_store = MEMORY"),e!=="light"&&at(Te),Te.exec(sc),rc(Te),dc(Te);try{Te.exec("PRAGMA optimize")}catch{}return Te}var Te,c_,H=se(()=>{"use strict";zr();Z();oc();cc();es();Te=null,c_="full"});import{existsSync as g_}from"node:fs";import{dirname as Rc}from"node:path";import{fileURLToPath as f_}from"node:url";function Tt(){if(ts)return ts;let e=Rc(f_(import.meta.url));for(;!g_(`${e}/package.json`);){let t=Rc(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return ts=e,ts}var ts,ns=se(()=>{"use strict";ts=null});function ss(e){let t=null;return()=>t||(t=(async()=>{try{return await e()}finally{t=null}})(),t)}var Vr=se(()=>{"use strict"});function __(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) - or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
756
+ `)}var rs,ye,ln=se(()=>{"use strict";rs="BAAI/bge-base-en-v1.5",ye=class extends Error{kind;path;underlying;cause;constructor(t){super(__(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var as={};St(as,{embed:()=>k_,embedQuery:()=>A_,getEmbedderStatus:()=>R_,loadEmbedder:()=>w_,unloadEmbedder:()=>x_});import{Worker as h_}from"node:worker_threads";import{join as E_}from"node:path";import{existsSync as b_}from"node:fs";function S_(){return E_(Tt(),"dist","daemon","embedder-worker.js")}function kc(e){for(let t of un.values())t.reject(e);un.clear()}function T_(){if(Ue)return Ue;let e=S_();if(!b_(e))throw new ye({kind:"bundle-missing",detail:"embedder-worker bundle not found - run `npm run build:cli` to emit it",path:e});let t=new h_(e);return t.on("message",n=>{let s=un.get(n.id);s&&(un.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker] thread error:",n);let s=n instanceof Error?n:new Error(String(n));kc(s),Ue=null,Ne=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),kc(new Error(`embedder worker exited with code ${n}`))),Ue=null,Ne=!1}),Ue=t,t}function os(e){return new Promise((t,n)=>{let s;try{s=T_()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}un.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function is(){return Qr=Qr+1>>>0,String(Qr)}function eo(e){if(!e.ok)throw e.unavailable?new ye({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function w_(){if(!(Ne&&Ue))try{await y_(),Ne=!0}catch(e){throw Ne=!1,e}}function R_(){return{loaded:Ne,modelId:rs,dim:768}}async function k_(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return eo(await os({id:is(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function A_(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=eo(await os({id:is(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function x_(){if(!Ue){Ne=!1;return}try{await os({id:is(),type:"unload"})}catch{}Ne=!1;let e=Ue;Ue=null;try{await e.terminate()}catch{}}var Ue,un,Qr,Ne,y_,Ac=se(()=>{"use strict";ns();Vr();ln();Ue=null,un=new Map,Qr=0,Ne=!1;y_=ss(async()=>{eo(await os({id:is(),type:"load"}))})});var so={};St(so,{LLAMACPP_MODEL_ID:()=>Nc,MODEL_ID:()=>rs,embed:()=>j_,embedQuery:()=>P_,getEmbedderStatus:()=>D_,loadEmbedder:()=>M_,unloadEmbedder:()=>F_});import{Worker as N_}from"node:worker_threads";import{join as O_}from"node:path";import{existsSync as L_}from"node:fs";function C_(){return O_(Tt(),"dist","daemon","embedder-worker-llamacpp.js")}function xc(e){for(let t of dn.values())t.reject(e);dn.clear()}function v_(){if(He)return He;let e=C_();if(!L_(e))throw new ye({kind:"bundle-missing",detail:"embedder-worker-llamacpp bundle not found - run `npm run build:cli` to emit it",path:e});let t=new N_(e);return t.on("message",n=>{let s=dn.get(n.id);s&&(dn.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker-llamacpp] thread error:",n);let s=n instanceof Error?n:new Error(String(n));xc(s),He=null,Oe=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),xc(new Error(`embedder worker exited with code ${n}`))),He=null,Oe=!1}),He=t,t}function cs(e){return new Promise((t,n)=>{let s;try{s=v_()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}dn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function ls(){return to=to+1>>>0,String(to)}function no(e){if(!e.ok)throw e.unavailable?new ye({kind:"platform-unsupported",detail:"embedder worker reports the llama.cpp runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function M_(){if(!(Oe&&He))try{await I_(),Oe=!0}catch(e){throw Oe=!1,e}}function D_(){return{loaded:Oe,modelId:Nc,dim:768}}async function j_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return no(await cs({id:ls(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function P_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=no(await cs({id:ls(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function F_(){if(!He){Oe=!1;return}try{await cs({id:ls(),type:"unload"})}catch{}Oe=!1;let e=He;He=null;try{await e.terminate()}catch{}}var Nc,He,dn,to,Oe,I_,Oc=se(()=>{"use strict";ns();Vr();ln();ln();Nc="BAAI/bge-base-en-v1.5-gguf-q8_0";He=null,dn=new Map,to=0,Oe=!1;I_=ss(async()=>{no(await cs({id:ls(),type:"load"}))})});var Lc={};St(Lc,{EmbedderUnavailableError:()=>ye,embed:()=>Ve,embedQuery:()=>U_,getEmbedderStatus:()=>le,loadEmbedder:()=>Be,unloadEmbedder:()=>H_});function $_(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?so:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
757
+ `),as)}var pn,Be,le,Ve,U_,H_,We=se(()=>{"use strict";Ac();Oc();ln();pn=$_(),Be=pn.loadEmbedder,le=pn.getEmbedderStatus,Ve=pn.embed,U_=pn.embedQuery,H_=pn.unloadEmbedder});import{writeFileSync as Bh}from"node:fs";import{join as Wh}from"node:path";function ut(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function wt(e,t){let n=ut(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=_(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?{tag:n,added:!1}:(s.transaction(()=>{s.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,n,r),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,n,r)})(),Qc(),{tag:n,added:!0})}function Vc(e,t){let n=ut(t);if(!n)return{tag:"",removed:!1};let s=_(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?(s.transaction(()=>{s.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,n),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,n,r)})(),Qc(),{tag:n,removed:!0}):{tag:n,removed:!1}}function _n(e){return _().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function Rt(){return _().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
758
+ GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function Qc(){try{K();let e=_(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),n=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),s={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:n};Bh(qh,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var qh,kt=se(()=>{"use strict";H();Z();qh=Wh(W,"tags.json")});function Xh(e,t){let n=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)s.add(Math.floor(o*r));return Array.from(s).sort((o,a)=>o-a).slice(0,t).map(o=>n[o])}function At(e){let t=_(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{n[`sid_${u}`]=c})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
737
759
  NULLIF(sa.alias, '') AS alias,
738
760
  COALESCE(s.first_user_message, '') AS first_user_message
739
761
  FROM sessions s
@@ -743,15 +765,15 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
743
765
  ORDER BY COALESCE(s.started_at, '') DESC
744
766
  LIMIT @limit`).all(n).map(a=>{let c=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
745
767
  FROM messages WHERE session_id = ?
746
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=lh(c,5).map(p=>`${p.role}: ${p.content_text.slice(0,400)}`).join(`
768
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=Xh(c,5).map(p=>`${p.role}: ${p.content_text.slice(0,400)}`).join(`
747
769
  ---
748
- `);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:mn(a.id)}})}var cs=re(()=>{"use strict";q();wt()});import{z as be}from"zod";function oo(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
749
- `)}function ph(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(`
750
- `)}function gh(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(`
751
- `)}function _h(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(`
752
- `)}function jc(e){return io.find(t=>t.name===e)}var uh,dh,mh,fh,hh,Eh,bh,Sh,io,ao=re(()=>{"use strict";uh={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).")};dh={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.")};mh={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};fh={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).")};hh={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:uh,build:oo,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Eh={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:dh,build:ph,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},bh={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:mh,build:gh,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Sh={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:fh,build:_h,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},io=[hh,Eh,bh,Sh]});function ls(e,t){let n=gn.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}function Dc(e,t){let n=gn.get(e);return n||(n=new Set,gn.set(e,n)),n.add(t),()=>{let s=gn.get(e);s&&(s.delete(t),s.size===0&&gn.delete(e))}}var gn,co=re(()=>{"use strict";gn=new Map});import{existsSync as yh,statSync as Th}from"node:fs";import{delimiter as wh,join as Rh}from"node:path";function kt(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(wh).filter(Boolean),n=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let s of t)for(let r of n){let o=Rh(s,r);try{if(yh(o)&&Th(o).isFile())return o}catch{}}return null}function us(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var ds=re(()=>{"use strict"});var ct={};_t(ct,{_resetClaudePathCacheForTests:()=>Nh,buildScanPrompt:()=>Fc,isClaudeCliAvailable:()=>me,runClaudeCliScan:()=>lo,spawnClaudePrompt:()=>at});import{spawn as kh}from"node:child_process";function Pc(){if(At!==void 0&&fn!==void 0)return{path:At,available:fn};let e=kt("claude");return At=e??"claude",fn=e!==null,{path:At,available:fn}}function xh(){return Pc().path}function me(){return Pc().available}function Nh(){At=void 0,fn=void 0}function Fc(e){return oo({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 Oh(e,t){let n=t.get(e);return n||e.slice(0,8)}function Ch(e){try{return Rt(e).map(n=>({id:n.id,label:n.alias&&n.alias.trim().length>0?n.alias:n.first_user_message&&n.first_user_message.trim().length>0?n.first_user_message.slice(0,60):n.id.slice(0,8)}))}catch{return[]}}function Lh(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return o=>{let a=o.trim();if(!a.startsWith("{"))return;let c;try{c=JSON.parse(a)}catch{return}if(!c||typeof c!="object")return;let u=c;if(!(u.type!=="assistant"||!u.message?.content))for(let d of u.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let p=d.input,f=typeof p?.sessionId=="string"?p.sessionId:null;!f||r.has(f)||(r.add(f),ls(t,{type:"progress",current:r.size,total:n,sessionId:f,sessionLabel:Oh(f,s)}))}}}function vh(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
770
+ `);return{id:a.id,project:a.project,git_branch:a.git_branch,alias:a.alias,first_user_message:a.first_user_message,message_sample:d,current_tags:_n(a.id)}})}var gs=se(()=>{"use strict";H();kt()});import{z as Ee}from"zod";function go(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
771
+ `)}function Yh(e){let t=e.mode==="detailed";return[`Summarize Claude Recall session ${e.sessionId} using the MCP tools available to you.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "condensed" to get the transcript as markdown.`,t?"2. Write a 1-paragraph overview (\u22643 sentences) of what this session was for, then 5-8 bullet points covering:":"2. Write 3-5 bullet points covering:"," - What was accomplished (shipped, decided, learned)"," - What was tried and abandoned"," - Any explicit open questions or follow-ups","","Rules:",'- Be concrete. Name files, functions, and decisions. Avoid vague "discussed X".',"- If nothing was actually shipped or decided, say so plainly.",'- Reply with just the summary \u2014 no preamble, no "Here is the summary:".'].join(`
772
+ `)}function Kh(e){return[`Extract every architectural or product decision made in Claude Recall session ${e.sessionId}.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "full" (so tool I/O is included \u2014 sometimes the decision lives in a commit message or diff).`,"2. For each distinct decision, emit one block:",""," - **Decision:** one sentence."," - **Why:** the stated rationale (quote if possible).",' - **Alternatives considered:** bullet list, or "none mentioned".',' - **Where it landed:** file path / function name / commit SHA if identifiable, otherwise "TBD".',"","Rules:","- Include only decisions that were actually made, not ideas merely discussed.","- If an alternative was rejected, list it with a one-line reason.","- Group related decisions under a short heading when useful.",'- If the session made zero real decisions, reply exactly with: "No decisions made in this session."',"- No preamble, no closing, just the decision blocks."].join(`
773
+ `)}function Qh(e){let t=e.limit??5;return[`Find ${t} Recall sessions most similar to session ${e.sessionId}.`,"",`1. Call \`get_session\` with id "${e.sessionId}" \u2014 note its alias, first user message, tags, and git branch.`,'2. Derive 2-3 short search queries from that content (topic words, library names, error strings \u2014 NOT generic words like "fix" or "add").',`3. Call \`search\` once per query (limit: ${Math.max(5,t*2)}). Dedupe hits by session_id.`,"4. Also call `list_sessions` with the same tag(s) if the target session has any (pick the most specific tag).","5. Union the results. Exclude the target session itself. Rank by a mix of:"," - Shared tags (strongest signal)"," - Matching search hits across multiple queries"," - Recency as a tiebreaker","",`6. Return the top ${t} as a numbered list. For each:`," - **<short_id> \xB7 <project>** \u2014 <alias or first_user_message truncated>",' - One sentence on WHY it is similar (not a generic "same topic" \u2014 be specific).',"","If fewer than 2 genuinely-similar sessions exist, say so rather than padding with weak matches.","No preamble. Just the ranked list."].join(`
774
+ `)}function Zc(e){return fo.find(t=>t.name===e)}var Jh,Gh,zh,Vh,Zh,eE,tE,nE,fo,_o=se(()=>{"use strict";Jh={project:Ee.string().optional().describe("Exact project name match (optional)."),collectionId:Ee.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:Ee.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:Ee.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:Ee.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:Ee.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:Ee.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};Gh={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:Ee.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};zh={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};Vh={sessionId:Ee.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:Ee.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};Zh={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:Jh,build:go,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},eE={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:Gh,build:Yh,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},tE={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:zh,build:Kh,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},nE={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:Vh,build:Qh,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},fo=[Zh,eE,tE,nE]});function fs(e,t){let n=hn.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}function el(e,t){let n=hn.get(e);return n||(n=new Set,hn.set(e,n)),n.add(t),()=>{let s=hn.get(e);s&&(s.delete(t),s.size===0&&hn.delete(e))}}var hn,ho=se(()=>{"use strict";hn=new Map});import{existsSync as sE,statSync as rE}from"node:fs";import{delimiter as oE,join as iE}from"node:path";function xt(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(oE).filter(Boolean),n=process.platform==="win32"?[`${e}.exe`,`${e}.cmd`,`${e}.bat`,e]:[e];for(let s of t)for(let r of n){let o=iE(s,r);try{if(sE(o)&&rE(o).isFile())return o}catch{}}return null}function _s(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var hs=se(()=>{"use strict"});var pt={};St(pt,{_resetClaudePathCacheForTests:()=>uE,buildScanPrompt:()=>nl,isClaudeCliAvailable:()=>me,runClaudeCliScan:()=>Eo,spawnClaudePrompt:()=>dt});import{spawn as aE}from"node:child_process";function tl(){if(Nt!==void 0&&En!==void 0)return{path:Nt,available:En};let e=xt("claude");return Nt=e??"claude",En=e!==null,{path:Nt,available:En}}function lE(){return tl().path}function me(){return tl().available}function uE(){Nt=void 0,En=void 0}function nl(e){return go({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 dE(e,t){let n=t.get(e);return n||e.slice(0,8)}function pE(e){try{return At(e).map(n=>({id:n.id,label:n.alias&&n.alias.trim().length>0?n.alias:n.first_user_message&&n.first_user_message.trim().length>0?n.first_user_message.slice(0,60):n.id.slice(0,8)}))}catch{return[]}}function mE(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return o=>{let a=o.trim();if(!a.startsWith("{"))return;let c;try{c=JSON.parse(a)}catch{return}if(!c||typeof c!="object")return;let u=c;if(!(u.type!=="assistant"||!u.message?.content))for(let d of u.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let p=d.input,f=typeof p?.sessionId=="string"?p.sessionId:null;!f||r.has(f)||(r.add(f),fs(t,{type:"progress",current:r.size,total:n,sessionId:f,sessionLabel:dE(f,s)}))}}}function gE(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
753
775
  `);for(;s!==-1;){let r=t.slice(0,s);t=t.slice(s+1),r.length>0&&e(r),s=t.indexOf(`
754
- `)}}}async function lo(e,t={},n){let s=!!t.scanId,r=s?Ch(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return s&&t.scanId&&(c=Lh({scanId:t.scanId,total:a,labelTable:o})),$c({prompt:Fc(e),allowedTools:Ah.split(","),opts:t,onProgress:n,onStdoutLine:c,outputFormat:s?"stream-json":"json"})}async function at(e,t,n={},s){return $c({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function $c(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return a==="stream-json"&&c.push("--verbose"),s.model&&c.push("--model",s.model),new Promise(u=>{let d=xh(),p=kh(d,c,{stdio:["ignore","pipe","pipe"],shell:us(d)||process.platform==="win32"&&At==="claude"}),f=[],S=[],y=o?vh(o):void 0;p.stdout.on("data",R=>{f.push(R),y&&y(R)}),p.stderr.on("data",R=>{if(S.push(R),r){let k=R.toString("utf8").trim();k&&r(k)}});let b=setTimeout(()=>{p.kill("SIGKILL")},1800*1e3);p.on("close",R=>{clearTimeout(b),u({success:R===0,stdout:Buffer.concat(f).toString("utf8"),stderr:Buffer.concat(S).toString("utf8"),exitCode:R})}),p.on("error",R=>{clearTimeout(b),u({success:!1,stdout:"",stderr:String(R),exitCode:null})})})}var Ah,At,fn,Te=re(()=>{"use strict";cs();ao();co();ds();Ah=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var nl={};_t(nl,{RetentionConfigSchema:()=>Eo,readRetentionConfig:()=>bo,writeRetentionConfig:()=>Ss});import{existsSync as Vc,mkdirSync as bE,readFileSync as SE,writeFileSync as yE}from"node:fs";import{homedir as TE}from"node:os";import{join as Qc}from"node:path";import{z as Es}from"zod";function Zc(){return process.env.RECALL_HOME??Qc(TE(),".recall")}function wE(){let e=Zc();Vc(e)||bE(e,{recursive:!0})}function el(){return Qc(Zc(),"config.json")}function tl(){let e=el();if(!Vc(e))return{};try{return JSON.parse(SE(e,"utf8"))}catch(t){let n=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${n}`),{}}}function bo(){let e=tl().retention;if(!e)return{...bs};let t=Eo.safeParse({...bs,...e});return t.success?t.data:{...bs}}function Ss(e){wE();let t=tl(),n=Eo.parse({...bs,...t.retention??{},...e}),s={...t,retention:n};return yE(el(),JSON.stringify(s,null,2)),n}var Eo,bs,So=re(()=>{"use strict";Eo=Es.object({autoArchiveEnabled:Es.boolean().default(!1),autoArchiveAfterDays:Es.number().int().min(7).max(3650).default(90),lastRunAt:Es.string().nullable().default(null)}),bs={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Le from"chalk";import{formatDistanceToNowStrict as iO,parseISO as aO}from"date-fns";var we,ys=re(()=>{"use strict";we={dim:Le.gray,bold:Le.bold,project:Le.cyan,user:Le.blue,assistant:Le.green,tool:Le.magenta,warn:Le.yellow,err:Le.red,ok:Le.green,accent:Le.hex("#f97316")}});import{existsSync as RE}from"node:fs";import{join as kE}from"node:path";function hn(){if(sl&&RE(_n))return;V();let e=h(),t=_n.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
776
+ `)}}}async function Eo(e,t={},n){let s=!!t.scanId,r=s?pE(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return s&&t.scanId&&(c=mE({scanId:t.scanId,total:a,labelTable:o})),sl({prompt:nl(e),allowedTools:cE.split(","),opts:t,onProgress:n,onStdoutLine:c,outputFormat:s?"stream-json":"json"})}async function dt(e,t,n={},s){return sl({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function sl(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return a==="stream-json"&&c.push("--verbose"),s.model&&c.push("--model",s.model),new Promise(u=>{let d=lE(),p=aE(d,c,{stdio:["ignore","pipe","pipe"],shell:_s(d)||process.platform==="win32"&&Nt==="claude"}),f=[],b=[],T=o?gE(o):void 0;p.stdout.on("data",w=>{f.push(w),T&&T(w)}),p.stderr.on("data",w=>{if(b.push(w),r){let A=w.toString("utf8").trim();A&&r(A)}});let S=setTimeout(()=>{p.kill("SIGKILL")},1800*1e3);p.on("close",w=>{clearTimeout(S),u({success:w===0,stdout:Buffer.concat(f).toString("utf8"),stderr:Buffer.concat(b).toString("utf8"),exitCode:w})}),p.on("error",w=>{clearTimeout(S),u({success:!1,stdout:"",stderr:String(w),exitCode:null})})})}var cE,Nt,En,we=se(()=>{"use strict";gs();_o();ho();hs();cE=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var yl={};St(yl,{RetentionConfigSchema:()=>Ao,readRetentionConfig:()=>xo,writeRetentionConfig:()=>As});import{existsSync as hl,mkdirSync as sb,readFileSync as rb,writeFileSync as ob}from"node:fs";import{homedir as ib}from"node:os";import{join as El}from"node:path";import{z as Rs}from"zod";function bl(){return process.env.RECALL_HOME??El(ib(),".recall")}function ab(){let e=bl();hl(e)||sb(e,{recursive:!0})}function Sl(){return El(bl(),"config.json")}function Tl(){let e=Sl();if(!hl(e))return{};try{return JSON.parse(rb(e,"utf8"))}catch(t){let n=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${n}`),{}}}function xo(){let e=Tl().retention;if(!e)return{...ks};let t=Ao.safeParse({...ks,...e});return t.success?t.data:{...ks}}function As(e){ab();let t=Tl(),n=Ao.parse({...ks,...t.retention??{},...e}),s={...t,retention:n};return ob(Sl(),JSON.stringify(s,null,2)),n}var Ao,ks,No=se(()=>{"use strict";Ao=Rs.object({autoArchiveEnabled:Rs.boolean().default(!1),autoArchiveAfterDays:Rs.number().int().min(7).max(3650).default(90),lastRunAt:Rs.string().nullable().default(null)}),ks={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Ce from"chalk";import{formatDistanceToNowStrict as eL,parseISO as tL}from"date-fns";var Re,xs=se(()=>{"use strict";Re={dim:Ce.gray,bold:Ce.bold,project:Ce.cyan,user:Ce.blue,assistant:Ce.green,tool:Ce.magenta,warn:Ce.yellow,err:Ce.red,ok:Ce.green,accent:Ce.hex("#f97316")}});import{existsSync as cb}from"node:fs";import{join as lb}from"node:path";function Sn(){if(wl&&cb(bn))return;K();let e=_(),t=bn.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
755
777
  CREATE TABLE IF NOT EXISTS archive.messages_archive (
756
778
  uuid TEXT PRIMARY KEY,
757
779
  session_id TEXT NOT NULL,
@@ -766,12 +788,12 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
766
788
  archived_at TEXT NOT NULL DEFAULT (datetime('now'))
767
789
  );
768
790
  CREATE INDEX IF NOT EXISTS archive.idx_messages_archive_session ON messages_archive(session_id);
769
- `)}finally{e.exec("DETACH DATABASE archive")}sl=!0}function rl(){let e=h();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;hn();let n=_n.replace(/'/g,"''"),s=0;e.exec(`ATTACH DATABASE '${n}' AS archive`);try{e.transaction(()=>{s=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
791
+ `)}finally{e.exec("DETACH DATABASE archive")}wl=!0}function Rl(){let e=_();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;Sn();let n=bn.replace(/'/g,"''"),s=0;e.exec(`ATTACH DATABASE '${n}' AS archive`);try{e.transaction(()=>{s=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
770
792
  (uuid, session_id, parent_uuid, type, role, timestamp,
771
793
  is_sidechain, content_text, tool_names, raw_json, archived_at)
772
794
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
773
795
  is_sidechain, content_text, tool_names, raw_json, archived_at
774
- FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return s}var _n,sl,yo=re(()=>{"use strict";ne();q();_n=kE(W,"archive.sqlite"),sl=!1});var il={};_t(il,{runArchive:()=>IE});function To(e){hn();let t=h(),n=_n.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${n}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function AE(){return To(e=>{let t=e.prepare(`SELECT
796
+ FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return s}var bn,wl,Oo=se(()=>{"use strict";Z();H();bn=lb(W,"archive.sqlite"),wl=!1});var Al={};St(Al,{runArchive:()=>hb});function Lo(e){Sn();let t=_(),n=bn.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${n}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function ub(){return Lo(e=>{let t=e.prepare(`SELECT
775
797
  SUM(CASE WHEN archive_status = 'archived' THEN 1 ELSE 0 END) AS archived,
776
798
  SUM(CASE WHEN archive_status != 'archived' THEN 1 ELSE 0 END) AS live
777
799
  FROM sessions`).get(),n=e.prepare("SELECT COUNT(*) AS n FROM messages").get().n,s=e.prepare(`SELECT
@@ -780,15 +802,15 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
780
802
  SELECT MAX(timestamp) AS t FROM main.messages_archive WHERE timestamp IS NOT NULL
781
803
  UNION ALL
782
804
  SELECT MAX(timestamp) AS t FROM archive.messages_archive WHERE timestamp IS NOT NULL
783
- )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function ol(e){return e?e.slice(0,10):"\u2014"}function xE(){let e=AE();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 ${ol(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${ol(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 NE(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let n=h().prepare(`SELECT s.id, s.ended_at, s.message_count
805
+ )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function kl(e){return e?e.slice(0,10):"\u2014"}function db(){let e=ub();return console.log(Re.dim("\u2014 Archive state \u2014")),console.log(` Live sessions ${e.liveSessions.toLocaleString()}`),console.log(` Archived sessions ${e.archivedSessions.toLocaleString()}`),console.log(` Live messages ${e.liveMessages.toLocaleString()}`),console.log(` Archived messages ${e.archivedMessages.toLocaleString()}`),console.log(` Oldest live ${kl(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${kl(e.newestArchivedTimestamp)}`),console.log(""),console.log(Re.dim(" recall archive run --before YYYY-MM-DD")),console.log(Re.dim(" recall archive restore <session-id>")),0}function pb(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let n=_().prepare(`SELECT s.id, s.ended_at, s.message_count
784
806
  FROM sessions s
785
807
  WHERE s.archive_status != 'archived'
786
- AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(n.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let s=n.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${n.length.toLocaleString()} session(s), ${s.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(we.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=n.map(c=>c.id),o=Date.now(),a=To(c=>c.transaction(d=>{let p=0,f=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
808
+ AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(n.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let s=n.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${n.length.toLocaleString()} session(s), ${s.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(Re.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=n.map(c=>c.id),o=Date.now(),a=Lo(c=>c.transaction(d=>{let p=0,f=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
787
809
  (uuid, session_id, parent_uuid, type, role, timestamp,
788
810
  is_sidechain, content_text, tool_names, raw_json, archived_at)
789
811
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
790
812
  is_sidechain, content_text, tool_names, raw_json, datetime('now')
791
- FROM messages WHERE session_id = ?`),S=c.prepare("DELETE FROM messages WHERE session_id = ?"),y=c.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let b of d){f.run(b);let R=S.run(b);p+=Number(R.changes??0),y.run(b)}return p})(r));return console.log(`Archived ${n.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 OE(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let n=h().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!n)return console.error(`Session ${e} not found.`),1;if(n.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${n.archive_status}).`),1;let s=To(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
813
+ FROM messages WHERE session_id = ?`),b=c.prepare("DELETE FROM messages WHERE session_id = ?"),T=c.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let S of d){f.run(S);let w=b.run(S);p+=Number(w.changes??0),T.run(S)}return p})(r));return console.log(`Archived ${n.length.toLocaleString()} session(s), moved ${a.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(Re.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function mb(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let n=_().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!n)return console.error(`Session ${e} not found.`),1;if(n.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${n.archive_status}).`),1;let s=Lo(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
792
814
  (uuid, session_id, parent_uuid, type, role, timestamp,
793
815
  is_sidechain, content_text, tool_names, raw_json)
794
816
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
@@ -798,32 +820,32 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
798
820
  is_sidechain, content_text, tool_names, raw_json)
799
821
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
800
822
  is_sidechain, content_text, tool_names, raw_json
801
- 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 = ?"),p=Number(a.run(e).changes??0),f=Number(c.run(e).changes??0);return u.run(e),d.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),p+f})());return console.log(`Restored ${s.toLocaleString()} message(s) for session ${e}.`),0}function CE(){let e=bo();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 LE(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let n=Ss({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${n.autoArchiveAfterDays} days).`),console.log(we.dim(" The daemon will run a daily archive pass on the next tick.")),0}function vE(){return Ss({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function IE(e){let t=e._action??"list";if(t==="list"||t==="stats")return xE();if(t==="run")return e.before?NE({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return OE(e._sessionId??"");if(t==="auto"){let n=e._subAction??"status";return n==="status"?CE():n==="on"||n==="enable"?LE(e.after):n==="off"||n==="disable"?vE():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
823
+ FROM archive.messages_archive WHERE session_id = ?`),u=r.prepare("DELETE FROM main.messages_archive WHERE session_id = ?"),d=r.prepare("DELETE FROM archive.messages_archive WHERE session_id = ?"),p=Number(a.run(e).changes??0),f=Number(c.run(e).changes??0);return u.run(e),d.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),p+f})());return console.log(`Restored ${s.toLocaleString()} message(s) for session ${e}.`),0}function gb(){let e=xo();return console.log(Re.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?Re.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function fb(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let n=As({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${n.autoArchiveAfterDays} days).`),console.log(Re.dim(" The daemon will run a daily archive pass on the next tick.")),0}function _b(){return As({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function hb(e){let t=e._action??"list";if(t==="list"||t==="stats")return db();if(t==="run")return e.before?pb({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return mb(e._sessionId??"");if(t==="auto"){let n=e._subAction??"status";return n==="status"?gb():n==="on"||n==="enable"?fb(e.after):n==="off"||n==="disable"?_b():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
802
824
  list \u2014 show archive counts
803
825
  run --before YYYY-MM-DD [--dry-run] \u2014 move sessions older than DATE
804
826
  restore <session-id> \u2014 pull a session back from archive
805
- auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var al=re(()=>{"use strict";ys();q();yo();So()});import{cpus as cA}from"node:os";import{Hono as kk}from"hono";import{serve as Ak}from"@hono/node-server";ne();import{existsSync as tf,readFileSync as nf,writeFileSync as LA,unlinkSync as vA}from"node:fs";import{join as sf}from"node:path";var La=sf(W,"license.json");function sn(){if(!tf(La))return null;try{let e=nf(La,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as rf,importSPKI as of}from"jose";var va=`-----BEGIN PUBLIC KEY-----
827
+ auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var xl=se(()=>{"use strict";xs();H();Oo();No()});var Wa=se(()=>{"use strict";H()});import{cpus as YA}from"node:os";import{Hono as dA}from"hono";import{serve as pA}from"@hono/node-server";Z();import{existsSync as Mf,readFileSync as Df,writeFileSync as hx,unlinkSync as Ex}from"node:fs";import{join as jf}from"node:path";var Xa=jf(W,"license.json");function an(){if(!Mf(Xa))return null;try{let e=Df(Xa,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as Pf,importSPKI as Ff}from"jose";var Ja=`-----BEGIN PUBLIC KEY-----
806
828
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
807
829
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
808
830
  -----END PUBLIC KEY-----
809
- `,Fr="ES256",Ia="clauderecall.com",Ma="clauderecall-cli";var Yn=null;async function af(){return Yn||(Yn=await of(va,Fr),Yn)}async function ja(e){try{let t=await af(),{payload:n}=await rf(e,t,{issuer:Ia,audience:Ma,algorithms:[Fr]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as cf}from"node:crypto";import{hostname as lf,userInfo as uf,platform as df,arch as pf}from"node:os";function Da(){let e="unknown";try{e=uf().username}catch{}let t=[lf(),e,df(),pf()];return cf("sha256").update(t.join("\0")).digest("hex")}ne();import{existsSync as mf,readFileSync as gf,writeFileSync as ff}from"node:fs";import{join as _f}from"node:path";function Pa(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),n;try{n=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let s=n.hostname==="127.0.0.1"||n.hostname==="localhost"||n.hostname==="::1";if(n.protocol==="https:"||n.protocol==="http:"&&s)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var $r=_f(W,"license-check.json"),hf=1440*60*1e3,Ef=720*60*60*1e3,bf=1e4;function Fa(){if(!mf($r))return null;try{let e=JSON.parse(gf($r,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Sf(e){V(),ff($r,JSON.stringify(e,null,2)+`
810
- `,{mode:384})}async function yf(e,t){let n=null,s=null;try{n=new AbortController,s=setTimeout(()=>n?.abort(),bf);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:n.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{s&&clearTimeout(s)}}async function $a(e,t={}){let n=Fa(),s=t.apiUrl??`${Pa()}/api/license/check`,r=n?.license_key===e,o=!n||!r||Date.now()-new Date(n.last_checked_at).getTime()>=hf;if(!t.force&&!o)return n;let a=await yf(e,s);if(!a)return r?n:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return Sf(c),c}function Ua(e){let t=Fa();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()>Ef?{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 Tf=Date.UTC(2026,5,1,7,0,0);var wf=1440*60*1e3,VA=60*wf;async function rt(){let e=sn();if(!e)return{tier:"free"};let t=await ja(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Da())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=Ua(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:Rf(e,t.claims)}async function Ha(e){let t=sn();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let n=await $a(t.license_key,{force:e?.force??!1});return n?{ran:!0,revoked:n.revoked,reason:n.reason,last_checked_at:n.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function Rf(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function Ba(){return(await rt()).tier==="pro"}q();q();import{createHash as i_}from"node:crypto";q();ne();import{writeFileSync as Of,readFileSync as _x,existsSync as Cf,mkdirSync as Lf,readdirSync as hx,unlinkSync as Ex}from"node:fs";import{join as Ga}from"node:path";import{randomUUID as Ya}from"node:crypto";var Wr=Ga(W,"bug-patterns");function vf(){V(),Cf(Wr)||Lf(Wr,{recursive:!0})}function Je(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 Ka(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function za(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=h(),n=new Date().toISOString(),s=e.id??Ya(),r=e.first_seen_at??n,o=e.last_seen_at??n,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
831
+ `,Xr="ES256",Ga="clauderecall.com",Ya="clauderecall-cli";var Qn=null;async function $f(){return Qn||(Qn=await Ff(Ja,Xr),Qn)}async function za(e){try{let t=await $f(),{payload:n}=await Pf(e,t,{issuer:Ga,audience:Ya,algorithms:[Xr]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Uf}from"node:crypto";import{hostname as Hf,userInfo as Bf,platform as Wf,arch as qf}from"node:os";function Ka(){let e="unknown";try{e=Bf().username}catch{}let t=[Hf(),e,Wf(),qf()];return Uf("sha256").update(t.join("\0")).digest("hex")}Z();import{existsSync as Xf,readFileSync as Jf,writeFileSync as Gf}from"node:fs";import{join as Yf}from"node:path";function Va(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),n;try{n=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let s=n.hostname==="127.0.0.1"||n.hostname==="localhost"||n.hostname==="::1";if(n.protocol==="https:"||n.protocol==="http:"&&s)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var Jr=Yf(W,"license-check.json"),zf=1440*60*1e3,Kf=720*60*60*1e3,Vf=1e4;function Qa(){if(!Xf(Jr))return null;try{let e=JSON.parse(Jf(Jr,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Qf(e){K(),Gf(Jr,JSON.stringify(e,null,2)+`
832
+ `,{mode:384})}async function Zf(e,t){let n=null,s=null;try{n=new AbortController,s=setTimeout(()=>n?.abort(),Vf);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:n.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{s&&clearTimeout(s)}}async function Za(e,t={}){let n=Qa(),s=t.apiUrl??`${Va()}/api/license/check`,r=n?.license_key===e,o=!n||!r||Date.now()-new Date(n.last_checked_at).getTime()>=zf;if(!t.force&&!o)return n;let a=await Zf(e,s);if(!a)return r?n:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return Qf(c),c}function ec(e){let t=Qa();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()>Kf?{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 e_=1440*60*1e3,Fx=60*e_;async function it(){let e=an();if(!e)return{tier:"free"};let t=await za(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Ka())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=ec(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:t_(e,t.claims)}async function tc(e){let t=an();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let n=await Za(t.license_key,{force:e?.force??!1});return n?{ran:!0,revoked:n.revoked,reason:n.reason,last_checked_at:n.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function t_(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function nc(){return(await it()).tier==="pro"}H();H();import{createHash as B_}from"node:crypto";H();Z();import{writeFileSync as l_,readFileSync as aN,existsSync as u_,mkdirSync as d_,readdirSync as cN,unlinkSync as lN}from"node:fs";import{join as pc}from"node:path";import{randomUUID as mc}from"node:crypto";var Kr=pc(W,"bug-patterns");function p_(){K(),u_(Kr)||d_(Kr,{recursive:!0})}function Ke(e){return{id:e.id,signature_hash:e.signature_hash,example_message:e.example_message,occurrence_count:e.occurrence_count,first_seen_at:e.first_seen_at,last_seen_at:e.last_seen_at,resolved_in_session_id:e.resolved_in_session_id,fix_summary:e.fix_summary}}function gc(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function fc(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=_(),n=new Date().toISOString(),s=e.id??mc(),r=e.first_seen_at??n,o=e.last_seen_at??n,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
811
833
  (id, signature_hash, example_message, occurrence_count,
812
834
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
813
835
  VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(s,e.signature_hash,e.example_message,a.length,r,o);let u=t.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
814
836
  VALUES (?, ?, ?)
815
- ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(s,d,n)})();let c=Va(s);if(!c)throw new Error("createCluster succeeded but read-back failed");return rn(s),c}function Va(e){let t=h(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:Je(n),members:s.map(Ka)}}function Qa(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let n=h();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;n.transaction(()=>{let c=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
837
+ ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(s,d,n)})();let c=_c(s);if(!c)throw new Error("createCluster succeeded but read-back failed");return cn(s),c}function _c(e){let t=_(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:Ke(n),members:s.map(gc)}}function hc(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let n=_();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;n.transaction(()=>{let c=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
816
838
  VALUES (?, ?, ?)
817
839
  ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let u of Array.from(new Set(t)))c.run(e,u,r).changes>0&&(o+=1);if(o>0){let u=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;n.prepare(`UPDATE bug_pattern_clusters
818
840
  SET occurrence_count = ?, last_seen_at = ?
819
- WHERE id = ?`).run(u,r,e)}})(),o>0&&rn(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Je(a),added:o}}function Za(e={}){let t=h(),n=[],s=[];typeof e.minOccurrenceCount=="number"&&(n.push("c.occurrence_count >= ?"),s.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&n.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(n.push(`EXISTS (
841
+ WHERE id = ?`).run(u,r,e)}})(),o>0&&cn(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Ke(a),added:o}}function Ec(e={}){let t=_(),n=[],s=[];typeof e.minOccurrenceCount=="number"&&(n.push("c.occurrence_count >= ?"),s.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&n.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(n.push(`EXISTS (
820
842
  SELECT 1 FROM bug_pattern_members m
821
843
  JOIN sessions s ON s.id = m.session_id
822
844
  JOIN projects p ON p.id = s.project_id
823
845
  WHERE m.cluster_id = c.id AND p.name = ?
824
846
  )`),s.push(e.project));let r=n.length>0?`WHERE ${n.join(" AND ")}`:"",o=t.prepare(`SELECT COUNT(*) AS n FROM bug_pattern_clusters c ${r}`).get(...s),a=Math.max(1,Math.min(5e3,e.limit??100)),c=Math.max(0,Math.floor(e.offset??0));return{clusters:t.prepare(`SELECT c.* FROM bug_pattern_clusters c ${r}
825
847
  ORDER BY c.occurrence_count DESC, c.last_seen_at DESC
826
- LIMIT ? OFFSET ?`).all(...s,a,c).map(Je),total:o.n}}function If(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,n=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:n,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function ec(e){let t=h(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
848
+ LIMIT ? OFFSET ?`).all(...s,a,c).map(Ke),total:o.n}}function m_(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,n=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:n,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function bc(e){let t=_(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
827
849
  NULLIF(sa.alias, '') AS alias,
828
850
  s.auto_title,
829
851
  s.first_user_message,
@@ -834,31 +856,31 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
834
856
  LEFT JOIN session_aliases sa ON sa.session_id = m.session_id
835
857
  LEFT JOIN projects p ON p.id = s.project_id
836
858
  WHERE m.cluster_id = ?
837
- ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:Je(n),members:s.map(If)}}function tc(e,t,n){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof n!="string"||!n.trim())throw new Error("fixSummary is required");let s=h();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!s.prepare("SELECT 1 FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?").get(e,t))throw new Error(`session ${t} is not a member of cluster ${e}`);s.prepare(`UPDATE bug_pattern_clusters
859
+ ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:Ke(n),members:s.map(m_)}}function Sc(e,t,n){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof n!="string"||!n.trim())throw new Error("fixSummary is required");let s=_();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!s.prepare("SELECT 1 FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?").get(e,t))throw new Error(`session ${t} is not a member of cluster ${e}`);s.prepare(`UPDATE bug_pattern_clusters
838
860
  SET resolved_in_session_id = ?, fix_summary = ?
839
- WHERE id = ?`).run(t,n.trim(),e),rn(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Je(a)}}function nc(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("memberSessionIds must be a non-empty array");let n=h(),s=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=n.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
840
- WHERE cluster_id = ? AND session_id IN (${o})`).all(e,...r);if(a.length===0)throw new Error(`none of the supplied session_ids are members of cluster ${e}`);let c=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;if(a.length>=c)throw new Error(`cannot split: would leave the original cluster empty (move ${a.length} of ${c})`);let u=Ya(),d=new Date().toISOString(),p=[];n.transaction(()=>{n.prepare(`INSERT INTO bug_pattern_clusters
861
+ WHERE id = ?`).run(t,n.trim(),e),cn(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Ke(a)}}function Tc(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("memberSessionIds must be a non-empty array");let n=_(),s=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=n.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
862
+ WHERE cluster_id = ? AND session_id IN (${o})`).all(e,...r);if(a.length===0)throw new Error(`none of the supplied session_ids are members of cluster ${e}`);let c=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;if(a.length>=c)throw new Error(`cannot split: would leave the original cluster empty (move ${a.length} of ${c})`);let u=mc(),d=new Date().toISOString(),p=[];n.transaction(()=>{n.prepare(`INSERT INTO bug_pattern_clusters
841
863
  (id, signature_hash, example_message, occurrence_count,
842
864
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
843
- VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(u,s.signature_hash,s.example_message,a.length,s.first_seen_at,d);let y=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
844
- VALUES (?, ?, ?)`),b=n.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let k of a)y.run(u,k.session_id,k.matched_at),b.run(e,k.session_id);let R=c-a.length;n.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(R,e),p=n.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),rn(e),rn(u);let f=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),S=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:Je(f),split:Je(S),movedMembers:p.map(Ka)}}function rn(e){try{vf();let t=Va(e);if(!t)return;let n=Ga(Wr,`${e}.json`),s={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};Of(n,JSON.stringify(s,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function sc(e){return e?h().prepare(`SELECT * FROM bug_pattern_clusters
865
+ VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(u,s.signature_hash,s.example_message,a.length,s.first_seen_at,d);let T=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
866
+ VALUES (?, ?, ?)`),S=n.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let A of a)T.run(u,A.session_id,A.matched_at),S.run(e,A.session_id);let w=c-a.length;n.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(w,e),p=n.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),cn(e),cn(u);let f=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),b=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:Ke(f),split:Ke(b),movedMembers:p.map(gc)}}function cn(e){try{p_();let t=_c(e);if(!t)return;let n=pc(Kr,`${e}.json`),s={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};l_(n,JSON.stringify(s,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function yc(e){return e?_().prepare(`SELECT * FROM bug_pattern_clusters
845
867
  WHERE signature_hash = ?
846
- ORDER BY last_seen_at DESC, id ASC`).all(e).map(Je):[]}function rc(e){if(!e)return new Set;let n=h().prepare(`SELECT DISTINCT m.session_id
868
+ ORDER BY last_seen_at DESC, id ASC`).all(e).map(Ke):[]}function wc(e){if(!e)return new Set;let n=_().prepare(`SELECT DISTINCT m.session_id
847
869
  FROM bug_pattern_members m
848
870
  JOIN bug_pattern_clusters c ON c.id = m.cluster_id
849
- WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var a_=/\b0x[0-9a-fA-F]+\b/g,c_=/\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,l_=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,u_=/:\d+:\d+/g,d_=/\bline\s+\d+\b/gi,p_=/\bcolumn\s+\d+\b/gi,m_=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,g_=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,f_=/\b\d{4,}\b/g,__=/(['"`])[^'"`\n]{1,128}\1/g;function h_(e){if(!e)return"";let t=String(e);return t=t.replace(a_,"<hex>"),t=t.replace(c_,"<uuid>"),t=t.replace(l_,"<ts>"),t=t.replace(u_,":<line>:<col>"),t=t.replace(d_,"line <n>"),t=t.replace(p_,"column <n>"),t=t.replace(m_,"pid <n>"),t=t.replace(g_,"port <n>"),t=t.replace(f_,"<num>"),t=t.replace(__,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function E_(e){let t=(e.error_type??"unknown").toLowerCase().trim(),n=h_(e.snippet??e.message_hash??""),s=`${t}|${n}`;return i_("sha256").update(s).digest("hex").slice(0,16)}function b_(e){let t=h(),n=["oi.bug_signatures IS NOT NULL"],s=[];e&&(n.push("p.name = ?"),s.push(e));let r=`WHERE ${n.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
871
+ WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var W_=/\b0x[0-9a-fA-F]+\b/g,q_=/\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,X_=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,J_=/:\d+:\d+/g,G_=/\bline\s+\d+\b/gi,Y_=/\bcolumn\s+\d+\b/gi,z_=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,K_=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,V_=/\b\d{4,}\b/g,Q_=/(['"`])[^'"`\n]{1,128}\1/g;function Z_(e){if(!e)return"";let t=String(e);return t=t.replace(W_,"<hex>"),t=t.replace(q_,"<uuid>"),t=t.replace(X_,"<ts>"),t=t.replace(J_,":<line>:<col>"),t=t.replace(G_,"line <n>"),t=t.replace(Y_,"column <n>"),t=t.replace(z_,"pid <n>"),t=t.replace(K_,"port <n>"),t=t.replace(V_,"<num>"),t=t.replace(Q_,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function eh(e){let t=(e.error_type??"unknown").toLowerCase().trim(),n=Z_(e.snippet??e.message_hash??""),s=`${t}|${n}`;return B_("sha256").update(s).digest("hex").slice(0,16)}function th(e){let t=_(),n=["oi.bug_signatures IS NOT NULL"],s=[];e&&(n.push("p.name = ?"),s.push(e));let r=`WHERE ${n.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
850
872
  p.name AS project,
851
873
  s.started_at AS started_at,
852
874
  oi.bug_signatures AS bug_signatures
853
875
  FROM session_output_index oi
854
876
  LEFT JOIN sessions s ON s.id = oi.session_id
855
877
  LEFT JOIN projects p ON p.id = s.project_id
856
- ${r}`).all(...s),a=[];for(let c of o){if(!c.bug_signatures)continue;let u=[];try{let d=JSON.parse(c.bug_signatures);Array.isArray(d)&&(u=d)}catch{continue}for(let d of u){if(!d||typeof d!="object"||!(d.snippet??"").trim())continue;let f=E_(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:f})}}return a}function S_(e){let t=new Map;for(let s of e){let r=t.get(s.fingerprint);r||(r=[],t.set(s.fingerprint,r)),r.push(s)}let n=[];for(let[s,r]of t){let o=new Set,a=[];for(let p of r)o.has(p.session_id)||(o.add(p.session_id),a.push(p));let c=[...a].sort((p,f)=>{let S=p.started_at??"",y=f.started_at??"";return S&&y?S<y?-1:S>y?1:0:S?-1:y?1:0}),u=c.find(p=>p.started_at),d=[...c].reverse().find(p=>p.started_at);n.push({fingerprint:s,example_message:a[0].signature.snippet??a[0].signature.message_hash??"",members:a,first_seen_at:u?.started_at??new Date().toISOString(),last_seen_at:d?.started_at??new Date().toISOString()})}return n}function y_(e,t){if(e.length!==t.length)return 0;let n=0,s=0,r=0;for(let a=0;a<e.length;a++)n+=e[a]*t[a],s+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(s)*Math.sqrt(r);return o>0?n/o:0}function T_(e){let{records:t,vectors:n,epsilon:s,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let p=0;p<o;p++){let f=[];for(let S=0;S<o;S++){if(p===S)continue;1-y_(n[p],n[S])<=s&&f.push(S)}a.push(f)}let c=new Array(o).fill(!1),u=new Array(o).fill(-1),d=[];for(let p=0;p<o;p++){if(c[p])continue;c[p]=!0;let f=a[p];if(f.length<r)continue;let S=d.length;d.push({members:[t[p]]}),u[p]=S;let y=[...f];for(;y.length>0;){let b=y.shift();if(!c[b]&&(c[b]=!0,a[b].length>=r))for(let R of a[b])(!c[R]||u[R]===-1)&&y.push(R);u[b]===-1&&(u[b]=S,d[S].members.push(t[b]))}}return d}async function w_(e,t,n,s){if(e.length===0)return[];let r=e.map(u=>{let d=u.signature.snippet??u.signature.message_hash??"";return`${u.signature.error_type??""}: ${d}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let a=T_({records:e,vectors:o,epsilon:n,minPts:s}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,p=[];for(let k of u.members)d.has(k.session_id)||(d.add(k.session_id),p.push(k));if(p.length===0)continue;let S=`sem:${[...p.map(k=>k.fingerprint)].sort()[0]}`,y=[...p].sort((k,M)=>{let P=k.started_at??"",G=M.started_at??"";return P<G?-1:P>G?1:0}),b=y.find(k=>k.started_at),R=[...y].reverse().find(k=>k.started_at);c.push({fingerprint:S,example_message:p[0].signature.snippet??p[0].signature.message_hash??"",members:p,first_seen_at:b?.started_at??new Date().toISOString(),last_seen_at:R?.started_at??new Date().toISOString()})}return c}function pc(e,t){let n={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let s of e){if(s.members.length<t)continue;let r=sc(s.fingerprint),o=rc(s.fingerprint),a=s.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=za({signature_hash:s.fingerprint,example_message:s.example_message.slice(0,256),member_session_ids:s.members.map(p=>p.session_id),first_seen_at:s.first_seen_at,last_seen_at:s.last_seen_at});n.clusters_created+=1,n.members_added+=d.members.length,n.cluster_ids.push(d.cluster.id);continue}if(a.length===0){n.cluster_ids.push(r[0].id);continue}let c=r[0],u=Qa(c.id,a);u.added>0&&(n.clusters_merged+=1,n.members_added+=u.added),n.cluster_ids.push(c.id)}return n}async function mc(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),n=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),s=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=b_(e.project),a=new Set(o.map(P=>P.session_id)),c=S_(o),u=[],d=!1;if(e.semantic){let P=e.embedder??null;if(!P)try{P=await A_()}catch(G){let v=(G instanceof Error?G.message:String(G)).split(`
857
- `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${v}
858
- Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(P){let G=[];for(let D of c)D.members.length===1&&G.push(D.members[0]);G.length>=2&&(u=await w_(G,P,s,r))}}let p=c.filter(P=>P.members.length>=t),f=u.filter(P=>P.members.length>=t),S=pc(p,t),y=pc(f,t),b=[...S.cluster_ids,...y.cluster_ids],R=Array.from(new Set(b)),k=[];if(R.length>0){let P=h(),G=R.map(()=>"?").join(","),D=P.prepare(`SELECT * FROM bug_pattern_clusters
859
- WHERE id IN (${G})
878
+ ${r}`).all(...s),a=[];for(let c of o){if(!c.bug_signatures)continue;let u=[];try{let d=JSON.parse(c.bug_signatures);Array.isArray(d)&&(u=d)}catch{continue}for(let d of u){if(!d||typeof d!="object"||!(d.snippet??"").trim())continue;let f=eh(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:f})}}return a}function nh(e){let t=new Map;for(let s of e){let r=t.get(s.fingerprint);r||(r=[],t.set(s.fingerprint,r)),r.push(s)}let n=[];for(let[s,r]of t){let o=new Set,a=[];for(let p of r)o.has(p.session_id)||(o.add(p.session_id),a.push(p));let c=[...a].sort((p,f)=>{let b=p.started_at??"",T=f.started_at??"";return b&&T?b<T?-1:b>T?1:0:b?-1:T?1:0}),u=c.find(p=>p.started_at),d=[...c].reverse().find(p=>p.started_at);n.push({fingerprint:s,example_message:a[0].signature.snippet??a[0].signature.message_hash??"",members:a,first_seen_at:u?.started_at??new Date().toISOString(),last_seen_at:d?.started_at??new Date().toISOString()})}return n}function sh(e,t){if(e.length!==t.length)return 0;let n=0,s=0,r=0;for(let a=0;a<e.length;a++)n+=e[a]*t[a],s+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(s)*Math.sqrt(r);return o>0?n/o:0}function rh(e){let{records:t,vectors:n,epsilon:s,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let p=0;p<o;p++){let f=[];for(let b=0;b<o;b++){if(p===b)continue;1-sh(n[p],n[b])<=s&&f.push(b)}a.push(f)}let c=new Array(o).fill(!1),u=new Array(o).fill(-1),d=[];for(let p=0;p<o;p++){if(c[p])continue;c[p]=!0;let f=a[p];if(f.length<r)continue;let b=d.length;d.push({members:[t[p]]}),u[p]=b;let T=[...f];for(;T.length>0;){let S=T.shift();if(!c[S]&&(c[S]=!0,a[S].length>=r))for(let w of a[S])(!c[w]||u[w]===-1)&&T.push(w);u[S]===-1&&(u[S]=b,d[b].members.push(t[S]))}}return d}async function oh(e,t,n,s){if(e.length===0)return[];let r=e.map(u=>{let d=u.signature.snippet??u.signature.message_hash??"";return`${u.signature.error_type??""}: ${d}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let a=rh({records:e,vectors:o,epsilon:n,minPts:s}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,p=[];for(let A of u.members)d.has(A.session_id)||(d.add(A.session_id),p.push(A));if(p.length===0)continue;let b=`sem:${[...p.map(A=>A.fingerprint)].sort()[0]}`,T=[...p].sort((A,I)=>{let j=A.started_at??"",J=I.started_at??"";return j<J?-1:j>J?1:0}),S=T.find(A=>A.started_at),w=[...T].reverse().find(A=>A.started_at);c.push({fingerprint:b,example_message:p[0].signature.snippet??p[0].signature.message_hash??"",members:p,first_seen_at:S?.started_at??new Date().toISOString(),last_seen_at:w?.started_at??new Date().toISOString()})}return c}function Cc(e,t){let n={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let s of e){if(s.members.length<t)continue;let r=yc(s.fingerprint),o=wc(s.fingerprint),a=s.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=fc({signature_hash:s.fingerprint,example_message:s.example_message.slice(0,256),member_session_ids:s.members.map(p=>p.session_id),first_seen_at:s.first_seen_at,last_seen_at:s.last_seen_at});n.clusters_created+=1,n.members_added+=d.members.length,n.cluster_ids.push(d.cluster.id);continue}if(a.length===0){n.cluster_ids.push(r[0].id);continue}let c=r[0],u=hc(c.id,a);u.added>0&&(n.clusters_merged+=1,n.members_added+=u.added),n.cluster_ids.push(c.id)}return n}async function vc(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),n=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),s=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=th(e.project),a=new Set(o.map(j=>j.session_id)),c=nh(o),u=[],d=!1;if(e.semantic){let j=e.embedder??null;if(!j)try{j=await ch()}catch(J){let F=(J instanceof Error?J.message:String(J)).split(`
879
+ `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${F}
880
+ Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(j){let J=[];for(let M of c)M.members.length===1&&J.push(M.members[0]);J.length>=2&&(u=await oh(J,j,s,r))}}let p=c.filter(j=>j.members.length>=t),f=u.filter(j=>j.members.length>=t),b=Cc(p,t),T=Cc(f,t),S=[...b.cluster_ids,...T.cluster_ids],w=Array.from(new Set(S)),A=[];if(w.length>0){let j=_(),J=w.map(()=>"?").join(","),M=j.prepare(`SELECT * FROM bug_pattern_clusters
881
+ WHERE id IN (${J})
860
882
  ORDER BY occurrence_count DESC, last_seen_at DESC
861
- LIMIT ?`).all(...R,n);for(let v of D)k.push({id:v.id,signature_hash:v.signature_hash,example_message:v.example_message,occurrence_count:v.occurrence_count,first_seen_at:v.first_seen_at,last_seen_at:v.last_seen_at,resolved_in_session_id:v.resolved_in_session_id,fix_summary:v.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:p.length,semantic_groups:f.length,clusters_created:S.clusters_created+y.clusters_created,clusters_merged:S.clusters_merged+y.clusters_merged,members_added:S.members_added+y.members_added,semantic_skipped:d},clusters:k}}var R_=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:n}=await Promise.resolve().then(()=>(bt(),dc));return n().loaded||await t(),e},k_=R_;async function A_(){return k_()}q();q();ne();import{writeFileSync as gc,readFileSync as Jx,existsSync as fc,mkdirSync as _c,readdirSync as Gx}from"node:fs";import{join as rs}from"node:path";var x_=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),hc=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),N_=new Set(["pending","approved","rejected"]),O_=new Set(["L1","L2","L3","L4","user"]),zr=rs(W,"links"),Vr=rs(W,"suggestions"),C_=rs(Vr,"index.json");function L_(){V(),fc(zr)||_c(zr,{recursive:!0})}function v_(){V(),fc(Vr)||_c(Vr,{recursive:!0})}function Ec(e){try{return JSON.parse(e)}catch{return e}}function os(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:Ec(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Qr(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:Ec(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function bc(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function Zr(e){if(!x_.has(e))throw new Error(`invalid link_type: ${e}`)}function I_(e){if(!hc.has(e))throw new Error(`invalid source: ${e}`)}function Sc(e){if(!O_.has(e))throw new Error(`invalid inferred_by: ${e}`)}function yc(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 Tc(e){yc(e.source_session_id,e.target_session_id),Zr(e.link_type),I_(e.source),bc(e.confidence);let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
883
+ LIMIT ?`).all(...w,n);for(let F of M)A.push({id:F.id,signature_hash:F.signature_hash,example_message:F.example_message,occurrence_count:F.occurrence_count,first_seen_at:F.first_seen_at,last_seen_at:F.last_seen_at,resolved_in_session_id:F.resolved_in_session_id,fix_summary:F.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:p.length,semantic_groups:f.length,clusters_created:b.clusters_created+T.clusters_created,clusters_merged:b.clusters_merged+T.clusters_merged,members_added:b.members_added+T.members_added,semantic_skipped:d},clusters:A}}var ih=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:n}=await Promise.resolve().then(()=>(We(),Lc));return n().loaded||await t(),e},ah=ih;async function ch(){return ah()}H();H();Z();import{writeFileSync as Ic,readFileSync as UN,existsSync as Mc,mkdirSync as Dc,readdirSync as HN}from"node:fs";import{join as us}from"node:path";var lh=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),jc=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),uh=new Set(["pending","approved","rejected"]),dh=new Set(["L1","L2","L3","L4","user"]),ro=us(W,"links"),oo=us(W,"suggestions"),ph=us(oo,"index.json");function mh(){K(),Mc(ro)||Dc(ro,{recursive:!0})}function gh(){K(),Mc(oo)||Dc(oo,{recursive:!0})}function Pc(e){try{return JSON.parse(e)}catch{return e}}function ds(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:Pc(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function io(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:Pc(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function Fc(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function ao(e){if(!lh.has(e))throw new Error(`invalid link_type: ${e}`)}function fh(e){if(!jc.has(e))throw new Error(`invalid source: ${e}`)}function $c(e){if(!dh.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Uc(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 Hc(e){Uc(e.source_session_id,e.target_session_id),ao(e.link_type),fh(e.source),Fc(e.confidence);let t=_(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
862
884
  (source_session_id, target_session_id, link_type,
863
885
  confidence, source, evidence, approved,
864
886
  created_at, updated_at)
@@ -871,11 +893,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
871
893
  updated_at = excluded.updated_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,e.source,s,r,n,n);let o=t.prepare(`SELECT * FROM session_links
872
894
  WHERE source_session_id = ?
873
895
  AND target_session_id = ?
874
- 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 to(e.source_session_id),os(o)}function is(e={}){let t=h(),n=[],s=[];e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.linkType&&(Zr(e.linkType),n.push("link_type = ?"),s.push(e.linkType)),e.approvedOnly&&n.push("approved = 1");let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
896
+ AND link_type = ?`).get(e.source_session_id,e.target_session_id,e.link_type);if(!o)throw new Error("createLink succeeded but read-back failed");return lo(e.source_session_id),ds(o)}function ps(e={}){let t=_(),n=[],s=[];e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.linkType&&(ao(e.linkType),n.push("link_type = ?"),s.push(e.linkType)),e.approvedOnly&&n.push("approved = 1");let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
875
897
  ORDER BY confidence DESC, updated_at DESC
876
- LIMIT ?`).all(...s,o).map(os)}function un(e){return h().prepare(`SELECT * FROM session_links
898
+ LIMIT ?`).all(...s,o).map(ds)}function mn(e){return _().prepare(`SELECT * FROM session_links
877
899
  WHERE source_session_id = ? OR target_session_id = ?
878
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(os)}function wc(e){let t=h(),n=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!n)return{removed:0,sourceSessionId:null};let s=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return s.changes>0&&to(n.source_session_id),{removed:s.changes,sourceSessionId:n.source_session_id}}function dn(e){yc(e.source_session_id,e.target_session_id),Zr(e.link_type),bc(e.confidence),Sc(e.inferred_by);let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
900
+ ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(ds)}function Bc(e){let t=_(),n=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!n)return{removed:0,sourceSessionId:null};let s=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return s.changes>0&&lo(n.source_session_id),{removed:s.changes,sourceSessionId:n.source_session_id}}function gn(e,t={}){Uc(e.source_session_id,e.target_session_id),ao(e.link_type),Fc(e.confidence),$c(e.inferred_by);let n=_(),s=new Date().toISOString(),r=JSON.stringify(e.evidence??null);n.prepare(`INSERT INTO session_link_suggestions
879
901
  (source_session_id, target_session_id, link_type,
880
902
  confidence, evidence, status, inferred_by,
881
903
  created_at, decided_at)
@@ -891,13 +913,13 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
891
913
  THEN excluded.evidence
892
914
  ELSE session_link_suggestions.evidence
893
915
  END,
894
- created_at = session_link_suggestions.created_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,s,e.inferred_by,n);let r=t.prepare(`SELECT * FROM session_link_suggestions
916
+ created_at = session_link_suggestions.created_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,r,e.inferred_by,s);let o=n.prepare(`SELECT * FROM session_link_suggestions
895
917
  WHERE source_session_id = ?
896
918
  AND target_session_id = ?
897
919
  AND link_type = ?
898
- 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 Rc(),Qr(r)}function St(e={}){let t=h(),n=[],s=[];if(e.status){if(!N_.has(e.status))throw new Error(`invalid status: ${e.status}`);n.push("status = ?"),s.push(e.status)}e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.inferredBy&&(Sc(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
920
+ AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!o)throw new Error("createSuggestion succeeded but read-back failed");return t.deferMirror||ct(),io(o)}function yt(e={}){let t=_(),n=[],s=[];if(e.status){if(!uh.has(e.status))throw new Error(`invalid status: ${e.status}`);n.push("status = ?"),s.push(e.status)}e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.inferredBy&&($c(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
899
921
  ORDER BY confidence DESC, created_at DESC
900
- LIMIT ?`).all(...s,o).map(Qr)}function eo(e,t,n={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let s=n.source??"manual";if(!hc.has(s))throw new Error(`invalid source: ${s}`);let r=h(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let a=new Date().toISOString(),c;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
922
+ LIMIT ?`).all(...s,o).map(io)}function co(e,t,n={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let s=n.source??"manual";if(!jc.has(s))throw new Error(`invalid source: ${s}`);let r=_(),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
901
923
  SET status = ?, decided_at = ?
902
924
  WHERE id = ?`).run(t,a,e),t==="approved"&&(r.prepare(`INSERT INTO session_links
903
925
  (source_session_id, target_session_id, link_type,
@@ -912,7 +934,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
912
934
  updated_at = excluded.updated_at`).run(o.source_session_id,o.target_session_id,o.link_type,o.confidence,s,o.evidence,a,a),c=r.prepare(`SELECT * FROM session_links
913
935
  WHERE source_session_id = ?
914
936
  AND target_session_id = ?
915
- AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),Rc(),t==="approved"&&to(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:Qr(u),link:c?os(c):null}}function to(e){try{L_();let t=is({sourceSessionId:e}),n=rs(zr,`${e}.json`);if(t.length===0)return;let s={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};gc(n,JSON.stringify(s,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function Rc(){try{v_();let e=St({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};gc(C_,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}q();ne();import{writeFileSync as M_,readFileSync as Zx,existsSync as j_,mkdirSync as D_,readdirSync as eN}from"node:fs";import{join as kc}from"node:path";var no=kc(W,"output-index");function P_(){V(),j_(no)||D_(no,{recursive:!0})}function pn(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function F_(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function Ac(e){return{session_id:e.session_id,files_written:pn(e.files_written),brands_mentioned:pn(e.brands_mentioned),terms_introduced:pn(e.terms_introduced),plan_ids_referenced:pn(e.plan_ids_referenced),bug_signatures:pn(e.bug_signatures),raw_extraction:F_(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function xc(e){if(!e.session_id)throw new Error("session_id is required");let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),a=JSON.stringify(e.plan_ids_referenced??[]),c=JSON.stringify(e.bug_signatures??[]),u=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),d=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
937
+ AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),ct(),t==="approved"&&lo(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:io(u),link:c?ds(c):null}}function lo(e){try{mh();let t=ps({sourceSessionId:e}),n=us(ro,`${e}.json`);if(t.length===0)return;let s={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};Ic(n,JSON.stringify(s,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function ct(){try{gh();let e=yt({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};Ic(ph,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}H();Z();import{writeFileSync as _h,readFileSync as GN,existsSync as hh,mkdirSync as Eh,readdirSync as YN}from"node:fs";import{join as Wc}from"node:path";var uo=Wc(W,"output-index");function bh(){K(),hh(uo)||Eh(uo,{recursive:!0})}function fn(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Sh(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function qc(e){return{session_id:e.session_id,files_written:fn(e.files_written),brands_mentioned:fn(e.brands_mentioned),terms_introduced:fn(e.terms_introduced),plan_ids_referenced:fn(e.plan_ids_referenced),bug_signatures:fn(e.bug_signatures),raw_extraction:Sh(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function Xc(e){if(!e.session_id)throw new Error("session_id is required");let t=_(),n=new Date().toISOString(),s=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),a=JSON.stringify(e.plan_ids_referenced??[]),c=JSON.stringify(e.bug_signatures??[]),u=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),d=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
916
938
  (session_id, files_written, brands_mentioned, terms_introduced,
917
939
  plan_ids_referenced, bug_signatures, raw_extraction,
918
940
  extracted_at, extractor_version)
@@ -925,34 +947,39 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
925
947
  bug_signatures = excluded.bug_signatures,
926
948
  raw_extraction = excluded.raw_extraction,
927
949
  extracted_at = excluded.extracted_at,
928
- extractor_version = excluded.extractor_version`).run(e.session_id,s,r,o,a,c,u,n,d);let p=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!p)throw new Error("setOutputIndex succeeded but read-back failed");let f=Ac(p);return $_(e.session_id),f}function ot(e){let n=h().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return n?Ac(n):null}function $_(e){try{P_();let t=ot(e);if(!t)return;let n=kc(no,`${e}.json`),s={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};M_(n,JSON.stringify(s,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var so={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},U_=2,H_=.25,B_=5,W_=60,q_=25;function as(e){return e.trim().toLowerCase()}function X_(e){let t=new Set;for(let n of e.files_written)t.add(`file:${as(n)}`);for(let n of e.brands_mentioned)t.add(`brand:${as(n)}`);for(let n of e.terms_introduced)t.add(`term:${as(n)}`);for(let n of e.plan_ids_referenced)t.add(`plan:${as(n)}`);return t}function J_(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/W_);return Math.max(.2,t)}function G_(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function Y_(e){let t=ot(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(n=>n.term),plan_ids_referenced:t.plan_ids_referenced}:null}function K_(e){return h().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
950
+ extractor_version = excluded.extractor_version`).run(e.session_id,s,r,o,a,c,u,n,d);let p=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!p)throw new Error("setOutputIndex succeeded but read-back failed");let f=qc(p);return Th(e.session_id),f}function lt(e){let n=_().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return n?qc(n):null}function Th(e){try{bh();let t=lt(e);if(!t)return;let n=Wc(uo,`${e}.json`),s={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};_h(n,JSON.stringify(s,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var po={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},yh=2,wh=.25,Rh=5,kh=60,Ah=25;function ms(e){return e.trim().toLowerCase()}function xh(e){let t=new Set;for(let n of e.files_written)t.add(`file:${ms(n)}`);for(let n of e.brands_mentioned)t.add(`brand:${ms(n)}`);for(let n of e.terms_introduced)t.add(`term:${ms(n)}`);for(let n of e.plan_ids_referenced)t.add(`plan:${ms(n)}`);return t}function Nh(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/kh);return Math.max(.2,t)}function Oh(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function Lh(e){let t=lt(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(n=>n.term),plan_ids_referenced:t.plan_ids_referenced}:null}function Ch(e){return _().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at,
951
+ s.rowid AS rowid,
952
+ (SELECT COALESCE(MAX(m.rowid), 0) FROM messages m WHERE m.session_id = s.id)
953
+ AS maxMessageRowid
929
954
  FROM sessions s
930
955
  JOIN session_output_index oi ON oi.session_id = s.id
931
956
  WHERE s.project_id = ?
932
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function z_(e,t){let n=new Map,s=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let a=t.get(o.id);if(!a)continue;let c=X_(a);if(c.size!==0){s.set(o.id,c);for(let u of c){let d=n.get(u);d?d.push(o.id):n.set(u,[o.id])}}}return{posting:n,vocab:s,startedAt:r}}function V_(e,t){let n=t.vocab.get(e),s=t.startedAt.get(e)??null;if(!n||!s)return[];let r=new Map;for(let a of n){let c=t.posting.get(a);if(c)for(let u of c){if(u===e)continue;let d=t.startedAt.get(u);if(!d||d>=s)continue;let p=r.get(u);p?p.push(a):r.set(u,[a])}}let o=[];for(let[a,c]of r){let u=c.length;if(u<U_)continue;let d=t.startedAt.get(a)??null,p=G_(s,d),f=J_(p),S=Math.min(1,u/B_*f);if(S<H_)continue;let y=c.slice(0,12);o.push({target_session_id:a,matched_terms:y,overlap:u,days_apart:Math.round(p*10)/10,recency:Math.round(f*1e3)/1e3,confidence:Math.round(S*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,q_)}async function Nc(e){if(so.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=K_(e.projectId),n=new Map;for(let a of t){let c=Y_(a.id);c&&n.set(a.id,c)}let s=z_(t,n),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let a of t){if(e.signal?.aborted)break;r.current_session_id=a.id,e.onProgress?.({...r});let c=V_(a.id,s);for(let u of c)try{let d=dn({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}}q();var Q_=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,Z_=[/\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],eh=.95,th=.85;var ro=50;function nh(e){if(!e)return[];let t=new Set,n=[],s=e.match(Q_);if(!s)return n;for(let r of s){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),n.push(o),n.length>=ro))break}return n}function sh(e){if(!e)return[];let t=new Set,n=[];for(let s of Z_){s.lastIndex=0;let r=e.match(s);if(r){for(let o of r){let a=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!a||a.length>64)&&!t.has(a)&&(t.add(a),n.push(a),n.length>=ro))break}if(n.length>=ro)break}}return n}function Oc(e){let t=h();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function Cc(e){let t=h();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
933
- s.project_id
934
- FROM messages m
935
- JOIN sessions s ON s.id = m.session_id
936
- WHERE s.project_id = ?
937
- AND m.is_sidechain = 0
938
- AND m.content_text IS NOT NULL
939
- AND length(m.content_text) > 0`).all(e):t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
957
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function vh(e,t){let n=new Map,s=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let a=t.get(o.id);if(!a)continue;let c=xh(a);if(c.size!==0){s.set(o.id,c);for(let u of c){let d=n.get(u);d?d.push(o.id):n.set(u,[o.id])}}}return{posting:n,vocab:s,startedAt:r}}function Ih(e,t){let n=t.vocab.get(e),s=t.startedAt.get(e)??null;if(!n||!s)return[];let r=new Map;for(let a of n){let c=t.posting.get(a);if(c)for(let u of c){if(u===e)continue;let d=t.startedAt.get(u);if(!d||d>=s)continue;let p=r.get(u);p?p.push(a):r.set(u,[a])}}let o=[];for(let[a,c]of r){let u=c.length;if(u<yh)continue;let d=t.startedAt.get(a)??null,p=Oh(s,d),f=Nh(p),b=Math.min(1,u/Rh*f);if(b<wh)continue;let T=c.slice(0,12);o.push({target_session_id:a,matched_terms:T,overlap:u,days_apart:Math.round(p*10)/10,recency:Math.round(f*1e3)/1e3,confidence:Math.round(b*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,Ah)}async function Jc(e){if(po.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=Ch(e.projectId),n=new Map;for(let c of t){let u=Lh(c.id);u&&n.set(c.id,u)}let s=vh(t,n),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let c of t){if(e.signal?.aborted)break;if(typeof e.sinceRowid=="number"&&c.maxMessageRowid<=e.sinceRowid)continue;r.current_session_id=c.id,e.onProgress?.({...r});let u=Ih(c.id,s);for(let d of u)o.push({source_session_id:c.id,target_session_id:d.target_session_id,confidence:d.confidence,matched_terms:d.matched_terms,overlap:d.overlap,recency:d.recency,days_apart:d.days_apart});r.processed_sessions+=1,e.onProgress?.({...r})}let a=[];return o.length>0&&(_().transaction(()=>{for(let d of o)try{let p=gn({source_session_id:d.source_session_id,target_session_id:d.target_session_id,link_type:"citation",confidence:d.confidence,evidence:{matched_terms:d.matched_terms,overlap_count:d.overlap,recency:d.recency,days_apart:d.days_apart},inferred_by:"L2"},{deferMirror:!0});a.push(p.id),r.suggestions_created+=1}catch(p){console.error("[citation-inference] createSuggestion failed:",p)}})(),ct()),r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:a}}H();var Mh=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,Dh=[/\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],jh=.95,Ph=.85;var mo=50;function Fh(e){if(!e)return[];let t=new Set,n=[],s=e.match(Mh);if(!s)return n;for(let r of s){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),n.push(o),n.length>=mo))break}return n}function $h(e){if(!e)return[];let t=new Set,n=[];for(let s of Dh){s.lastIndex=0;let r=e.match(s);if(r){for(let o of r){let a=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!a||a.length>64)&&!t.has(a)&&(t.add(a),n.push(a),n.length>=mo))break}if(n.length>=mo)break}}return n}function Gc(e){let t=_();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 Yc(e,t){let n=_(),s=typeof t=="number";if(typeof e=="number"){let o=`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
940
958
  s.project_id
941
959
  FROM messages m
942
960
  JOIN sessions s ON s.id = m.session_id
943
- WHERE m.is_sidechain = 0
961
+ WHERE s.project_id = ?
962
+ AND m.is_sidechain = 0
944
963
  AND m.content_text IS NOT NULL
945
- AND length(m.content_text) > 0`).all()}function rh(e){let t=h(),n=typeof e=="number"?"WHERE s.project_id = ?":"",s=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
964
+ AND length(m.content_text) > 0`+(s?`
965
+ AND m.rowid > ?`:"");return s?n.prepare(o).all(e,t):n.prepare(o).all(e)}let r=`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
966
+ s.project_id
967
+ FROM messages m
968
+ JOIN sessions s ON s.id = m.session_id
969
+ WHERE m.is_sidechain = 0
970
+ AND m.content_text IS NOT NULL
971
+ AND length(m.content_text) > 0`+(s?`
972
+ AND m.rowid > ?`:"");return s?n.prepare(r).all(t):n.prepare(r).all()}function Uh(e){let t=_(),n=typeof e=="number"?"WHERE s.project_id = ?":"",s=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
946
973
  s.project_id AS project_id,
947
974
  oi.plan_ids_referenced AS plan_ids_json
948
975
  FROM session_output_index oi
949
976
  JOIN sessions s ON s.id = oi.session_id
950
- ${n}`).all(...s)}function oh(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>typeof n=="string")}catch{}return[]}function Lc(e={}){if(so.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Oc(e.projectId),n=new Set(t.map(o=>o.id)),s=new Map;for(let o of t)s.set(o.id,o.project_id);let r=0;for(let o of Cc(e.projectId)){if(e.signal?.aborted)break;let a=nh(o.content_text);if(a.length===0)continue;let c=s.get(o.session_id);if(c!==void 0)for(let u of a){if(u===o.session_id)continue;let d=s.get(u);if(!(d===void 0&&!n.has(u))&&!(d!==void 0&&c!==void 0&&d!==c))try{dn({source_session_id:o.session_id,target_session_id:u,link_type:"citation",confidence:eh,evidence:{matched_uuid:u,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function vc(e={}){let t=rh(e.projectId);if(t.length===0)return{created:0};let n=new Map;for(let c of t){let u=oh(c.plan_ids_json);for(let d of u){let p=d.trim().toLowerCase();if(!p)continue;let f=n.get(p);f?f.push({id:c.session_id,project_id:c.project_id}):n.set(p,[{id:c.session_id,project_id:c.project_id}])}}if(n.size===0)return{created:0};let s=Oc(e.projectId),r=new Map;for(let c of s)r.set(c.id,c.project_id);let o=0,a=new Map;for(let c of Cc(e.projectId)){if(e.signal?.aborted)break;let u=sh(c.content_text);if(u.length===0)continue;let d=r.get(c.session_id);if(d!==void 0)for(let p of u){let f=n.get(p);if(f)for(let S of f){if(S.id===c.session_id||S.project_id!==d)continue;let y=a.get(c.session_id);y||(y=new Map,a.set(c.session_id,y));let b=y.get(S.id);b||(b=new Set,y.set(S.id,b)),b.add(p)}}}for(let[c,u]of a)for(let[d,p]of u)try{dn({source_session_id:c,target_session_id:d,link_type:"citation",confidence:th,evidence:{matched_plan_ids:Array.from(p).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}q();Te();import{existsSync as Fh,mkdirSync as $h,writeFileSync as Uh}from"node:fs";import{homedir as Hh}from"node:os";import{join as po}from"node:path";Br();q();ne();import{existsSync as Uc,mkdirSync as Ih,readFileSync as Mh,writeFileSync as jh}from"node:fs";import{homedir as Dh}from"node:os";import{join as Hc}from"node:path";import{z as Ce}from"zod";function Bc(){return process.env.RECALL_HOME??Hc(Dh(),".recall")}function Ph(){let e=Bc();Uc(e)||Ih(e,{recursive:!0})}function Wc(){return Hc(Bc(),"config.json")}var ms=Ce.object({enabled:Ce.boolean().default(!1),model:Ce.string().optional(),ratePerMinute:Ce.number().int().min(1).max(600).default(30),lastProcessedSessionId:Ce.string().nullable().default(null),backfillPaused:Ce.boolean().default(!1),autoExtractEnabled:Ce.boolean().default(!1),autoExtractIntervalMinutes:Ce.number().int().min(5).max(720).default(60),autoExtractBatchSize:Ce.number().int().min(1).max(20).default(1),autoResumeWorker:Ce.boolean().default(!1)}),ps={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function qc(){let e=Wc();if(!Uc(e))return{};try{return JSON.parse(Mh(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ae(){let e=qc().semantic;if(!e)return{...ps};let t=ms.safeParse({...ps,...e});return t.success?t.data:{...ps}}function gs(e,t="unknown"){Ph();let n=qc(),s=ms.parse({...ps,...n.semantic??{},...e}),r={...n,semantic:s};return jh(Wc(),JSON.stringify(r,null,2)),uo(s.enabled,t),s}function uo(e,t="unknown"){let n=e?"1":"0",s=null;try{h().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
951
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=pooled at=${new Date().toISOString()}`);return}catch(r){s=r;let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] pooled getDb() failed while syncing semantic_enabled=${n}: ${o} \u2014 falling back to raw connection`)}try{let r=new Kn(st);try{r.exec(`CREATE TABLE IF NOT EXISTS app_settings (
977
+ ${n}`).all(...s)}function Hh(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>typeof n=="string")}catch{}return[]}function zc(e={}){if(po.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Gc(e.projectId),n=new Set(t.map(c=>c.id)),s=new Map;for(let c of t)s.set(c.id,c.project_id);let r=_(),o=0;return r.transaction(()=>{for(let c of Yc(e.projectId,e.sinceRowid)){if(e.signal?.aborted)break;let u=Fh(c.content_text);if(u.length===0)continue;let d=s.get(c.session_id);if(d!==void 0)for(let p of u){if(p===c.session_id)continue;let f=s.get(p);if(!(f===void 0&&!n.has(p))&&!(f!==void 0&&d!==void 0&&f!==d))try{gn({source_session_id:c.session_id,target_session_id:p,link_type:"citation",confidence:jh,evidence:{matched_uuid:p,source_message_uuid:c.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"},{deferMirror:!0}),o+=1}catch{}}}})(),o>0&&ct(),{created:o}}function Kc(e={}){let t=Uh(e.projectId);if(t.length===0)return{created:0};let n=new Map;for(let d of t){let p=Hh(d.plan_ids_json);for(let f of p){let b=f.trim().toLowerCase();if(!b)continue;let T=n.get(b);T?T.push({id:d.session_id,project_id:d.project_id}):n.set(b,[{id:d.session_id,project_id:d.project_id}])}}if(n.size===0)return{created:0};let s=Gc(e.projectId),r=new Map;for(let d of s)r.set(d.id,d.project_id);let o=0,a=new Map;for(let d of Yc(e.projectId,e.sinceRowid)){if(e.signal?.aborted)break;let p=$h(d.content_text);if(p.length===0)continue;let f=r.get(d.session_id);if(f!==void 0)for(let b of p){let T=n.get(b);if(T)for(let S of T){if(S.id===d.session_id||S.project_id!==f)continue;let w=a.get(d.session_id);w||(w=new Map,a.set(d.session_id,w));let A=w.get(S.id);A||(A=new Set,w.set(S.id,A)),A.add(b)}}}return _().transaction(()=>{for(let[d,p]of a)for(let[f,b]of p)try{gn({source_session_id:d,target_session_id:f,link_type:"citation",confidence:Ph,evidence:{matched_plan_ids:Array.from(b).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"},{deferMirror:!0}),o+=1}catch{}})(),o>0&&ct(),{created:o}}H();we();import{existsSync as SE,mkdirSync as TE,writeFileSync as yE}from"node:fs";import{homedir as wE}from"node:os";import{join as So}from"node:path";zr();H();Z();import{existsSync as rl,mkdirSync as fE,readFileSync as _E,writeFileSync as hE}from"node:fs";import{homedir as EE}from"node:os";import{join as ol}from"node:path";import{z as Le}from"zod";function il(){return process.env.RECALL_HOME??ol(EE(),".recall")}function bE(){let e=il();rl(e)||fE(e,{recursive:!0})}function al(){return ol(il(),"config.json")}var bs=Le.object({enabled:Le.boolean().default(!1),model:Le.string().optional(),ratePerMinute:Le.number().int().min(1).max(600).default(30),lastProcessedSessionId:Le.string().nullable().default(null),backfillPaused:Le.boolean().default(!1),autoExtractEnabled:Le.boolean().default(!1),autoExtractIntervalMinutes:Le.number().int().min(5).max(720).default(60),autoExtractBatchSize:Le.number().int().min(1).max(20).default(1),autoResumeWorker:Le.boolean().default(!1)}),Es={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function cl(){let e=al();if(!rl(e))return{};try{return JSON.parse(_E(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ae(){let e=cl().semantic;if(!e)return{...Es};let t=bs.safeParse({...Es,...e});return t.success?t.data:{...Es}}function Ss(e,t="unknown"){bE();let n=cl(),s=bs.parse({...Es,...n.semantic??{},...e}),r={...n,semantic:s};return hE(al(),JSON.stringify(r,null,2)),bo(s.enabled,t),s}function bo(e,t="unknown"){let n=e?"1":"0",s=null;try{_().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
978
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=pooled at=${new Date().toISOString()}`);return}catch(r){s=r;let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] pooled getDb() failed while syncing semantic_enabled=${n}: ${o} \u2014 falling back to raw connection`)}try{let r=new Zn(ze);try{r.exec(`CREATE TABLE IF NOT EXISTS app_settings (
952
979
  key TEXT PRIMARY KEY,
953
980
  value TEXT NOT NULL
954
981
  );`),r.prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
955
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=fallback at=${new Date().toISOString()}`)}finally{r.close()}}catch(r){let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] raw-connection fallback ALSO failed for semantic_enabled=${n}: ${o} (original: ${s instanceof Error?s.message:String(s)})`)}}var Bh=1,Wh=12e3,Xc=3,qh=[];function Xh(){return process.env.RECALL_HOME??po(Hh(),".recall")}function Jc(){return po(Xh(),"semantic")}function Jh(){let e=Jc();Fh(e)||$h(e,{recursive:!0})}function Gh(e){let t=h(),n=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
982
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=fallback at=${new Date().toISOString()}`)}finally{r.close()}}catch(r){let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] raw-connection fallback ALSO failed for semantic_enabled=${n}: ${o} (original: ${s instanceof Error?s.message:String(s)})`)}}var RE=1,kE=12e3,ll=3,AE=[];function xE(){return process.env.RECALL_HOME??So(wE(),".recall")}function ul(){return So(xE(),"semantic")}function NE(){let e=ul();SE(e)||TE(e,{recursive:!0})}function OE(e){let t=_(),n=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
956
983
  p.name AS project,
957
984
  NULLIF(sa.alias, '') AS alias
958
985
  FROM sessions s
@@ -961,10 +988,10 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
961
988
  WHERE s.id = ?`).get(e);if(!n)return null;let s=t.prepare(`SELECT role, content_text
962
989
  FROM messages
963
990
  WHERE session_id = ? AND is_sidechain = 0
964
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){if(!a.content_text)continue;let c=a.role??"system",u=a.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!u)continue;let d=u.length>1500?u.slice(0,1500)+"\u2026":u,p=`${c}: ${d}`;if(o+p.length>Wh)break;r.push(p),o+=p.length}return{id:n.id,alias:n.alias,project:n.project,firstUserMessage:n.first_user_message,excerpt:r.join(`
991
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){if(!a.content_text)continue;let c=a.role??"system",u=a.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!u)continue;let d=u.length>1500?u.slice(0,1500)+"\u2026":u,p=`${c}: ${d}`;if(o+p.length>kE)break;r.push(p),o+=p.length}return{id:n.id,alias:n.alias,project:n.project,firstUserMessage:n.first_user_message,excerpt:r.join(`
965
992
 
966
- `),messageCount:n.message_count}}function Yh(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(`
967
- `)}function Kh(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1);try{let o=JSON.parse(r),a=typeof o.summary=="string"?o.summary.trim():"",u=(Array.isArray(o.keywords)?o.keywords:[]).filter(d=>typeof d=="string").map(d=>d.trim().toLowerCase()).filter(d=>d.length>0&&d.length<64);return!a||u.length===0?null:{summary:a,keywords:Array.from(new Set(u)).slice(0,20)}}catch{return null}}function zh(e){let t=h(),n=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
993
+ `),messageCount:n.message_count}}function LE(e){return["You are summarizing a Claude Code session for a local semantic-search index. The summary will be stored as plain text and matched against future natural-language queries.","",`Session: ${e.alias??e.id}`,`Project: ${e.project}`,e.firstUserMessage?`Opening prompt: ${e.firstUserMessage}`:"","","Transcript excerpt (truncated):","---",e.excerpt,"---","","Output a single JSON object on one line, with no Markdown fences and no commentary:",'{"summary": "<3 sentences describing what the user was trying to do, what was built or debugged, and the outcome>", "keywords": ["<concept>", "<technology>", "<problem>", ...]}',"","Constraints:","- summary: 3 sentences, plain prose, no bullet points",'- keywords: 10\u201315 lowercase tokens, multi-word entries hyphenated (e.g. "memory-leak"); no duplicates; no generic words like "code" or "session"',"- Output JSON only. Do not echo this prompt."].filter(Boolean).join(`
994
+ `)}function CE(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1);try{let o=JSON.parse(r),a=typeof o.summary=="string"?o.summary.trim():"",u=(Array.isArray(o.keywords)?o.keywords:[]).filter(d=>typeof d=="string").map(d=>d.trim().toLowerCase()).filter(d=>d.length>0&&d.length<64);return!a||u.length===0?null:{summary:a,keywords:Array.from(new Set(u)).slice(0,20)}}catch{return null}}function vE(e){let t=_(),n=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
968
995
  (session_id, summary, keywords, model, source_message_count, generated_at)
969
996
  VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
970
997
  ON CONFLICT(session_id) DO UPDATE SET
@@ -972,16 +999,16 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
972
999
  keywords = excluded.keywords,
973
1000
  model = excluded.model,
974
1001
  source_message_count = excluded.source_message_count,
975
- generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:n,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),Jh();let s=po(Jc(),`${e.sessionId}.json`);Uh(s,JSON.stringify({version:Bh,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 fs=null;function Vh(){let t=ae().ratePerMinute,n=t/6e4;return(!fs||fs.capacity!==t)&&(fs={tokens:t,capacity:t,refillPerMs:n,lastRefill:Date.now()}),fs}function Qh(e){let t=Date.now(),n=t-e.lastRefill;n>0&&(e.tokens=Math.min(e.capacity,e.tokens+n*e.refillPerMs),e.lastRefill=t)}async function Zh(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=Vh();if(Qh(t),t.tokens>=1){t.tokens-=1;return}let n=1-t.tokens,s=Math.max(50,Math.ceil(n/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(s,5e3)))}}async function _s(e,t={}){let n=ae();if(!n.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!me())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let s=Gh(e);if(!s)return{sessionId:e,ok:!1,reason:"session-not-found"};if(s.messageCount<Xc)return{sessionId:e,ok:!1,reason:"too-short"};if(!s.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await Zh(t.signal);let r=Yh(s),o=await at(r,qh,{model:n.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:n.model??null};let a=Kh(o.stdout);return a?(zh({sessionId:s.id,summary:a.summary,keywords:a.keywords,model:n.model??null,sourceMessageCount:s.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:n.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:n.model??null}}async function hs(e={}){let t=ae();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let n=h(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let a=n.prepare(`SELECT s.id
1002
+ generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:n,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),NE();let s=So(ul(),`${e.sessionId}.json`);yE(s,JSON.stringify({version:RE,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 Ts=null;function IE(){let t=ae().ratePerMinute,n=t/6e4;return(!Ts||Ts.capacity!==t)&&(Ts={tokens:t,capacity:t,refillPerMs:n,lastRefill:Date.now()}),Ts}function ME(e){let t=Date.now(),n=t-e.lastRefill;n>0&&(e.tokens=Math.min(e.capacity,e.tokens+n*e.refillPerMs),e.lastRefill=t)}async function DE(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=IE();if(ME(t),t.tokens>=1){t.tokens-=1;return}let n=1-t.tokens,s=Math.max(50,Math.ceil(n/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(s,5e3)))}}async function ys(e,t={}){let n=ae();if(!n.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!me())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let s=OE(e);if(!s)return{sessionId:e,ok:!1,reason:"session-not-found"};if(s.messageCount<ll)return{sessionId:e,ok:!1,reason:"too-short"};if(!s.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await DE(t.signal);let r=LE(s),o=await dt(r,AE,{model:n.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:n.model??null};let a=CE(o.stdout);return a?(vE({sessionId:s.id,summary:a.summary,keywords:a.keywords,model:n.model??null,sourceMessageCount:s.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:n.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:n.model??null}}async function ws(e={}){let t=ae();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let n=_(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let a=n.prepare(`SELECT s.id
976
1003
  FROM sessions s
977
1004
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
978
1005
  WHERE ${o}
979
1006
  ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
980
- 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 _s(u,{signal:e.signal})).ok?c.ok+=1:c.failed+=1}catch(p){c.failed+=1,console.error("[semantic.backfill] failed for",u,p)}c.processed+=1,gs({lastProcessedSessionId:u},"pipeline"),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function Gc(e){if(!ae().enabled)return;let s=h().prepare(`SELECT s.message_count, s.ended_at,
1007
+ LIMIT @limit`).all(r),c={total:a.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(c);for(let{id:u}of a){if(e.signal?.aborted||ae().backfillPaused)break;c.currentSessionId=u,e.onProgress?.({...c});try{(await ys(u,{signal:e.signal})).ok?c.ok+=1:c.failed+=1}catch(p){c.failed+=1,console.error("[semantic.backfill] failed for",u,p)}c.processed+=1,Ss({lastProcessedSessionId:u},"pipeline"),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function dl(e){if(!ae().enabled)return;let s=_().prepare(`SELECT s.message_count, s.ended_at,
981
1008
  ss.generated_at, ss.source_message_count
982
1009
  FROM sessions s
983
1010
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
984
- WHERE s.id = ?`).get(e);if(s&&!(s.message_count<Xc)&&!(s.generated_at&&s.source_message_count!=null&&s.source_message_count>=s.message_count))try{await _s(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function mo(){let e=ae(),t=h(),n=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,s=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:me(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:n,processedSessions:s,pendingSessions:Math.max(0,n-s),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}q();import{createHash as sE}from"node:crypto";var eE=[{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 tE(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),n=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-n.length))}${n}`}function nE(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(36)}function Se(e){if(!e)return{redacted:e,count:0};let t=e,n=0,s=new Set;for(let r of eE)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${nE(o)}`;return s.has(a)||(s.add(a),n+=1),`[REDACTED ${r.name}: ${tE(o)}]`});return{redacted:t,count:n}}Te();var _o=1,lt="claude-haiku-4-5-20251001",rE=3,oE=32e3,Yc=2e3,iE=30,aE=30,cE=30,lE=30;function uE(e){let n=h().prepare(`SELECT s.id,
1011
+ WHERE s.id = ?`).get(e);if(s&&!(s.message_count<ll)&&!(s.generated_at&&s.source_message_count!=null&&s.source_message_count>=s.message_count))try{await ys(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function To(){let e=ae(),t=_(),n=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,s=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:me(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:n,processedSessions:s,pendingSessions:Math.max(0,n-s),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}H();import{createHash as HE}from"node:crypto";var jE=[{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"}],pl=64*1024,PE="[OVERSIZED-BLOB-SKIPPED]";function ml(e){return e===32||e===9||e===10||e===13||e===12||e===11}function FE(e){let t=e.length,n=-1,s=-1,r=0;for(let c=0;c<t;c++)if(ml(e.charCodeAt(c)))r=0;else if(++r>pl){s=c;break}if(s===-1)return e;let o=[],a=0;n=-1;for(let c=0;c<=t;c++){let u=c===t||ml(e.charCodeAt(c));!u&&n===-1?n=c:u&&n!==-1&&(c-n>pl&&(o.push(e.slice(a,n)),o.push(PE),a=c),n=-1)}return o.push(e.slice(a)),o.join("")}function $E(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),n=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-n.length))}${n}`}function UE(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(36)}function be(e){if(!e)return{redacted:e,count:0};e=FE(e);let t=e,n=0,s=new Set;for(let r of jE)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${UE(o)}`;return s.has(a)||(s.add(a),n+=1),`[REDACTED ${r.name}: ${$E(o)}]`});return{redacted:t,count:n}}we();var Ro=1,mt="claude-haiku-4-5-20251001",BE=3,WE=32e3,gl=2e3,qE=30,XE=30,JE=30,GE=30;function YE(e){let n=_().prepare(`SELECT s.id,
985
1012
  NULLIF(sa.alias, '') AS alias,
986
1013
  s.auto_title,
987
1014
  s.auto_title_source,
@@ -992,15 +1019,15 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
992
1019
  FROM sessions s
993
1020
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
994
1021
  LEFT JOIN projects p ON p.id = s.project_id
995
- WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function Kc(e,t={}){if(e.message_count<rE)return{eligible:!1,reason:"too-short"};let n=e.title_quality;if(n==="programmatic"||n==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let s=ot(e.id);if(s&&s.extractor_version>=_o)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function dE(e){let t=uE(e);if(!t)return null;let s=h().prepare(`SELECT role, content_text
1022
+ WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function fl(e,t={}){if(e.message_count<BE)return{eligible:!1,reason:"too-short"};let n=e.title_quality;if(n==="programmatic"||n==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let s=lt(e.id);if(s&&s.extractor_version>=Ro)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function zE(e){let t=YE(e);if(!t)return null;let s=_().prepare(`SELECT role, content_text
996
1023
  FROM messages
997
1024
  WHERE session_id = ?
998
1025
  AND is_sidechain = 0
999
1026
  AND content_text IS NOT NULL
1000
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>Yc?u.slice(0,Yc)+"\u2026":u,p=`${c}: ${d}`;if(o+p.length>oE)break;r.push(p),o+=p.length}return{meta:t,excerpt:r.join(`
1027
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>gl?u.slice(0,gl)+"\u2026":u,p=`${c}: ${d}`;if(o+p.length>WE)break;r.push(p),o+=p.length}return{meta:t,excerpt:r.join(`
1001
1028
 
1002
- `)}}function pE(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(`
1003
- `)}function mE(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function go(e,t,n=!1){if(!Array.isArray(e))return[];let s=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=n?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!s.has(a)&&(s.add(a),r.push(a),r.length>=t))break}return r}function gE(e,t){if(!Array.isArray(e))return[];let n=new Set,s=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,a=r.freq;if(typeof o!="string")continue;let c=o.trim().toLowerCase();if(!c||c.length>128||n.has(c))continue;n.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(s.push({term:c,frequency:u}),s.length>=t)break}return s}function fE(e,t){if(!Array.isArray(e))return[];let n=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.error_type,o=s.snippet,a=s.file,c=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",u=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!u)continue;let d=typeof a=="string"&&a.trim().length>0?a.trim().slice(0,256):null,p=sE("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(n.push({error_type:c,message_hash:p,snippet:u,file:d}),n.length>=t)break}return n}function _E(e){let t=e.trim();try{let S=JSON.parse(t);typeof S.result=="string"&&(t=S.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1),o;try{o=JSON.parse(r)}catch{return null}let a=go(o.files_written,200),c=go(o.brands_mentioned,aE),u=gE(o.terms_introduced,iE),d=go(o.plan_ids_referenced,cE),p=fE(o.bug_signatures,lE);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&p.length===0&&!mE(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:p}}var fo=null;async function hE(e,t){return fo?fo(e,t):at(e,[],{model:t})}async function ho(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let n=dE(e);if(!n)return{session_id:e,ok:!1,skipped:"session-not-found"};let s=Kc(n.meta,{force:t.force});if(!s.eligible)return{session_id:e,ok:!1,skipped:s.reason};if(!fo&&!me())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=pE(n),o=t.model??lt,a=await hE(r,o);if(!a.success){let p=(a.stderr||a.stdout||"").slice(0,400),f=p?Se(p).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:f}}let c=_E(a.stdout);if(!c){let p=a.stdout.slice(0,400),f=p?Se(p).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:f}}let u=EE(a.stdout),d=xc({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:_o});return{session_id:e,ok:!0,index:d,usage:u}}function EE(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let n=t.usage,s={};return typeof n.input_tokens=="number"&&(s.input_tokens=n.input_tokens),typeof n.output_tokens=="number"&&(s.output_tokens=n.output_tokens),s}}catch{}return null}function xt(e={}){let t=h(),n=[],s=[];typeof e.projectId=="number"&&(n.push("s.project_id = ?"),s.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=n.length?`WHERE ${n.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
1029
+ `)}}function KE(e){let{meta:t}=e;return["You are extracting a structured Output Index from a Claude Code session for a local knowledge graph.","","Read the transcript and produce a single JSON object with EXACTLY these fields. Output JSON only \u2014 no Markdown fences, no commentary, no explanation.","","Fields (every field MUST appear; use [] for empty):","- files_written: array of strings. Relative or absolute file paths the assistant created, edited, or extensively discussed (Write/Edit tool calls or paths quoted verbatim).",'- brands_mentioned: array of strings. Distinct proper-noun company / product / brand names mentioned. Examples of valid: "Acme Group", "Apollo", "TikTok", "Cloudflare R2". NOT generic terms like "the company" or "users".','- terms_introduced: array of objects { "term": "<lowercase phrase>", "freq": <int> }. Distinctive multi-word noun phrases the session introduced or extensively discussed. Skip generic words. Cap at 30 entries.','- plan_ids_referenced: array of strings. Planning identifiers like "v0.18.A", "Phase D", "L3", "MASTER-PLAN-cognitive-graph". Empty array if none.','- bug_signatures: array of objects { "error_type": "<class or null>", "snippet": "<first line of the error>", "file": "<path or null>" }. Errors actually encountered in the session (not hypothetical). The error_type is the exception class (e.g. "TypeError", "ReferenceError") or null if unspecified.',"","Hard constraints:","- Do NOT fabricate. If a field has no concrete content from the transcript, output an empty array.","- Output JSON ONLY. No backticks, no markdown, no preamble.","- terms_introduced \u2264 30 entries; brands_mentioned \u2264 30 distinct entries; plan_ids_referenced \u2264 30; bug_signatures \u2264 30.","",`Session: ${t.alias??t.id.slice(0,8)}`,`Project: ${t.project??"unknown"}`,t.first_user_message?`Opening prompt: ${t.first_user_message.slice(0,500)}`:"","","Transcript excerpt (may be truncated):","---",e.excerpt,"---"].filter(Boolean).join(`
1030
+ `)}function VE(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function yo(e,t,n=!1){if(!Array.isArray(e))return[];let s=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=n?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!s.has(a)&&(s.add(a),r.push(a),r.length>=t))break}return r}function QE(e,t){if(!Array.isArray(e))return[];let n=new Set,s=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,a=r.freq;if(typeof o!="string")continue;let c=o.trim().toLowerCase();if(!c||c.length>128||n.has(c))continue;n.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(s.push({term:c,frequency:u}),s.length>=t)break}return s}function ZE(e,t){if(!Array.isArray(e))return[];let n=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.error_type,o=s.snippet,a=s.file,c=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",u=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!u)continue;let d=typeof a=="string"&&a.trim().length>0?a.trim().slice(0,256):null,p=HE("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(n.push({error_type:c,message_hash:p,snippet:u,file:d}),n.length>=t)break}return n}function eb(e){let t=e.trim();try{let b=JSON.parse(t);typeof b.result=="string"&&(t=b.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1),o;try{o=JSON.parse(r)}catch{return null}let a=yo(o.files_written,200),c=yo(o.brands_mentioned,XE),u=QE(o.terms_introduced,qE),d=yo(o.plan_ids_referenced,JE),p=ZE(o.bug_signatures,GE);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&p.length===0&&!VE(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:p}}var wo=null;async function tb(e,t){return wo?wo(e,t):dt(e,[],{model:t})}async function ko(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let n=zE(e);if(!n)return{session_id:e,ok:!1,skipped:"session-not-found"};let s=fl(n.meta,{force:t.force});if(!s.eligible)return{session_id:e,ok:!1,skipped:s.reason};if(!wo&&!me())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=KE(n),o=t.model??mt,a=await tb(r,o);if(!a.success){let p=(a.stderr||a.stdout||"").slice(0,400),f=p?be(p).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:f}}let c=eb(a.stdout);if(!c){let p=a.stdout.slice(0,400),f=p?be(p).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:f}}let u=nb(a.stdout),d=Xc({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:Ro});return{session_id:e,ok:!0,index:d,usage:u}}function nb(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let n=t.usage,s={};return typeof n.input_tokens=="number"&&(s.input_tokens=n.input_tokens),typeof n.output_tokens=="number"&&(s.output_tokens=n.output_tokens),s}}catch{}return null}function Ot(e={}){let t=_(),n=[],s=[];typeof e.projectId=="number"&&(n.push("s.project_id = ?"),s.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=n.length?`WHERE ${n.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
1004
1031
  NULLIF(sa.alias, '') AS alias,
1005
1032
  s.auto_title,
1006
1033
  s.auto_title_source,
@@ -1012,33 +1039,45 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1012
1039
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1013
1040
  LEFT JOIN projects p ON p.id = s.project_id
1014
1041
  ${o}
1015
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(...s),c=[],u=new Map;for(let d of a){let p={...d,alias_source:d.alias?"manual":null},f=Kc(p,{force:e.force});if(!f.eligible){let S=f.reason??"session-not-found";u.set(S,(u.get(S)??0)+1);continue}if(c.push(p),c.length>=r)break}return{eligible:c,skipped:u}}async function zc(e={}){let t=xt({projectId:e.projectId,limit:e.limit,force:e.force}),n={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())n.skipped+=r;e.onProgress?.({...n});let s=[];for(let r of t.eligible){if(e.signal?.aborted)break;n.current_session_id=r.id,e.onProgress?.({...n});let o=await ho(r.id,{model:e.model,force:e.force,signal:e.signal});s.push(o),e.onResult?.(o),o.ok?n.ok+=1:o.skipped?n.skipped+=1:n.failed+=1,o.usage?.input_tokens&&(n.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(n.total_output_tokens+=o.usage.output_tokens),n.processed+=1,e.onProgress?.({...n})}return n.current_session_id=null,e.onProgress?.({...n}),{progress:n,results:s}}Te();var se="[daemon:inference]",wo=!1,Ro=!1,ko=!1,Ao=!1,xo=!1,cl=0,No=!1,Oo=!1,Ts=3,ut=0,Nt=!1,ws=null,Ot=null,ll=!1;function ME(){return h().prepare("SELECT id, name FROM projects").all()}async function ul(){if(wo)return;wo=!0;let e=Date.now();try{let n=(await mc({minClusterSize:2})).progress;(n.clusters_created||n.clusters_merged||n.members_added)&&console.log(`${se} bug-patterns: created=${n.clusters_created} merged=${n.clusters_merged} members_added=${n.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${se} bug-patterns failed:`,t)}finally{wo=!1}}async function dl(){if(Ro)return;Ro=!0;let e=Date.now();try{let t=0,n=0;for(let s of ME())try{let r=await Nc({projectId:s.id});t+=r.progress.suggestions_created,n+=1}catch(r){console.error(`${se} citations failed for project "${s.name}":`,r)}t>0&&console.log(`${se} citations: ${t} suggestion(s) across ${n} project(s) (${Date.now()-e}ms)`)}catch(t){console.error(`${se} citations failed:`,t)}finally{Ro=!1}}function pl(){if(ko)return;ko=!0;let e=Date.now();try{let t=Lc({}),n=vc({});t.created+n.created>0&&console.log(`${se} l1: uuid=${t.created} plan=${n.created} (${Date.now()-e}ms)`)}catch(t){console.error(`${se} l1 failed:`,t)}finally{ko=!1}}async function ml(){if(Ao)return;let e=ae();if(!e.enabled||e.backfillPaused)return;Ao=!0;let t=Date.now();try{let n=await hs({limit:25});n.processed>0&&console.log(`${se} backfill: processed=${n.processed} ok=${n.ok} failed=${n.failed} (${Date.now()-t}ms)`)}catch(n){console.error(`${se} backfill failed:`,n)}finally{Ao=!1}}async function gl(){if(xo)return;let e=ae();if(e.autoExtractEnabled&&!ll&&Nt&&(jE(),console.log(`${se} auto-extract: circuit breaker reset (config toggled on).`)),ll=e.autoExtractEnabled,!e.autoExtractEnabled||Nt)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-cl<t)return;if(!me()){No||(console.log(`${se} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),No=!0);return}if(No=!1,!await Ba().catch(()=>!1)){Oo||(console.log(`${se} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),Oo=!0);return}Oo=!1,xo=!0,cl=Date.now();let s=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=xt({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,p=new Map,f=null;for(let b of o){let R=await ho(b.id,{model:lt});if(R.ok)a+=1;else if(!R.skipped){c+=1;let k=R.failed??"unknown";p.set(k,(p.get(k)??0)+1),!f&&R.stderr_excerpt&&(f=R.stderr_excerpt)}R.usage?.input_tokens&&(u+=R.usage.input_tokens),R.usage?.output_tokens&&(d+=R.usage.output_tokens)}let S=p.size>0?" reasons="+Array.from(p.entries()).map(([b,R])=>`${b}:${R}`).join(","):"";if(console.log(`${se} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-s}ms)${S}`),f){let R=Se(f).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${se} auto-extract: first failure excerpt: ${R}`)}o.length>0&&u===0&&d===0?(ut+=1,ut>=Ts&&(Nt=!0,ws=Date.now(),Ot=`${Ts} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${se} auto-extract: CIRCUIT BREAKER tripped \u2014 ${Ot}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(ut=0)}catch(r){console.error(`${se} auto-extract failed:`,r),ut+=1,ut>=Ts&&(Nt=!0,ws=Date.now(),Ot=`${Ts} consecutive thrown errors`,console.log(`${se} auto-extract: CIRCUIT BREAKER tripped \u2014 ${Ot}. Pausing nibbler.`))}finally{xo=!1}}function _l(){return{broken:Nt,brokenAt:ws,reason:Ot,consecutiveZeroTokenRuns:ut}}function jE(){Nt=!1,ws=null,Ot=null,ut=0}var Co=!1;async function fl(){if(Co)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(So(),nl)),n=e();if(!n.autoArchiveEnabled)return;let s=1380*60*1e3;if(n.lastRunAt&&Date.now()-Date.parse(n.lastRunAt)<s)return;Co=!0;let r=Date.now();try{let a=new Date(Date.now()-n.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(al(),il)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${se} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${se} auto-archive failed: ${a}`)}finally{Co=!1}}function hl(){let p=[],f=[];p.push(setTimeout(()=>{ul()},9e4)),f.push(setInterval(()=>{ul()},18e5)),p.push(setTimeout(()=>{dl()},18e4)),f.push(setInterval(()=>{dl()},36e5)),p.push(setTimeout(()=>pl(),12e4)),f.push(setInterval(()=>pl(),18e5)),p.push(setTimeout(()=>{ml()},24e4)),f.push(setInterval(()=>{ml()},9e5)),p.push(setTimeout(()=>{gl()},3e5)),f.push(setInterval(()=>{gl()},9e5));let S=3600*1e3,y=300*1e3;return p.push(setTimeout(()=>{fl()},y)),f.push(setInterval(()=>{fl()},S)),console.log(`${se} scheduled: bug-patterns (30m), citations (60m), l1 (30m), backfill (15m, when enabled), auto-extract (60m, when enabled), auto-archive (24h, when enabled)`),{startupTimers:p,intervalTimers:f,stop:()=>{for(let b of p)clearTimeout(b);for(let b of f)clearInterval(b)}}}q();import{serveStatic as Ig}from"@hono/node-server/serve-static";import{existsSync as xk,readFileSync as Ra}from"node:fs";import{stat as Nk,readFile as Ok,realpath as Ck}from"node:fs/promises";import{timingSafeEqual as Lk}from"node:crypto";import{homedir as vk}from"node:os";import{dirname as xa,join as Dr}from"node:path";import{fileURLToPath as Na}from"node:url";import{existsSync as wi,readdirSync as NT,readFileSync as bd,statSync as OT,statfsSync as Sd}from"node:fs";import{homedir as CT}from"node:os";import{join as yd}from"node:path";ys();q();ne();ne();q();import{watch as dy}from"chokidar";import{readdirSync as py,statSync as Vs}from"node:fs";import{createReadStream as DE}from"node:fs";import{createInterface as PE}from"node:readline";function Rs(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Io(e){let t=e?.usage;if(!t||typeof t!="object")return null;let n={inputTokens:Rs(t.input_tokens),outputTokens:Rs(t.output_tokens),cacheCreateTokens:Rs(t.cache_creation_input_tokens),cacheReadTokens:Rs(t.cache_read_input_tokens)};return n.inputTokens===0&&n.outputTokens===0&&n.cacheCreateTokens===0&&n.cacheReadTokens===0?null:n}var FE=/\x1B\[[0-9;]*[a-zA-Z]/g;function Lo(e){return e.replace(FE,"")}var vo=12e3;function El(e,t){if(e.length<=vo)return e;let n=e.slice(0,vo),s=e.length-vo;return`${n}
1042
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(...s),c=[],u=new Map;for(let d of a){let p={...d,alias_source:d.alias?"manual":null},f=fl(p,{force:e.force});if(!f.eligible){let b=f.reason??"session-not-found";u.set(b,(u.get(b)??0)+1);continue}if(c.push(p),c.length>=r)break}return{eligible:c,skipped:u}}async function _l(e={}){let t=Ot({projectId:e.projectId,limit:e.limit,force:e.force}),n={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())n.skipped+=r;e.onProgress?.({...n});let s=[];for(let r of t.eligible){if(e.signal?.aborted)break;n.current_session_id=r.id,e.onProgress?.({...n});let o=await ko(r.id,{model:e.model,force:e.force,signal:e.signal});s.push(o),e.onResult?.(o),o.ok?n.ok+=1:o.skipped?n.skipped+=1:n.failed+=1,o.usage?.input_tokens&&(n.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(n.total_output_tokens+=o.usage.output_tokens),n.processed+=1,e.onProgress?.({...n})}return n.current_session_id=null,e.onProgress?.({...n}),{progress:n,results:s}}we();var re="[daemon:inference]",Co=!1,vo=!1,Io=!1,Mo=!1,Do=!1,Nl=0,jo=!1,Po=!1,Ns=3,gt=0,Lt=!1,Os=null,Ct=null,Ol=!1;function Eb(){return _().prepare("SELECT id, name FROM projects").all()}async function Ll(){if(Co)return;Co=!0;let e=Date.now();try{let n=(await vc({minClusterSize:2})).progress;(n.clusters_created||n.clusters_merged||n.members_added)&&console.log(`${re} bug-patterns: created=${n.clusters_created} merged=${n.clusters_merged} members_added=${n.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} bug-patterns failed:`,t)}finally{Co=!1}}async function Cl(){if(vo)return;vo=!0;let e=Date.now();try{let t=_(),n=Fl(t,Il),s=t.prepare("SELECT COALESCE(MAX(rowid), 0) AS m FROM messages").get(),r=0,o=0,a=!1;for(let c of Eb())try{let u=await Jc({projectId:c.id,sinceRowid:n});r+=u.progress.suggestions_created,o+=1}catch(u){a=!0,console.error(`${re} citations failed for project "${c.name}":`,u)}a||$l(t,Il,s.m),r>0&&console.log(`${re} citations: ${r} suggestion(s) across ${o} project(s) since_rowid=${n} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} citations failed:`,t)}finally{vo=!1}}var vl="l1_inference_watermark_rowid",Il="citations_inference_watermark_rowid";function Fl(e,t){let n=e.prepare("SELECT value FROM app_settings WHERE key = ?").get(t);if(!n)return 0;let s=Number(n.value);return Number.isFinite(s)&&s>=0?s:0}function $l(e,t,n){e.prepare("INSERT INTO app_settings(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(t,String(n))}function Ml(){if(Io)return;Io=!0;let e=Date.now();try{let t=_(),n=Fl(t,vl),s=t.prepare("SELECT COALESCE(MAX(rowid), 0) AS m FROM messages").get(),r=zc({sinceRowid:n}),o=Kc({sinceRowid:n});$l(t,vl,s.m),r.created+o.created>0&&console.log(`${re} l1: uuid=${r.created} plan=${o.created} since_rowid=${n} (${Date.now()-e}ms)`)}catch(t){console.error(`${re} l1 failed:`,t)}finally{Io=!1}}async function Dl(){if(Mo)return;let e=ae();if(!e.enabled||e.backfillPaused)return;Mo=!0;let t=Date.now();try{let n=await ws({limit:25});n.processed>0&&console.log(`${re} backfill: processed=${n.processed} ok=${n.ok} failed=${n.failed} (${Date.now()-t}ms)`)}catch(n){console.error(`${re} backfill failed:`,n)}finally{Mo=!1}}async function jl(){if(Do)return;let e=ae();if(e.autoExtractEnabled&&!Ol&&Lt&&(bb(),console.log(`${re} auto-extract: circuit breaker reset (config toggled on).`)),Ol=e.autoExtractEnabled,!e.autoExtractEnabled||Lt)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-Nl<t)return;if(!me()){jo||(console.log(`${re} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),jo=!0);return}if(jo=!1,!await nc().catch(()=>!1)){Po||(console.log(`${re} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),Po=!0);return}Po=!1,Do=!0,Nl=Date.now();let s=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=Ot({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,p=new Map,f=null;for(let S of o){let w=await ko(S.id,{model:mt});if(w.ok)a+=1;else if(!w.skipped){c+=1;let A=w.failed??"unknown";p.set(A,(p.get(A)??0)+1),!f&&w.stderr_excerpt&&(f=w.stderr_excerpt)}w.usage?.input_tokens&&(u+=w.usage.input_tokens),w.usage?.output_tokens&&(d+=w.usage.output_tokens)}let b=p.size>0?" reasons="+Array.from(p.entries()).map(([S,w])=>`${S}:${w}`).join(","):"";if(console.log(`${re} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-s}ms)${b}`),f){let w=be(f).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${re} auto-extract: first failure excerpt: ${w}`)}o.length>0&&u===0&&d===0?(gt+=1,gt>=Ns&&(Lt=!0,Os=Date.now(),Ct=`${Ns} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${re} auto-extract: CIRCUIT BREAKER tripped \u2014 ${Ct}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(gt=0)}catch(r){console.error(`${re} auto-extract failed:`,r),gt+=1,gt>=Ns&&(Lt=!0,Os=Date.now(),Ct=`${Ns} consecutive thrown errors`,console.log(`${re} auto-extract: CIRCUIT BREAKER tripped \u2014 ${Ct}. Pausing nibbler.`))}finally{Do=!1}}function Ul(){return{broken:Lt,brokenAt:Os,reason:Ct,consecutiveZeroTokenRuns:gt}}function bb(){Lt=!1,Os=null,Ct=null,gt=0}var Fo=!1;async function Pl(){if(Fo)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(No(),yl)),n=e();if(!n.autoArchiveEnabled)return;let s=1380*60*1e3;if(n.lastRunAt&&Date.now()-Date.parse(n.lastRunAt)<s)return;Fo=!0;let r=Date.now();try{let a=new Date(Date.now()-n.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(xl(),Al)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${re} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${re} auto-archive failed: ${a}`)}finally{Fo=!1}}function Hl(){let p=[],f=[];p.push(setTimeout(()=>{Ll()},9e4)),f.push(setInterval(()=>{Ll()},18e5)),p.push(setTimeout(()=>{Cl()},18e4)),f.push(setInterval(()=>{Cl()},36e5)),p.push(setTimeout(()=>Ml(),12e4)),f.push(setInterval(()=>Ml(),18e5)),p.push(setTimeout(()=>{Dl()},24e4)),f.push(setInterval(()=>{Dl()},9e5)),p.push(setTimeout(()=>{jl()},3e5)),f.push(setInterval(()=>{jl()},9e5));let b=3600*1e3,T=300*1e3;return p.push(setTimeout(()=>{Pl()},T)),f.push(setInterval(()=>{Pl()},b)),console.log(`${re} scheduled: bug-patterns (30m), citations (60m), l1 (30m), backfill (15m, when enabled), auto-extract (60m, when enabled), auto-archive (24h, when enabled)`),{startupTimers:p,intervalTimers:f,stop:()=>{for(let S of p)clearTimeout(S);for(let S of f)clearInterval(S)}}}H();import{serveStatic as df}from"@hono/node-server/serve-static";import{existsSync as mA,readFileSync as Pa}from"node:fs";import{stat as gA,readFile as fA,realpath as _A}from"node:fs/promises";import{timingSafeEqual as hA}from"node:crypto";import{homedir as EA}from"node:os";import{dirname as Ua,join as Wr}from"node:path";import{fileURLToPath as Ha}from"node:url";import{existsSync as ji,readdirSync as gw,readFileSync as Kd,statSync as fw,statfsSync as Vd}from"node:fs";import{homedir as _w}from"node:os";import{join as Qd}from"node:path";xs();H();es();Z();Z();H();import{watch as QT}from"chokidar";import{readdirSync as ZT,statSync as $t}from"node:fs";import{createReadStream as Sb}from"node:fs";function Ls(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Ho(e){let t=e?.usage;if(!t||typeof t!="object")return null;let n={inputTokens:Ls(t.input_tokens),outputTokens:Ls(t.output_tokens),cacheCreateTokens:Ls(t.cache_creation_input_tokens),cacheReadTokens:Ls(t.cache_read_input_tokens)};return n.inputTokens===0&&n.outputTokens===0&&n.cacheCreateTokens===0&&n.cacheReadTokens===0?null:n}var Tb=/\x1B\[[0-9;]*[a-zA-Z]/g;function $o(e){return e.replace(Tb,"")}var Uo=12e3;function Bl(e,t){if(e.length<=Uo)return e;let n=e.slice(0,Uo),s=e.length-Uo;return`${n}
1016
1043
 
1017
- \u27E8\u2026 ${s.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function $E(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function UE(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let n of e.content)if(n&&typeof n=="object"){let s=n;s.type==="text"&&typeof s.text=="string"?t.push(s.text):s.type==="image"&&t.push("[image]")}return t.join(`
1018
- `)}return""}function HE(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Lo(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],n=[];for(let s of e.content)if(!(!s||typeof s!="object")){if(s.type==="text"&&typeof s.text=="string"){t.push(Lo(s.text));continue}if(s.type==="tool_use"&&typeof s.name=="string"){n.push(s.name);let r=s.input!=null?$E(s.input):"",o=El(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${s.name}\`**
1044
+ \u27E8\u2026 ${s.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function yb(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function wb(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let n of e.content)if(n&&typeof n=="object"){let s=n;s.type==="text"&&typeof s.text=="string"?t.push(s.text):s.type==="image"&&t.push("[image]")}return t.join(`
1045
+ `)}return""}function Rb(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:$o(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],n=[];for(let s of e.content)if(!(!s||typeof s!="object")){if(s.type==="text"&&typeof s.text=="string"){t.push($o(s.text));continue}if(s.type==="tool_use"&&typeof s.name=="string"){n.push(s.name);let r=s.input!=null?yb(s.input):"",o=Bl(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${s.name}\`**
1019
1046
 
1020
1047
  \`\`\`json
1021
1048
  ${o}
1022
- \`\`\``);continue}if(s.type==="tool_result"){let r=Lo(UE(s));if(r){let o=El(r,"tool result");t.push(`**Tool result**
1049
+ \`\`\``);continue}if(s.type==="tool_result"){let r=$o(wb(s));if(r){let o=Bl(r,"tool result");t.push(`**Tool result**
1023
1050
 
1024
1051
  \`\`\`
1025
1052
  ${o}
1026
1053
  \`\`\``)}else t.push("_(tool result was empty or image-only)_");continue}if(s.type==="image"){t.push("_(image)_");continue}t.push(`_(unknown block: ${s.type})_`)}return{text:t.join(`
1027
1054
 
1028
- `),toolNames:n}}async function*bl(e){let t=DE(e,{encoding:"utf8"}),n=PE({input:t,crlfDelay:1/0});for await(let s of n){if(!s.trim())continue;let r;try{r=JSON.parse(s)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:a}=HE(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:s,usage:Io(r.message),model:r.message?.model??null}}}import{basename as zs,join as my}from"node:path";import{execFile as ub}from"node:child_process";import{promisify as db}from"node:util";import{existsSync as pb}from"node:fs";import{basename as mb}from"node:path";ne();import{existsSync as BE,readFileSync as WE,writeFileSync as qE}from"node:fs";import{join as XE}from"node:path";import{z as _e}from"zod";var Mo=XE(W,"terminals.json"),Sl=1440*60*1e3,JE=3e4,GE=6e4,YE=_e.object({shell_pid:_e.number(),tab_name:_e.string(),cwd:_e.string().nullable().optional(),opened_at:_e.string(),last_seen_at:_e.string()}),KE=_e.object({schema:_e.string().optional(),saved_at:_e.string().optional(),terminals:_e.array(YE).max(500).default([]),sessions_by_pid:_e.record(_e.string(),_e.array(_e.string()).max(50)).optional().default({})}),yl=/^[⠀-⣿✳\s]+/,Tl=/^\d+(\.\d+){1,3}$/;function le(e){let t=e.trim();return!!(!t||yl.test(t)||Tl.test(t))}function Ct(e){let t=e.trim();if(!t||Tl.test(t))return null;let n=t.replace(yl,"").trim();return n.length>0?n:null}function jo(e,t){if(!le(e))return e;let n=Ct(e);return n||(t&&!le(t)?t:e)}function zE(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var Do=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,!!BE(Mo)))try{let t=WE(Mo,"utf8"),n=JSON.parse(t),s=KE.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,a]of Object.entries(r.sessions_by_pid??{})){let c=Number(o);if(!Number.isFinite(c))continue;let u=new Set;for(let d of a)d.length>0&&u.add(d);u.size>0&&this.sessionsByPid.set(c,u)}this.gc()}catch{}}save(){try{V();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};qE(Mo,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=jo(t.tab_name,s?.tab_name),o=s?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:n};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=jo(n,s.tab_name),o={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>GE?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=jo(o.tab_name,a?.tab_name),u=a?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:c,opened_at:u,last_seen_at:n}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=zE({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(n.kind==="pid-match"||n.kind==="singleton-cwd"){let s=this.pendingClaudeStarts.indexOf(n.entry);s>=0&&this.pendingClaudeStarts.splice(s,1)}return n}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,n,s,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:n,queued_at:Date.now(),cwd:s,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,n])=>({session_id:t,...n}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[n,s]of this.deferredLinks)s.queued_at<t&&this.deferredLinks.delete(n)}gcPending(){let t=Date.now()-JE;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(n=>{let s=Date.parse(n.started_at);return Number.isFinite(s)&&s>=t}))}outputTails=new Map;setOutputTail(t,n,s){this.ensureLoaded(),this.outputTails.set(t,{text:n,captured_at:s})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,n){this.ensureLoaded(),this.origins.set(t,n),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-Sl;for(let[n,s]of this.entries){let r=Date.parse(s.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(n)}}gcOrigins(){let t=Date.now()-Sl;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,o=0;for(let[a,c]of this.sessionsByPid){let u=this.entries.get(a);if(u){let d=Date.parse(u.last_seen_at);if(Number.isFinite(d)&&d>=s)continue}o+=c.size,this.sessionsByPid.delete(a),u&&(this.entries.delete(a),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let o=this.sessionsByPid.get(s);o&&(n+=o.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},j=new Do;q();ne();import{writeFileSync as VE}from"node:fs";import{join as QE}from"node:path";var ZE=QE(W,"aliases.json");function Po(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function ye(e){return h().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function eb(){return h().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:Po(t.previous_aliases)}))}function he(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=h(),r=new Date().toISOString(),o=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=Po(o.previous_aliases),o.alias!==n&&a.push({alias:o.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
1055
+ `),toolNames:n}}function kb(e){if(e.endsWith("\r")&&(e=e.slice(0,-1)),!e.trim())return null;let t;try{t=JSON.parse(e)}catch{return null}if(!t.uuid||!t.sessionId)return null;let{text:n,toolNames:s}=Rb(t.message);return{uuid:t.uuid,parentUuid:t.parentUuid??null,sessionId:t.sessionId,type:t.type??"unknown",role:t.message?.role??null,timestamp:t.timestamp??null,isSidechain:t.isSidechain===!0,cwd:t.cwd??null,gitBranch:t.gitBranch??null,version:t.version??null,contentText:n,toolNames:s,raw:e,usage:Ho(t.message),model:t.message?.model??null}}async function Wl(e,t){let n=[];await new Promise((u,d)=>{let p=Sb(e,{start:t});p.on("data",f=>{n.push(f)}),p.on("end",u),p.on("error",d)});let s=Buffer.concat(n),r=s.lastIndexOf(10);if(r===-1)return{entries:[],endOffset:t};let o=r+1,a=s.subarray(0,o).toString("utf8"),c=[];for(let u of a.split(`
1056
+ `)){let d=kb(u);d!==null&&c.push(d)}return{entries:c,endOffset:t+o}}function ql(e,t,n){return e?t.inode!==e.inode?{mode:"full"}:t.size<e.byteOffset?{mode:"full"}:n?{mode:"incremental",start:e.byteOffset}:{mode:"full"}:{mode:"full"}}import{createHash as Xl}from"node:crypto";import{openSync as Ab,readSync as xb,closeSync as Nb}from"node:fs";var Jl=4096;function Cs(e,t=Jl,n=Jl){let s=Math.max(0,Math.min(n,t));if(s===0)return Xl("sha256").update(Buffer.alloc(0)).digest("hex");let r=Ab(e,"r");try{let o=Buffer.allocUnsafe(s),a=xb(r,o,0,s,0);return Xl("sha256").update(o.subarray(0,a)).digest("hex")}finally{Nb(r)}}function Gl(e,t){let n=e.prepare("SELECT byte_offset, size_bytes, inode, prefix_hash FROM file_cursor WHERE file_path = ? LIMIT 1").get(t);return!n||n.inode==null||n.prefix_hash==null?null:{byteOffset:n.byte_offset,sizeBytes:n.size_bytes,inode:n.inode,prefixHash:n.prefix_hash}}function Bo(e,t,n){e.prepare(`
1057
+ INSERT INTO file_cursor (file_path, byte_offset, size_bytes, line_count, inode, prefix_hash, mtime, updated_at)
1058
+ VALUES (@file_path, @byte_offset, @size_bytes, @line_count, @inode, @prefix_hash, @mtime, datetime('now'))
1059
+ ON CONFLICT(file_path) DO UPDATE SET
1060
+ byte_offset = excluded.byte_offset,
1061
+ size_bytes = excluded.size_bytes,
1062
+ line_count = excluded.line_count,
1063
+ inode = excluded.inode,
1064
+ prefix_hash = excluded.prefix_hash,
1065
+ mtime = excluded.mtime,
1066
+ updated_at = datetime('now')
1067
+ `).run({file_path:t,byte_offset:n.byteOffset,size_bytes:n.sizeBytes,line_count:n.lineCount,inode:n.inode,prefix_hash:n.prefixHash,mtime:n.mtime})}import{basename as sr,join as ey}from"node:path";import{execFile as Vb}from"node:child_process";import{promisify as Qb}from"node:util";import{existsSync as Zb}from"node:fs";import{basename as eS}from"node:path";Z();import{existsSync as Ob,readFileSync as Lb,writeFileSync as Cb}from"node:fs";import{join as vb}from"node:path";import{z as fe}from"zod";var Wo=vb(W,"terminals.json"),Yl=1440*60*1e3,Ib=3e4,Mb=6e4,Db=fe.object({shell_pid:fe.number(),tab_name:fe.string(),cwd:fe.string().nullable().optional(),opened_at:fe.string(),last_seen_at:fe.string()}),jb=fe.object({schema:fe.string().optional(),saved_at:fe.string().optional(),terminals:fe.array(Db).max(500).default([]),sessions_by_pid:fe.record(fe.string(),fe.array(fe.string()).max(50)).optional().default({})}),zl=/^[⠀-⣿✳\s]+/,Kl=/^\d+(\.\d+){1,3}$/;function ue(e){let t=e.trim();return!!(!t||zl.test(t)||Kl.test(t))}function vt(e){let t=e.trim();if(!t||Kl.test(t))return null;let n=t.replace(zl,"").trim();return n.length>0?n:null}function qo(e,t){if(!ue(e))return e;let n=vt(e);return n||(t&&!ue(t)?t:e)}function Pb(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var Xo=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,!!Ob(Wo)))try{let t=Lb(Wo,"utf8"),n=JSON.parse(t),s=jb.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,a]of Object.entries(r.sessions_by_pid??{})){let c=Number(o);if(!Number.isFinite(c))continue;let u=new Set;for(let d of a)d.length>0&&u.add(d);u.size>0&&this.sessionsByPid.set(c,u)}this.gc()}catch{}}save(){try{K();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};Cb(Wo,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=qo(t.tab_name,s?.tab_name),o=s?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:n};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=qo(n,s.tab_name),o={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>Mb?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=qo(o.tab_name,a?.tab_name),u=a?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:c,opened_at:u,last_seen_at:n}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=Pb({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(n.kind==="pid-match"||n.kind==="singleton-cwd"){let s=this.pendingClaudeStarts.indexOf(n.entry);s>=0&&this.pendingClaudeStarts.splice(s,1)}return n}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,n,s,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:n,queued_at:Date.now(),cwd:s,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,n])=>({session_id:t,...n}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[n,s]of this.deferredLinks)s.queued_at<t&&this.deferredLinks.delete(n)}gcPending(){let t=Date.now()-Ib;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(n=>{let s=Date.parse(n.started_at);return Number.isFinite(s)&&s>=t}))}outputTails=new Map;setOutputTail(t,n,s){this.ensureLoaded(),this.outputTails.set(t,{text:n,captured_at:s})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,n){this.ensureLoaded(),this.origins.set(t,n),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-Yl;for(let[n,s]of this.entries){let r=Date.parse(s.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(n)}}gcOrigins(){let t=Date.now()-Yl;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,o=0;for(let[a,c]of this.sessionsByPid){let u=this.entries.get(a);if(u){let d=Date.parse(u.last_seen_at);if(Number.isFinite(d)&&d>=s)continue}o+=c.size,this.sessionsByPid.delete(a),u&&(this.entries.delete(a),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let o=this.sessionsByPid.get(s);o&&(n+=o.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},D=new Xo;H();Z();import{writeFileSync as Fb}from"node:fs";import{join as $b}from"node:path";var Ub=$b(W,"aliases.json");function Jo(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Se(e){return _().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function Hb(){return _().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:Jo(t.previous_aliases)}))}function _e(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=_(),r=new Date().toISOString(),o=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=Jo(o.previous_aliases),o.alias!==n&&a.push({alias:o.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
1029
1068
  VALUES (?, ?, ?, ?)
1030
1069
  ON CONFLICT(session_id) DO UPDATE SET
1031
1070
  alias = excluded.alias,
1032
1071
  updated_at = excluded.updated_at,
1033
- previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(a)),wl(),{session_id:e,alias:n,updated_at:r,previous_aliases:a}}function ks(e){let t=h(),n=new Date().toISOString(),s=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!s)return;let r=Po(s.previous_aliases);r.push({alias:s.alias,replaced_at:n}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
1034
- WHERE session_id = ?`).run(n,JSON.stringify(r),e),wl()}function wl(){try{V();let e=eb(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};VE(ZE,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}q();import{execFile as tb}from"node:child_process";import{readFile as nb}from"node:fs/promises";import{promisify as sb}from"node:util";var rb=sb(tb),Rl=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function ob(e){let t={};for(let n of Rl){let s=e[n];typeof s=="string"&&s.length>0&&(t[n]=s)}return t}function ib(e){let t=Date.now();if(e.CURSOR_TRACE_ID)return{editor:"cursor",label:"Cursor",detectedAt:t};if(e.VSCODE_PID||e.VSCODE_INJECTION)return{editor:"vscode",label:"VS Code",detectedAt:t};let n=e.TERM_PROGRAM;return n==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:n==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:n==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:n==="WezTerm"?{editor:"wezterm",label:"WezTerm",detectedAt:t}:e.WT_SESSION?{editor:"windows-terminal",label:"Windows Terminal",detectedAt:t}:e.KITTY_WINDOW_ID?{editor:"kitty",label:"Kitty",detectedAt:t}:e.TERM==="alacritty"||e.ALACRITTY_SOCKET?{editor:"alacritty",label:"Alacritty",detectedAt:t}:null}async function ab(e){if(process.platform==="linux")try{let t=await nb(`/proc/${e}/environ`,"utf8");return cb(t)}catch{return{}}try{let{stdout:t}=await rb("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return lb(t)}catch{return{}}}function cb(e){let t={};for(let n of e.split("\0")){if(!n)continue;let s=n.indexOf("=");if(s<=0)continue;let r=n.slice(0,s),o=n.slice(s+1);t[r]=o}return t}function lb(e){let t={},n=new Set(Rl),s=e.replace(/\s+/g," ").trim().split(" ");for(let r of s){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);n.has(a)&&(t[a]=r.slice(o+1))}return t}async function kl(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await ab(e),n=ob(t);return ib(n)}catch{return null}}var xs=db(ub),As;function gb(){if(As!==void 0)return As;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(pb(t))return As=t,t;return As=null,null}var fb=3,_b=3600*1e3,En=new Map;function Ol(){let e=j.all(),t=e.map(n=>n.shell_pid).sort((n,s)=>n-s).join(",");return`${e.length}:${t}`}function hb(e){let t=En.get(e);return t?Date.now()-t.lastAt>_b?(En.delete(e),!1):t.refusals<fb?!1:t.fingerprint===Ol():!1}function Al(e){let t=Ol(),n=En.get(e);n&&n.fingerprint===t?(n.refusals+=1,n.lastAt=Date.now()):En.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function Eb(e){En.delete(e)}async function bb(e){let t=gb();if(!t)return null;try{let{stdout:n}=await xs(t,["-Fpc",e],{timeout:2e3}),s=n.split(`
1035
- `),r=null,o=null,a=null;for(let c of s)c.startsWith("p")?(a=Number(c.slice(1)),Number.isFinite(a)&&a>0?r==null&&(r=a):a=null):c.startsWith("c")&&a!=null&&c.slice(1).trim()==="claude"&&o==null&&(o=a);return o??r}catch{return null}}async function xl(e){try{let{stdout:t}=await xs("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),n=Number(t.trim());return Number.isFinite(n)&&n>0?n:null}catch{return null}}async function Sb(e){try{let{stdout:t}=await xs("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),n=Date.parse(t.trim());return Number.isFinite(n)?n:null}catch{return null}}var Nl=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 n=t.replace(/^[-/]+/,"").replace(/\s*\(\d+\)\s*$/,"").trim();if(Nl.has(n))return!0;if(/^[-/][\w./-]*$/.test(t)){let s=n.replace(/^.*\//,"");if(Nl.has(s))return!0}return!1}function Lt(e){let t=e.tabName?.trim();return t&&!de(t)&&!le(t)?t:null}function yb(e){try{return h().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function Ns(e){let t=mb(e,".jsonl");if(ye(t)||hb(t))return;let n=yb(t),s=await bb(e),r=s?await xl(s):null,o=s?await kl(s):null;o&&j.setOrigin(t,o);let a=null,c=null,u=null,d=j.takePendingMatched({shellPid:r,cwd:n?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${n?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),Al(t);return}if(d.kind==="pid-match"||d.kind==="singleton-cwd"){let b=d.entry;c=b.shell_pid,u=d.kind==="pid-match"?"pending-pid":"pending-cwd",a=j.get(b.shell_pid)??{shell_pid:b.shell_pid,tab_name:b.tab_name,cwd:b.cwd,opened_at:b.started_at,last_seen_at:b.started_at}}let p=null;if(!a&&s){let b=s;for(let R=0;R<4&&b!=null;R++){let k=await xl(b);if(!k)break;let M=j.get(k);if(M){a=M,c=k,u="ppid";break}p==null&&(p=k),b=k}}if(!a&&n?.cwd){let b=n.cwd.replace(/\/+$/,""),R=j.all().filter(k=>k.cwd&&k.cwd.replace(/\/+$/,"")===b);R.length===1?(a=R[0],c=a.shell_pid,u="cwd"):R.length>=2&&(console.log(`[correlator] ${R.length} registered terminals in ${b} for ${t.slice(0,8)} \u2014 refusing to guess; heuristic title will display`),Al(t))}let f=null;if(a?.tab_name&&!de(a.tab_name)&&!le(a.tab_name))f=a.tab_name;else if(a?.tab_name&&le(a.tab_name)){let b=Ct(a.tab_name);b&&!de(b)&&(f=b)}if(!f&&!(u==="pending-pid"||u==="pending-cwd")&&a&&n?.cwd){let b=n.cwd.replace(/\/+$/,""),R=j.all().filter(k=>k.shell_pid!==c&&k.cwd&&k.cwd.replace(/\/+$/,"")===b&&!de(k.tab_name)&&!le(k.tab_name)).sort((k,M)=>Date.parse(M.last_seen_at)-Date.parse(k.last_seen_at))[0];R&&(f=R.tab_name)}let y=Lt({tabName:f,origin:o,cwd:n?.cwd??null,gitBranch:n?.git_branch??null});if(p!=null&&!c&&j.deferSessionLink(t,p,n?.cwd??null,n?.git_branch??null),!!y)try{he(t,y),Eb(t);let b=!!f&&!de(f)&&!le(f)&&y===f.trim();c!=null&&j.linkSession(t,c);let R=u==="pending-pid"?"pending PID-exact match":u==="pending-cwd"?"pending cwd-singleton match":u??"unknown";console.log(`[correlator] auto-aliased ${t.slice(0,8)} \u2192 "${y}"`+(b?` (tab name via ${R}, shell pid ${c})`:a?` (generic shell name "${a.tab_name}" \u2192 ${o?.editor??"origin"} fallback, shell pid ${c})`:o?` (${o.editor} origin \u2014 no terminal match)`:""))}catch{}}async function Tb(){try{let{stdout:e}=await xs("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let n of e.split(`
1036
- `)){let s=n.trim();if(!s)continue;let r=s.match(/^(\d+)\s+(\d+)\s+(.+)$/);if(!r)continue;let o=Number(r[1]),a=Number(r[2]),c=r[3].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&(!Number.isFinite(o)||!Number.isFinite(a)||t.set(o,a))}return t}catch{return new Map}}var wb=9e4;function Rb(e){let t=(Date.now()-wb)/1e3,n=e.replace(/\/+$/,"");return h().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1072
+ previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(a)),Vl(),{session_id:e,alias:n,updated_at:r,previous_aliases:a}}function vs(e){let t=_(),n=new Date().toISOString(),s=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!s)return;let r=Jo(s.previous_aliases);r.push({alias:s.alias,replaced_at:n}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
1073
+ WHERE session_id = ?`).run(n,JSON.stringify(r),e),Vl()}function Vl(){try{K();let e=Hb(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};Fb(Ub,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}H();import{execFile as Bb}from"node:child_process";import{readFile as Wb}from"node:fs/promises";import{promisify as qb}from"node:util";var Xb=qb(Bb),Ql=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function Jb(e){let t={};for(let n of Ql){let s=e[n];typeof s=="string"&&s.length>0&&(t[n]=s)}return t}function Gb(e){let t=Date.now();if(e.CURSOR_TRACE_ID)return{editor:"cursor",label:"Cursor",detectedAt:t};if(e.VSCODE_PID||e.VSCODE_INJECTION)return{editor:"vscode",label:"VS Code",detectedAt:t};let n=e.TERM_PROGRAM;return n==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:n==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:n==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:n==="WezTerm"?{editor:"wezterm",label:"WezTerm",detectedAt:t}:e.WT_SESSION?{editor:"windows-terminal",label:"Windows Terminal",detectedAt:t}:e.KITTY_WINDOW_ID?{editor:"kitty",label:"Kitty",detectedAt:t}:e.TERM==="alacritty"||e.ALACRITTY_SOCKET?{editor:"alacritty",label:"Alacritty",detectedAt:t}:null}async function Yb(e){if(process.platform==="linux")try{let t=await Wb(`/proc/${e}/environ`,"utf8");return zb(t)}catch{return{}}try{let{stdout:t}=await Xb("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return Kb(t)}catch{return{}}}function zb(e){let t={};for(let n of e.split("\0")){if(!n)continue;let s=n.indexOf("=");if(s<=0)continue;let r=n.slice(0,s),o=n.slice(s+1);t[r]=o}return t}function Kb(e){let t={},n=new Set(Ql),s=e.replace(/\s+/g," ").trim().split(" ");for(let r of s){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);n.has(a)&&(t[a]=r.slice(o+1))}return t}async function Zl(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await Yb(e),n=Jb(t);return Gb(n)}catch{return null}}var Ms=Qb(Vb),Is;function tS(){if(Is!==void 0)return Is;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(Zb(t))return Is=t,t;return Is=null,null}var nS=3,sS=3600*1e3,Tn=new Map;function su(){let e=D.all(),t=e.map(n=>n.shell_pid).sort((n,s)=>n-s).join(",");return`${e.length}:${t}`}function rS(e){let t=Tn.get(e);return t?Date.now()-t.lastAt>sS?(Tn.delete(e),!1):t.refusals<nS?!1:t.fingerprint===su():!1}function eu(e){let t=su(),n=Tn.get(e);n&&n.fingerprint===t?(n.refusals+=1,n.lastAt=Date.now()):Tn.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function oS(e){Tn.delete(e)}async function iS(e){let t=tS();if(!t)return null;try{let{stdout:n}=await Ms(t,["-Fpc",e],{timeout:2e3}),s=n.split(`
1074
+ `),r=null,o=null,a=null;for(let c of s)c.startsWith("p")?(a=Number(c.slice(1)),Number.isFinite(a)&&a>0?r==null&&(r=a):a=null):c.startsWith("c")&&a!=null&&c.slice(1).trim()==="claude"&&o==null&&(o=a);return o??r}catch{return null}}async function tu(e){try{let{stdout:t}=await Ms("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),n=Number(t.trim());return Number.isFinite(n)&&n>0?n:null}catch{return null}}async function aS(e){try{let{stdout:t}=await Ms("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),n=Date.parse(t.trim());return Number.isFinite(n)?n:null}catch{return null}}var nu=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu","node","deno","bun","python","python3","ruby","ts-node","tsx","go","cargo","java","php","irb","pry","iex","terminal","shell","console"]);function pe(e){let t=e.trim().toLowerCase();if(!t)return!0;let n=t.replace(/^[-/]+/,"").replace(/\s*\(\d+\)\s*$/,"").trim();if(nu.has(n))return!0;if(/^[-/][\w./-]*$/.test(t)){let s=n.replace(/^.*\//,"");if(nu.has(s))return!0}return!1}function It(e){let t=e.tabName?.trim();return t&&!pe(t)&&!ue(t)?t:null}function cS(e){try{return _().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function Ds(e){let t=eS(e,".jsonl");if(Se(t)||rS(t))return;let n=cS(t),s=await iS(e),r=s?await tu(s):null,o=s?await Zl(s):null;o&&D.setOrigin(t,o);let a=null,c=null,u=null,d=D.takePendingMatched({shellPid:r,cwd:n?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${n?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),eu(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=D.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 p=null;if(!a&&s){let S=s;for(let w=0;w<4&&S!=null;w++){let A=await tu(S);if(!A)break;let I=D.get(A);if(I){a=I,c=A,u="ppid";break}p==null&&(p=A),S=A}}if(!a&&n?.cwd){let S=n.cwd.replace(/\/+$/,""),w=D.all().filter(A=>A.cwd&&A.cwd.replace(/\/+$/,"")===S);w.length===1?(a=w[0],c=a.shell_pid,u="cwd"):w.length>=2&&(console.log(`[correlator] ${w.length} registered terminals in ${S} for ${t.slice(0,8)} \u2014 refusing to guess; heuristic title will display`),eu(t))}let f=null;if(a?.tab_name&&!pe(a.tab_name)&&!ue(a.tab_name))f=a.tab_name;else if(a?.tab_name&&ue(a.tab_name)){let S=vt(a.tab_name);S&&!pe(S)&&(f=S)}if(!f&&!(u==="pending-pid"||u==="pending-cwd")&&a&&n?.cwd){let S=n.cwd.replace(/\/+$/,""),w=D.all().filter(A=>A.shell_pid!==c&&A.cwd&&A.cwd.replace(/\/+$/,"")===S&&!pe(A.tab_name)&&!ue(A.tab_name)).sort((A,I)=>Date.parse(I.last_seen_at)-Date.parse(A.last_seen_at))[0];w&&(f=w.tab_name)}let T=It({tabName:f,origin:o,cwd:n?.cwd??null,gitBranch:n?.git_branch??null});if(p!=null&&!c&&D.deferSessionLink(t,p,n?.cwd??null,n?.git_branch??null),!!T)try{_e(t,T),oS(t);let S=!!f&&!pe(f)&&!ue(f)&&T===f.trim();c!=null&&D.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 lS(){try{let{stdout:e}=await Ms("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let n of e.split(`
1075
+ `)){let s=n.trim();if(!s)continue;let r=s.match(/^(\d+)\s+(\d+)\s+(.+)$/);if(!r)continue;let o=Number(r[1]),a=Number(r[2]),c=r[3].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&(!Number.isFinite(o)||!Number.isFinite(a)||t.set(o,a))}return t}catch{return new Map}}var uS=9e4;function dS(e){let t=(Date.now()-uS)/1e3,n=e.replace(/\/+$/,"");return _().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1037
1076
  FROM sessions s
1038
1077
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1039
- WHERE s.cwd = ? AND s.file_mtime > ?`).all(n,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function kb(e){let t=await import("node:fs/promises"),n="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);n=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let s=[];for(let r of n.split(`
1078
+ WHERE s.cwd = ? AND s.file_mtime > ?`).all(n,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function pS(e){let t=await import("node:fs/promises"),n="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);n=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let s=[];for(let r of n.split(`
1040
1079
  `)){if(!r.trim())continue;let o;try{o=JSON.parse(r)}catch{continue}let a=o;if(!a||a.type!=="user"&&a.type!=="assistant")continue;let c=a.message?.content,u="";if(typeof c=="string")u=c;else if(Array.isArray(c))for(let d of c)d&&typeof d=="object"&&"text"in d&&typeof d.text=="string"&&(u+=d.text+`
1041
- `);if(u){for(let d of u.split(/\r?\n/)){let p=d.trim();p.length>=60&&p.length<=400&&s.push(p)}if(s.length>=8)break}}return s}async function Cl(e){if(ye(e))return null;let t=h().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let n=await kb(t.file_path);if(n.length===0)return null;let s=t.cwd.replace(/\/+$/,""),r=j.allOutputTails(),o=[];for(let[a,c]of r){let u=j.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==s||de(u.tab_name)||le(u.tab_name))continue;let d=0;for(let p of n)c.text.includes(p)&&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 Ab=3e4;function Ll(){let e=Date.now(),t=j.all(),n=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>Ab},s=new Map;for(let o of t){if(n(o)||!o.cwd||de(o.tab_name)||le(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=s.get(a);c||(c=[],s.set(a,c)),c.push({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd})}let r={rebound:0,ghosts:0,ambiguous:0};for(let o of t){if(!n(o))continue;let a=j.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=ye(c);if(!u)continue;let d=h().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let p=`${d.cwd.replace(/\/+$/,"")}::${u}`,f=s.get(p)??[];if(f.length===0)continue;if(f.length>1){r.ambiguous++;continue}let S=f[0];S.shell_pid!==o.shell_pid&&(j.unlinkSession(c),j.linkSession(c,S.shell_pid),r.rebound++)}}}return r}function vl(){let e={resolved:0,expired:0},t=j.allDeferredLinks();for(let n of t){let s=j.get(n.parent_shell_pid);if(!s||de(s.tab_name)||le(s.tab_name))continue;let r=ye(n.session_id);if(r&&!j.isSessionAutoLinked(n.session_id)){j.resolveDeferredLink(n.session_id);continue}let o=j.getOrigin(n.session_id),a=Lt({tabName:s.tab_name,origin:o??null,cwd:n.cwd,gitBranch:n.git_branch});if(!a){j.resolveDeferredLink(n.session_id);continue}r!==a&&he(n.session_id,a),j.linkSession(n.session_id,n.parent_shell_pid),j.resolveDeferredLink(n.session_id),e.resolved++}return e}var xb=6e4;async function Os(){let e=await Tb(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let n=[];for(let[a,c]of e){let u=j.get(c);if(!u||!u.cwd||de(u.tab_name)||le(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let p=await Sb(a);n.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:p})}let s=new Map,r=a=>{let c=s.get(a);if(c)return c;let u=Rb(a);return s.set(a,u),u},o=new Set;for(let a of n){let c=r(a.cwd).filter(d=>!o.has(d.id));if(c.length===0)continue;let u=null;if(a.startTimeMs!=null){let d=xb;for(let p of c){if(p.started_at_ms==null)continue;let f=Math.abs(p.started_at_ms-a.startTimeMs);f<d&&(d=f,u=p)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!j.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){j.linkSession(u.id,a.shellPid);continue}try{he(u.id,a.target),j.linkSession(u.id,a.shellPid),u.alias?t.renamed++:t.linked++,console.log(`[correlator] linked ${u.id.slice(0,8)} \u2192 "${a.target}" (live sweep, claude pid ${a.claudePid}, shell pid ${a.shellPid})`)}catch{}}return t}function Il(e,t,n){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let s=e.prepare(`
1080
+ `);if(u){for(let d of u.split(/\r?\n/)){let p=d.trim();p.length>=60&&p.length<=400&&s.push(p)}if(s.length>=8)break}}return s}async function ru(e){if(Se(e))return null;let t=_().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let n=await pS(t.file_path);if(n.length===0)return null;let s=t.cwd.replace(/\/+$/,""),r=D.allOutputTails(),o=[];for(let[a,c]of r){let u=D.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==s||pe(u.tab_name)||ue(u.tab_name))continue;let d=0;for(let p of n)c.text.includes(p)&&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 mS=3e4;function ou(){let e=Date.now(),t=D.all(),n=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>mS},s=new Map;for(let o of t){if(n(o)||!o.cwd||pe(o.tab_name)||ue(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=s.get(a);c||(c=[],s.set(a,c)),c.push({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd})}let r={rebound:0,ghosts:0,ambiguous:0};for(let o of t){if(!n(o))continue;let a=D.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=Se(c);if(!u)continue;let d=_().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let p=`${d.cwd.replace(/\/+$/,"")}::${u}`,f=s.get(p)??[];if(f.length===0)continue;if(f.length>1){r.ambiguous++;continue}let b=f[0];b.shell_pid!==o.shell_pid&&(D.unlinkSession(c),D.linkSession(c,b.shell_pid),r.rebound++)}}}return r}function iu(){let e={resolved:0,expired:0},t=D.allDeferredLinks();for(let n of t){let s=D.get(n.parent_shell_pid);if(!s||pe(s.tab_name)||ue(s.tab_name))continue;let r=Se(n.session_id);if(r&&!D.isSessionAutoLinked(n.session_id)){D.resolveDeferredLink(n.session_id);continue}let o=D.getOrigin(n.session_id),a=It({tabName:s.tab_name,origin:o??null,cwd:n.cwd,gitBranch:n.git_branch});if(!a){D.resolveDeferredLink(n.session_id);continue}r!==a&&_e(n.session_id,a),D.linkSession(n.session_id,n.parent_shell_pid),D.resolveDeferredLink(n.session_id),e.resolved++}return e}var gS=6e4;async function js(){let e=await lS(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let n=[];for(let[a,c]of e){let u=D.get(c);if(!u||!u.cwd||pe(u.tab_name)||ue(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let p=await aS(a);n.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:p})}let s=new Map,r=a=>{let c=s.get(a);if(c)return c;let u=dS(a);return s.set(a,u),u},o=new Set;for(let a of n){let c=r(a.cwd).filter(d=>!o.has(d.id));if(c.length===0)continue;let u=null;if(a.startTimeMs!=null){let d=gS;for(let p of c){if(p.started_at_ms==null)continue;let f=Math.abs(p.started_at_ms-a.startTimeMs);f<d&&(d=f,u=p)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!D.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){D.linkSession(u.id,a.shellPid);continue}try{_e(u.id,a.target),D.linkSession(u.id,a.shellPid),u.alias?t.renamed++:t.linked++,console.log(`[correlator] linked ${u.id.slice(0,8)} \u2192 "${a.target}" (live sweep, claude pid ${a.claudePid}, shell pid ${a.shellPid})`)}catch{}}return t}function Go(e,t,n,s={}){s.insertOnly||e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let r=e.prepare(`
1042
1081
  INSERT INTO message_usage (
1043
1082
  message_uuid, session_id, model,
1044
1083
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1055,7 +1094,7 @@ ${o}
1055
1094
  cache_create_tokens = excluded.cache_create_tokens,
1056
1095
  cache_read_tokens = excluded.cache_read_tokens,
1057
1096
  timestamp = excluded.timestamp
1058
- `);for(let r of n)r.usage&&r.role==="assistant"&&s.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 Cs(e,t){let n=e.prepare(`SELECT
1097
+ `);for(let o of n)o.usage&&o.role==="assistant"&&r.run({uuid:o.uuid,session_id:t,model:o.model,input:o.usage.inputTokens,output:o.usage.outputTokens,cc:o.usage.cacheCreateTokens,cr:o.usage.cacheReadTokens,ts:o.timestamp})}function yn(e,t){let n=e.prepare(`SELECT
1059
1098
  COALESCE(SUM(input_tokens), 0) AS input_tokens,
1060
1099
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
1061
1100
  COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
@@ -1070,11 +1109,11 @@ ${o}
1070
1109
  total_cache_create_tokens = @cc,
1071
1110
  total_cache_read_tokens = @cr,
1072
1111
  primary_model = @model
1073
- WHERE id = @id`).run({id:t,input:n.input_tokens,output:n.output_tokens,cc:n.cache_create_tokens,cr:n.cache_read_tokens,model:s?.model??null})}q();import{execFile as Nb}from"node:child_process";import{promisify as Ob}from"node:util";import{stat as Cb}from"node:fs/promises";var Ml=Ob(Nb),jl=1e4,Lb="%H%x09%aI%x09%s";async function vb(e){try{let{stdout:t}=await Ml("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:jl});return t.trim()==="true"}catch{return!1}}async function Ib(e,t,n){let s=["--no-pager","log","--all","--no-color","--since",t,"--until",n,`--pretty=format:${Lb}`],{stdout:r}=await Ml("git",s,{cwd:e,timeout:jl,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1074
- `)){if(!c)continue;let[u,d,...p]=c.split(" ");!u||a.has(u)||(a.add(u),o.push({commit_sha:u,committed_at:d??null,subject:p.join(" ")||null}))}return o}function Mb(e){return h().prepare(`SELECT id, cwd, started_at, ended_at
1075
- FROM sessions WHERE id = ?`).get(e)??null}function jb(e,t,n){if(n.length===0)return 0;let s=h(),r=new Date().toISOString(),o=s.prepare(`INSERT OR IGNORE INTO session_commits
1112
+ WHERE id = @id`).run({id:t,input:n.input_tokens,output:n.output_tokens,cc:n.cache_create_tokens,cr:n.cache_read_tokens,model:s?.model??null})}H();import{execFile as fS}from"node:child_process";import{promisify as _S}from"node:util";import{stat as hS}from"node:fs/promises";var au=_S(fS),cu=1e4,ES="%H%x09%aI%x09%s";async function bS(e){try{let{stdout:t}=await au("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:cu});return t.trim()==="true"}catch{return!1}}async function SS(e,t,n){let s=["--no-pager","log","--all","--no-color","--since",t,"--until",n,`--pretty=format:${ES}`],{stdout:r}=await au("git",s,{cwd:e,timeout:cu,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1113
+ `)){if(!c)continue;let[u,d,...p]=c.split(" ");!u||a.has(u)||(a.add(u),o.push({commit_sha:u,committed_at:d??null,subject:p.join(" ")||null}))}return o}function TS(e){return _().prepare(`SELECT id, cwd, started_at, ended_at
1114
+ FROM sessions WHERE id = ?`).get(e)??null}function yS(e,t,n){if(n.length===0)return 0;let s=_(),r=new Date().toISOString(),o=s.prepare(`INSERT OR IGNORE INTO session_commits
1076
1115
  (session_id, commit_sha, committed_at, subject, cwd_snapshot, correlated_at)
1077
- VALUES (?, ?, ?, ?, ?, ?)`),a=0;return s.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(n),a}async function Fo(e){let t=Mb(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let n=t.started_at,s=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await Cb(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 vb(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await Ib(t.cwd,n,s),a=jb(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 Ls(e){let t=h(),n=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(n))return[];let s=`${n.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1116
+ VALUES (?, ?, ?, ?, ?, ?)`),a=0;return s.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(n),a}async function Yo(e){let t=TS(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let n=t.started_at,s=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await hS(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 bS(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await SS(t.cwd,n,s),a=yS(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 Ps(e){let t=_(),n=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(n))return[];let s=`${n.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1078
1117
  NULLIF(sa.alias, '') AS alias,
1079
1118
  p.name AS project,
1080
1119
  s.started_at AS startedAt,
@@ -1088,18 +1127,18 @@ ${o}
1088
1127
  LEFT JOIN session_aliases sa ON sa.session_id = sc.session_id
1089
1128
  WHERE lower(sc.commit_sha) = lower(?)
1090
1129
  OR lower(sc.commit_sha) LIKE ?
1091
- ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(n,s)}function $o(e){return h().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1130
+ ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(n,s)}function zo(e){return _().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1092
1131
  FROM session_commits
1093
1132
  WHERE session_id = ?
1094
- ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var Db=3e4;function Dl(e){try{let n=h().prepare(`SELECT MAX(correlated_at) AS last_at
1095
- FROM session_commits WHERE session_id = ?`).get(e),s=n?.last_at?Date.parse(n.last_at):0;if(s&&Date.now()-s<Db)return}catch{}Fo(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}q();ne();import{writeFileSync as Wb,mkdirSync as qb,existsSync as Xb}from"node:fs";import{join as Jl}from"node:path";q();var Pl=80;function Fl(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let n=t.split(`
1096
- `)[0].trim();return n.length>Pl?n.slice(0,Pl)+"\u2026":n}function Pb(e){return h().prepare(`SELECT s.id AS id,
1133
+ ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var wS=3e4;function lu(e){try{let n=_().prepare(`SELECT MAX(correlated_at) AS last_at
1134
+ FROM session_commits WHERE session_id = ?`).get(e),s=n?.last_at?Date.parse(n.last_at):0;if(s&&Date.now()-s<wS)return}catch{}Yo(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}H();Z();import{writeFileSync as LS,mkdirSync as CS,existsSync as vS}from"node:fs";import{join as bu}from"node:path";H();var uu=80;function du(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let n=t.split(`
1135
+ `)[0].trim();return n.length>uu?n.slice(0,uu)+"\u2026":n}function RS(e){return _().prepare(`SELECT s.id AS id,
1097
1136
  sa.alias AS alias,
1098
1137
  s.auto_title AS auto_title,
1099
1138
  s.first_user_message AS first_user_message
1100
1139
  FROM sessions s
1101
1140
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1102
- WHERE s.id = ?`).get(e)??null}function Fb(e){let t=Pb(e);return t?Fl(t):e.slice(0,8)}function $l(e){if(!e)return null;let t=h(),n=t.prepare(`SELECT e.thread_id AS thread_id,
1141
+ WHERE s.id = ?`).get(e)??null}function kS(e){let t=RS(e);return t?du(t):e.slice(0,8)}function pu(e){if(!e)return null;let t=_(),n=t.prepare(`SELECT e.thread_id AS thread_id,
1103
1142
  t.name AS thread_name,
1104
1143
  e.parent_session_id AS parent_session_id,
1105
1144
  e.added_at AS added_at
@@ -1108,7 +1147,7 @@ ${o}
1108
1147
  WHERE e.session_id = ?
1109
1148
  AND t.archived = 0
1110
1149
  ORDER BY e.added_at DESC
1111
- LIMIT 1`).get(e);if(!n)return null;let s=n.parent_session_id?{id:n.parent_session_id,title:Fb(n.parent_session_id)}:null,r=n.parent_session_id?[e,n.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1150
+ LIMIT 1`).get(e);if(!n)return null;let s=n.parent_session_id?{id:n.parent_session_id,title:kS(n.parent_session_id)}:null,r=n.parent_session_id?[e,n.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1112
1151
  s.id AS id,
1113
1152
  sa.alias AS alias,
1114
1153
  s.auto_title AS auto_title,
@@ -1118,33 +1157,33 @@ ${o}
1118
1157
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1119
1158
  WHERE e.thread_id = ?
1120
1159
  AND e.session_id NOT IN (${o})
1121
- ORDER BY e.added_at ASC`).all(n.thread_id,...r).map(u=>({id:u.session_id,title:u.id?Fl(u):u.session_id.slice(0,8)}));return{thread_id:n.thread_id,thread_name:n.thread_name,parent_session:s,siblings:c}}var Ul=[/^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 Uo(e){return Ul.some(t=>t.test(e))}var $b=[/^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 Hl(e){return e?$b.some(t=>t.test(e)):!1}var Ub=[/^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],Hb=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],Bb=20;function bn(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<Bb)return"low_signal";for(let t of Ul)if(t.test(e.auto_title))return"recursive_meta";for(let t of Ub)if(t.test(e.auto_title))return"programmatic";for(let t of Hb)if(t.test(e.auto_title))return"template_pending";return"clean"}function vs(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function Bl(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let n=e.slice(0,t),s=e.slice(t+3),r=vs(s);return!r||r===s?e:`${n} \xB7 ${r}`}var Wo=Jl(W,"titles"),Jb=80,Gb=60,Yb=100,Kb=50,Sn=5,Is=15,zb=500;function Gl(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>!!n&&typeof n=="object"&&typeof n.title=="string"&&typeof n.replaced_at=="string")}catch{}return[]}function It(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let n=Vb(t,e);if(n)return n;let s=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(s&&s.length<=Jb?s:t.slice(0,Gb)).trim()||null}function Vb(e,t){let n=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(n){let s=`/${n[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=qo(r);return o?vt(`${s} \xB7 ${o}`):s}for(let s of Qb){if(!e.match(s.match))continue;let o=s.prefix,a=s.extract?s.extract(e,t):qo(t);return a?s.completeFromExtract?vt(a):vt(`${o} \xB7 ${a}`):o}for(let s of tS){if(!e.match(s.match))continue;let o=s.extract?s.extract(e,t):Ms(t);return o?s.completeFromExtract?vt(o):vt(`${s.prefix} \xB7 ${o}`):s.prefix}return null}var Qb=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let s=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=qo(t);return s&&r?`${s} \xB7 ${r}`:s||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let n=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return n?n.split("/").filter(Boolean).slice(-2).join("/")||n:null}},{match:/^(?:read|inspect) this and (?:then )?begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>eS(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let n=t.match(/\.claude\/skills\/([^/\s]+)/);return n?.[1]?`[skill] ${n[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>Zb(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>Wl(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)=>Wl(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let n=t[1],s=t[2]?.trim();return s?`Implementing ${n} \xB7 ${s}`:`Implementing ${n}`}}];function Zb(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),n=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),s=t?.[1]?.trim(),r=n?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return s&&o?`[output-index] \xB7 ${s} \xB7 ${o}`:s?`[output-index] \xB7 ${s}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function Wl(e,t){if(!e)return t;let n=e.indexOf("Messages:");if(n===-1)return t;let s=e.slice(n+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of s.matchAll(r)){let a=o[1]?.trim()??"";if(!a)continue;let c=a.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!c||/^<local-command-/.test(c))continue;let u=c.length>60?c.slice(0,57)+"\u2026":c;return`${t} \xB7 ${u}`}return t}function eS(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let n=t[1].split("/").filter(Boolean);return(n[n.length-1]??"").replace(/\.[^.]+$/,"")||null}var tS=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>Ms(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let s=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=s?`${s} co-pilot`:"co-pilot",o=Ms(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>Ms(t)}];function Ms(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,n;for(;(n=t.exec(e))!==null;){let s=n[2].trim();if(!s||/^(null|none|n\/a|—|-)$/i.test(s))continue;let r=s.replace(/\s+/g," ");return vs(r)||r}return null}function vt(e){return e.slice(0,Yb).trim()}var nS=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],sS=[/^(?: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 Ho(e){let t=e.trim();return t.length<3?!0:nS.some(n=>n.test(t))}function rS(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return sS.some(n=>n.test(t))}function qo(e){let t=oS(e);return t===null?null:vs(t)||t}function oS(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,n,s=[];for(;(n=t.exec(e))!==null;){let u=n[2].trim(),d=n[3].trim().replace(/\s+/g," ");if(!Ho(d)){if(rS(u))return d;s.push(d)}}if(s.length>0)return s[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!Ho(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(n=o.exec(e))!==null;){let u=n[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!Ho(d)&&!/product context|reference/i.test(u))return d}let a=e.replace(/^Context for this run[^:]*:\s*/i,"");if(a!==e){let u=a.split(`
1160
+ ORDER BY e.added_at ASC`).all(n.thread_id,...r).map(u=>({id:u.session_id,title:u.id?du(u):u.session_id.slice(0,8)}));return{thread_id:n.thread_id,thread_name:n.thread_name,parent_session:s,siblings:c}}var mu=[/^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 Ko(e){return mu.some(t=>t.test(e))}var AS=[/^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 gu(e){return e?AS.some(t=>t.test(e)):!1}var xS=[/^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],NS=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],OS=20;function wn(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<OS)return"low_signal";for(let t of mu)if(t.test(e.auto_title))return"recursive_meta";for(let t of xS)if(t.test(e.auto_title))return"programmatic";for(let t of NS)if(t.test(e.auto_title))return"template_pending";return"clean"}function Fs(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function fu(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let n=e.slice(0,t),s=e.slice(t+3),r=Fs(s);return!r||r===s?e:`${n} \xB7 ${r}`}var Zo=bu(W,"titles"),IS=80,MS=60,DS=100,jS=50,Rn=5,$s=15,PS=500;function Su(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>!!n&&typeof n=="object"&&typeof n.title=="string"&&typeof n.replaced_at=="string")}catch{}return[]}function Dt(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let n=FS(t,e);if(n)return n;let s=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(s&&s.length<=IS?s:t.slice(0,MS)).trim()||null}function FS(e,t){let n=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(n){let s=`/${n[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=ei(r);return o?Mt(`${s} \xB7 ${o}`):s}for(let s of $S){if(!e.match(s.match))continue;let o=s.prefix,a=s.extract?s.extract(e,t):ei(t);return a?s.completeFromExtract?Mt(a):Mt(`${o} \xB7 ${a}`):o}for(let s of BS){if(!e.match(s.match))continue;let o=s.extract?s.extract(e,t):Us(t);return o?s.completeFromExtract?Mt(o):Mt(`${s.prefix} \xB7 ${o}`):s.prefix}return null}var $S=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let s=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=ei(t);return s&&r?`${s} \xB7 ${r}`:s||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let n=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return n?n.split("/").filter(Boolean).slice(-2).join("/")||n:null}},{match:/^(?:read|inspect) this and (?:then )?begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>HS(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let n=t.match(/\.claude\/skills\/([^/\s]+)/);return n?.[1]?`[skill] ${n[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>US(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>_u(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)=>_u(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let n=t[1],s=t[2]?.trim();return s?`Implementing ${n} \xB7 ${s}`:`Implementing ${n}`}}];function US(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),n=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),s=t?.[1]?.trim(),r=n?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return s&&o?`[output-index] \xB7 ${s} \xB7 ${o}`:s?`[output-index] \xB7 ${s}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function _u(e,t){if(!e)return t;let n=e.indexOf("Messages:");if(n===-1)return t;let s=e.slice(n+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of s.matchAll(r)){let a=o[1]?.trim()??"";if(!a)continue;let c=a.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!c||/^<local-command-/.test(c))continue;let u=c.length>60?c.slice(0,57)+"\u2026":c;return`${t} \xB7 ${u}`}return t}function HS(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let n=t[1].split("/").filter(Boolean);return(n[n.length-1]??"").replace(/\.[^.]+$/,"")||null}var BS=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>Us(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let s=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=s?`${s} co-pilot`:"co-pilot",o=Us(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>Us(t)}];function Us(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,n;for(;(n=t.exec(e))!==null;){let s=n[2].trim();if(!s||/^(null|none|n\/a|—|-)$/i.test(s))continue;let r=s.replace(/\s+/g," ");return Fs(r)||r}return null}function Mt(e){return e.slice(0,DS).trim()}var WS=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],qS=[/^(?: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 Vo(e){let t=e.trim();return t.length<3?!0:WS.some(n=>n.test(t))}function XS(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return qS.some(n=>n.test(t))}function ei(e){let t=JS(e);return t===null?null:Fs(t)||t}function JS(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,n,s=[];for(;(n=t.exec(e))!==null;){let u=n[2].trim(),d=n[3].trim().replace(/\s+/g," ");if(!Vo(d)){if(XS(u))return d;s.push(d)}}if(s.length>0)return s[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!Vo(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(n=o.exec(e))!==null;){let u=n[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!Vo(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(`
1122
1161
  `).map(d=>d.trim()).find(d=>d.length>=4);if(u)return u.slice(0,60)}let c=e.split(`
1123
- `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function Xo(e){let t=h(),n=t.prepare(`SELECT rowid AS rid, content_text
1162
+ `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function ti(e){let t=_(),n=t.prepare(`SELECT rowid AS rid, content_text
1124
1163
  FROM messages
1125
1164
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1126
1165
  AND content_text IS NOT NULL AND content_text != ''
1127
1166
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1128
- LIMIT ?`).all(e,Sn),s=t.prepare(`SELECT rowid AS rid, content_text
1167
+ LIMIT ?`).all(e,Rn),s=t.prepare(`SELECT rowid AS rid, content_text
1129
1168
  FROM messages
1130
1169
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1131
1170
  AND content_text IS NOT NULL AND content_text != ''
1132
1171
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1133
- LIMIT ?`).all(e,Is),r=new Map;for(let p of n)r.set(p.rid,p.content_text);for(let p of s)r.set(p.rid,p.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let o=Array.from(r.entries()).sort((p,f)=>p[0]-f[0]).map(([,p])=>({content_text:p})),a=n.length===Sn&&s.length===Is&&r.size===Sn+Is,c=o.map((p,f)=>{let S=(p.content_text??"").slice(0,zb);return a&&f===Sn?`--- (middle of session omitted) ---
1134
- ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1135
- `),u=null;try{u=$l(e)}catch(p){console.error("[autoTitle] thread context resolution failed:",p),u=null}let d=[];return u&&(d.push(iS(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${Sn}`,`messages (initial intent) and the last ${Is} 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(`
1136
- `)}var Bo=5;function iS(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let n=e.siblings.length;if(n>0){let r=e.siblings.slice(0,Bo).map(a=>`"${a.title}"`).join(", "),o=n>Bo?`, and ${n-Bo} more`:"";t.push(`- Sibling sessions (${n}): ${r}${o}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
1137
- `)}async function Yl(e){let t=Xo(e),{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(Te(),ct));if(!s())throw new Error("claude CLI not found on PATH");let r=await n(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=aS(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,Kb)}function aS(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return ql(s)}}catch{}return ql(t)}function ql(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function Ee(e,t,n){let s=t.trim();if(!s)return;let r=h(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1138
- FROM sessions WHERE id = ?`).get(e);if(!o||n==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===s&&o.auto_title_source===n)return;let a=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
1172
+ LIMIT ?`).all(e,$s),r=new Map;for(let p of n)r.set(p.rid,p.content_text);for(let p of s)r.set(p.rid,p.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let o=Array.from(r.entries()).sort((p,f)=>p[0]-f[0]).map(([,p])=>({content_text:p})),a=n.length===Rn&&s.length===$s&&r.size===Rn+$s,c=o.map((p,f)=>{let b=(p.content_text??"").slice(0,PS);return a&&f===Rn?`--- (middle of session omitted) ---
1173
+ ${f+1}. ${b}`:`${f+1}. ${b}`}).join(`
1174
+ `),u=null;try{u=pu(e)}catch(p){console.error("[autoTitle] thread context resolution failed:",p),u=null}let d=[];return u&&(d.push(GS(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${Rn}`,`messages (initial intent) and the last ${$s} messages (current direction).`,"Write a single descriptive title, max 50 characters, focused on what the user is","currently trying to accomplish. If initial intent and current direction differ, prefer","the current direction. Output ONLY the title, with no quotes and no trailing punctuation.","","Messages:",c),d.join(`
1175
+ `)}var Qo=5;function GS(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let n=e.siblings.length;if(n>0){let r=e.siblings.slice(0,Qo).map(a=>`"${a.title}"`).join(", "),o=n>Qo?`, and ${n-Qo} more`:"";t.push(`- Sibling sessions (${n}): ${r}${o}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
1176
+ `)}async function Tu(e){let t=ti(e),{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(we(),pt));if(!s())throw new Error("claude CLI not found on PATH");let r=await n(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=YS(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,jS)}function YS(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return hu(s)}}catch{}return hu(t)}function hu(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function he(e,t,n){let s=t.trim();if(!s)return;let r=_(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1177
+ FROM sessions WHERE id = ?`).get(e);if(!o||n==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===s&&o.auto_title_source===n)return;let a=Su(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
1139
1178
  SET auto_title = ?,
1140
1179
  auto_title_source = ?,
1141
1180
  auto_title_generated_at = ?,
1142
1181
  auto_title_history = ?
1143
- WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(a),e),dS(e,s,n,c)}function ve(e){let t=h().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1144
- 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 Kl(){let t=h().prepare(`SELECT id, first_user_message
1182
+ WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(a),e),QS(e,s,n,c)}function ve(e){let t=_().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1183
+ FROM sessions WHERE id = ?`).get(e);return t?{auto_title:t.auto_title,auto_title_source:t.auto_title_source??null,auto_title_generated_at:t.auto_title_generated_at,auto_title_history:Su(t.auto_title_history)}:null}function yu(){let t=_().prepare(`SELECT id, first_user_message
1145
1184
  FROM sessions
1146
1185
  WHERE auto_title IS NULL
1147
- AND first_user_message IS NOT NULL`).all(),n=0;for(let s of t){let r=It(s.first_user_message);r&&(Ee(s.id,r,"heuristic"),n+=1)}return{updated:n}}function zl(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1186
+ AND first_user_message IS NOT NULL`).all(),n=0;for(let s of t){let r=Dt(s.first_user_message);r&&(he(s.id,r,"heuristic"),n+=1)}return{updated:n}}function wu(e){let t=_(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1148
1187
  SELECT auto_title, project_id
1149
1188
  FROM sessions
1150
1189
  WHERE auto_title IS NOT NULL
@@ -1197,7 +1236,7 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1197
1236
  AND content_text IS NOT NULL
1198
1237
  AND content_text != ''
1199
1238
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1200
- LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),p=null;for(let f of d){let S=f.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!S||/^<local-command-caveat>/.test(S))continue;let y=It(S);if(y&&!Xl(y)){p=y;break}}if(!p){let f=d.map(y=>y.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(y=>y.length>0&&!/^<local-command-caveat>/.test(y)),S=f?It(f):null;S&&Xl(S)&&(p=`[vacuous] ${S}`)}p&&(Ee(u.id,p,"heuristic"),c+=1)}return{scanned:a,updated:c}}function Xl(e){let t=e.trim();return!t||/^\*\*Tool result\*\*$/i.test(t)?!0:[/^all right\.?$/i,/^alright\.?$/i,/^inspect this\.?$/i,/^resolve this\.?$/i,/^do it\.?$/i,/^go\.?$/i,/^continue\.?$/i,/^proceed\.?$/i,/^keep going\.?$/i,/^ok\.?$/i,/^okay\.?$/i,/^yes\.?$/i,/^yep\.?$/i].some(s=>s.test(t))}function Vl(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1239
+ LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),p=null;for(let f of d){let b=f.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!b||/^<local-command-caveat>/.test(b))continue;let T=Dt(b);if(T&&!Eu(T)){p=T;break}}if(!p){let f=d.map(T=>T.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(T=>T.length>0&&!/^<local-command-caveat>/.test(T)),b=f?Dt(f):null;b&&Eu(b)&&(p=`[vacuous] ${b}`)}p&&(he(u.id,p,"heuristic"),c+=1)}return{scanned:a,updated:c}}function Eu(e){let t=e.trim();return!t||/^\*\*Tool result\*\*$/i.test(t)?!0:[/^all right\.?$/i,/^alright\.?$/i,/^inspect this\.?$/i,/^resolve this\.?$/i,/^do it\.?$/i,/^go\.?$/i,/^continue\.?$/i,/^proceed\.?$/i,/^keep going\.?$/i,/^ok\.?$/i,/^okay\.?$/i,/^yes\.?$/i,/^yep\.?$/i].some(s=>s.test(t))}function Ru(e){let t=_(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1201
1240
  FROM sessions s
1202
1241
  WHERE s.auto_title_source = 'heuristic'${n}
1203
1242
  AND (
@@ -1212,47 +1251,47 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1212
1251
  AND content_text IS NOT NULL
1213
1252
  AND content_text != ''
1214
1253
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1215
- LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),p=cS(d,u.auto_title);p&&(Ee(u.id,p,"heuristic"),c+=1)}return{scanned:a,updated:c}}function cS(e,t){for(let n of e){let s=lS(n.content_text);if(!s||Uo(s))continue;let r=It(s);if(r){if(r===t)return null;if(!Uo(r))return r}}return t.startsWith("[meta]")?null:vt(`[meta] ${t}`)}function Ql(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1254
+ LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),p=zS(d,u.auto_title);p&&(he(u.id,p,"heuristic"),c+=1)}return{scanned:a,updated:c}}function zS(e,t){for(let n of e){let s=KS(n.content_text);if(!s||Ko(s))continue;let r=Dt(s);if(r){if(r===t)return null;if(!Ko(r))return r}}return t.startsWith("[meta]")?null:Mt(`[meta] ${t}`)}function ku(e){let t=_(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1216
1255
  FROM sessions s
1217
1256
  WHERE s.auto_title_source = 'heuristic'${n}
1218
1257
  AND s.auto_title IS NOT NULL
1219
1258
  AND s.auto_title LIKE '% \xB7 %'
1220
- AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...s),o=0,a=0;for(let c of r){o+=1;let u=Bl(c.auto_title);u!==c.auto_title&&(Ee(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function lS(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 uS(){V(),Xb(Wo)||qb(Wo,{recursive:!0})}function dS(e,t,n,s){try{uS();let r=Jl(Wo,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
1221
- `;Wb(r,o+t+`
1222
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}import{existsSync as Zl,mkdirSync as pS,readFileSync as mS,writeFileSync as gS}from"node:fs";import{homedir as fS}from"node:os";import{join as eu}from"node:path";import{z as Jo}from"zod";function tu(){return process.env.RECALL_HOME??eu(fS(),".recall")}function _S(){let e=tu();Zl(e)||pS(e,{recursive:!0})}function nu(){return eu(tu(),"config.json")}var Ds=Jo.object({heuristicEnabled:Jo.boolean().default(!0),agentEnabled:Jo.boolean().default(!1)}),js={heuristicEnabled:!0,agentEnabled:!1};function su(){let e=nu();if(!Zl(e))return{};try{return JSON.parse(mS(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function dt(){let e=su().autoTitle;if(!e)return{...js};let t=Ds.safeParse({...js,...e});return t.success?t.data:{...js}}function ru(e){_S();let t=su(),n=Ds.parse({...js,...t.autoTitle??{},...e}),s={...t,autoTitle:n};return gS(nu(),JSON.stringify(s,null,2)),n}q();ne();import{randomUUID as zo}from"node:crypto";import{existsSync as wS,mkdirSync as RS,writeFileSync as mu}from"node:fs";import{homedir as kS}from"node:os";import{basename as AS,join as Vo}from"node:path";q();ne();import{randomUUID as hS}from"node:crypto";import{writeFileSync as ES,readFileSync as AC,existsSync as xC}from"node:fs";import{join as bS}from"node:path";var SS=bS(W,"collections.json"),Ps=8;function Fs(e){return{...e}}function Re(e,t,n,s=null,r=new Date().toISOString()){h().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1223
- VALUES (?, ?, ?, ?, ?)`).run(e,s,t,n?JSON.stringify(n):null,r)}function $s(e){let t=h().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function ou(e){if(!e)return 0;let t=0,n=e,s=new Set,r=h();for(;n;){if(s.has(n))throw new Error("collection cycle detected");s.add(n);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)break;t+=1,n=o.parent_id}return t}function yS(e,t){let n=h(),s=e,r=new Set;for(;s;){if(r.has(s))return!1;if(r.add(s),s===t)return!0;let o=n.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)return!1;s=o.parent_id}return!1}function iu(e=!1){return h().prepare(`SELECT c.*,
1259
+ AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...s),o=0,a=0;for(let c of r){o+=1;let u=fu(c.auto_title);u!==c.auto_title&&(he(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function KS(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 VS(){K(),vS(Zo)||CS(Zo,{recursive:!0})}function QS(e,t,n,s){try{VS();let r=bu(Zo,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
1260
+ `;LS(r,o+t+`
1261
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}import{existsSync as Au,mkdirSync as ZS,readFileSync as eT,writeFileSync as tT}from"node:fs";import{homedir as nT}from"node:os";import{join as xu}from"node:path";import{z as ni}from"zod";function Nu(){return process.env.RECALL_HOME??xu(nT(),".recall")}function sT(){let e=Nu();Au(e)||ZS(e,{recursive:!0})}function Ou(){return xu(Nu(),"config.json")}var Bs=ni.object({heuristicEnabled:ni.boolean().default(!0),agentEnabled:ni.boolean().default(!1)}),Hs={heuristicEnabled:!0,agentEnabled:!1};function Lu(){let e=Ou();if(!Au(e))return{};try{return JSON.parse(eT(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function ft(){let e=Lu().autoTitle;if(!e)return{...Hs};let t=Bs.safeParse({...Hs,...e});return t.success?t.data:{...Hs}}function Cu(e){sT();let t=Lu(),n=Bs.parse({...Hs,...t.autoTitle??{},...e}),s={...t,autoTitle:n};return tT(Ou(),JSON.stringify(s,null,2)),n}H();Z();import{randomUUID as ii}from"node:crypto";import{existsSync as uT,mkdirSync as dT,writeFileSync as Uu}from"node:fs";import{homedir as pT}from"node:os";import{basename as mT,join as ai}from"node:path";H();Z();import{randomUUID as rT}from"node:crypto";import{writeFileSync as oT,readFileSync as wC,existsSync as RC}from"node:fs";import{join as iT}from"node:path";var aT=iT(W,"collections.json"),Ws=8;function qs(e){return{...e}}function ke(e,t,n,s=null,r=new Date().toISOString()){_().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1262
+ VALUES (?, ?, ?, ?, ?)`).run(e,s,t,n?JSON.stringify(n):null,r)}function Xs(e){let t=_().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function vu(e){if(!e)return 0;let t=0,n=e,s=new Set,r=_();for(;n;){if(s.has(n))throw new Error("collection cycle detected");s.add(n);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)break;t+=1,n=o.parent_id}return t}function cT(e,t){let n=_(),s=e,r=new Set;for(;s;){if(r.has(s))return!1;if(r.add(s),s===t)return!0;let o=n.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)return!1;s=o.parent_id}return!1}function Iu(e=!1){return _().prepare(`SELECT c.*,
1224
1263
  (SELECT COUNT(*) FROM collection_sessions cs WHERE cs.collection_id = c.id) AS session_count
1225
1264
  FROM collections c
1226
1265
  ${e?"":"WHERE c.archived_at IS NULL"}
1227
- ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(s=>({...Fs(s),session_count:s.session_count}))}function Ye(e){let t=h().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Fs(t):null}function au(e,t=!0){let n=h(),s=t?Go(e):[e];if(s.length===0)return[];let r=s.map(()=>"?").join(",");return n.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1266
+ ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(s=>({...qs(s),session_count:s.session_count}))}function Qe(e){let t=_().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?qs(t):null}function Mu(e,t=!0){let n=_(),s=t?si(e):[e];if(s.length===0)return[];let r=s.map(()=>"?").join(",");return n.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1228
1267
  FROM collection_sessions
1229
1268
  WHERE collection_id IN (${r})
1230
- ORDER BY added_at DESC`).all(...s)}function Go(e){let t=h(),n=[e],s=[e],r=new Set([e]);for(;s.length>0;){let o=s.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...s),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),n.push(u.id),c.push(u.id));s=c}return n}function cu(e){return h().prepare(`SELECT c.* FROM collections c
1269
+ ORDER BY added_at DESC`).all(...s)}function si(e){let t=_(),n=[e],s=[e],r=new Set([e]);for(;s.length>0;){let o=s.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...s),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),n.push(u.id),c.push(u.id));s=c}return n}function Du(e){return _().prepare(`SELECT c.* FROM collections c
1231
1270
  JOIN collection_sessions cs ON cs.collection_id = c.id
1232
1271
  WHERE cs.session_id = ? AND c.archived_at IS NULL
1233
- ORDER BY LOWER(c.name)`).all(e)}function yn(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let n=h(),s=new Date().toISOString(),r=hS();if(e.parent_id){if(!Ye(e.parent_id))throw new Error("parent collection not found");if(ou(e.parent_id)>=Ps-1)throw new Error(`max collection depth is ${Ps}`)}return n.transaction(()=>{n.prepare(`INSERT INTO collections
1272
+ ORDER BY LOWER(c.name)`).all(e)}function kn(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let n=_(),s=new Date().toISOString(),r=rT();if(e.parent_id){if(!Qe(e.parent_id))throw new Error("parent collection not found");if(vu(e.parent_id)>=Ws-1)throw new Error(`max collection depth is ${Ws}`)}return n.transaction(()=>{n.prepare(`INSERT INTO collections
1234
1273
  (id, name, description, icon, color, parent_id, sort_key, created_at, updated_at, archived_at)
1235
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",s,s),Re(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,s)})(),pt(),Ye(r)}function lu(e,t){let n=h(),s=$s(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():s.name,description:t.description!==void 0?t.description:s.description,icon:t.icon!==void 0?t.icon:s.icon,color:t.color!==void 0?t.color:s.color,parent_id:t.parent_id!==void 0?t.parent_id:s.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:s.sort_key};if(!o.name)throw new Error("name required");if(o.name.length>120)throw new Error("name too long (max 120 chars)");if(t.parent_id!==void 0&&t.parent_id!==s.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Ye(t.parent_id))throw new Error("parent collection not found");if(yS(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(ou(t.parent_id)>=Ps-1)throw new Error(`max collection depth is ${Ps}`)}return n.transaction(()=>{n.prepare(`UPDATE collections
1274
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",s,s),ke(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,s)})(),_t(),Qe(r)}function ju(e,t){let n=_(),s=Xs(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():s.name,description:t.description!==void 0?t.description:s.description,icon:t.icon!==void 0?t.icon:s.icon,color:t.color!==void 0?t.color:s.color,parent_id:t.parent_id!==void 0?t.parent_id:s.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:s.sort_key};if(!o.name)throw new Error("name required");if(o.name.length>120)throw new Error("name too long (max 120 chars)");if(t.parent_id!==void 0&&t.parent_id!==s.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Qe(t.parent_id))throw new Error("parent collection not found");if(cT(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(vu(t.parent_id)>=Ws-1)throw new Error(`max collection depth is ${Ws}`)}return n.transaction(()=>{n.prepare(`UPDATE collections
1236
1275
  SET name = ?, description = ?, icon = ?, color = ?,
1237
1276
  parent_id = ?, sort_key = ?, updated_at = ?
1238
- WHERE id = ?`).run(o.name,o.description,o.icon,o.color,o.parent_id,o.sort_key,r,e),t.name!==void 0&&t.name!==s.name&&Re(e,"rename",{from:s.name,to:o.name},null,r),t.description!==void 0&&t.description!==s.description&&Re(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==s.icon||t.color!==void 0&&t.color!==s.color)&&Re(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==s.parent_id&&Re(e,"move",{from:s.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==s.sort_key&&Re(e,"reorder",{from:s.sort_key,to:o.sort_key},null,r)})(),pt(),Ye(e)}function uu(e){let t=h(),n=$s(e);if(n.archived_at)return Fs(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(s,s,e),Re(e,"archive",{name:n.name},null,s)})(),pt(),Ye(e)}function du(e){let t=h(),n=$s(e);if(!n.archived_at)return Fs(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(s,e),Re(e,"restore",{name:n.name},null,s)})(),pt(),Ye(e)}function Tn(e,t,n=null,s={}){let r=h();if($s(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let c=s.source??"manual",u=s.rule_id??null;if(c==="auto"&&!u)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
1239
- VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,n,c,u),Re(e,"add",{note:n,source:c,rule_id:u},t,d)})(),pt(),{added:!0}}function pu(e,t){let n=h();if(!n.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return n.transaction(()=>{n.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),Re(e,"remove",null,t,r)})(),pt(),{removed:!0}}function Us(e){let t=h(),n=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1240
- WHERE rule_id = ?`).all(e);if(n.length===0)return{removed:0};let s=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of n)Re(o.collection_id,"remove",{rule_id:e},o.session_id,s)})(),pt(),{removed:n.length}}function TS(){return h().prepare(`SELECT id, collection_id, session_id, action, payload, at
1277
+ WHERE id = ?`).run(o.name,o.description,o.icon,o.color,o.parent_id,o.sort_key,r,e),t.name!==void 0&&t.name!==s.name&&ke(e,"rename",{from:s.name,to:o.name},null,r),t.description!==void 0&&t.description!==s.description&&ke(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==s.icon||t.color!==void 0&&t.color!==s.color)&&ke(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==s.parent_id&&ke(e,"move",{from:s.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==s.sort_key&&ke(e,"reorder",{from:s.sort_key,to:o.sort_key},null,r)})(),_t(),Qe(e)}function Pu(e){let t=_(),n=Xs(e);if(n.archived_at)return qs(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(s,s,e),ke(e,"archive",{name:n.name},null,s)})(),_t(),Qe(e)}function Fu(e){let t=_(),n=Xs(e);if(!n.archived_at)return qs(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(s,e),ke(e,"restore",{name:n.name},null,s)})(),_t(),Qe(e)}function An(e,t,n=null,s={}){let r=_();if(Xs(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let c=s.source??"manual",u=s.rule_id??null;if(c==="auto"&&!u)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
1278
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,n,c,u),ke(e,"add",{note:n,source:c,rule_id:u},t,d)})(),_t(),{added:!0}}function $u(e,t){let n=_();if(!n.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return n.transaction(()=>{n.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),ke(e,"remove",null,t,r)})(),_t(),{removed:!0}}function Js(e){let t=_(),n=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1279
+ WHERE rule_id = ?`).all(e);if(n.length===0)return{removed:0};let s=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of n)ke(o.collection_id,"remove",{rule_id:e},o.session_id,s)})(),_t(),{removed:n.length}}function lT(){return _().prepare(`SELECT id, collection_id, session_id, action, payload, at
1241
1280
  FROM collection_events
1242
- ORDER BY at ASC, id ASC`).all()}function pt(){try{V();let e=h(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
1281
+ ORDER BY at ASC, id ASC`).all()}function _t(){try{K();let e=_(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
1243
1282
  created_at, updated_at, archived_at
1244
1283
  FROM collections
1245
1284
  ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),n=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1246
1285
  FROM collection_sessions
1247
- ORDER BY collection_id, added_at`).all(),s=TS(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:n,events:s};ES(SS,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Hs=Vo(W,"auto-rules"),xS=Vo(Hs,"rules.json"),NS=Vo(Hs,"suggestions.json"),Yo="Repositories",OS="Topics",gu=3;var CS=5,LS=2,vS=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function Qo(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 IS(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 MS(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return Yo;case"tag":return OS;case"plan-file":return null}}function jS(e){let n=h().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(n)return n.id;let o=yn({name:e,icon:e===Yo?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===Yo?"0000-repos":"0001-topics"});return h().prepare(`INSERT INTO auto_collection_rules
1286
+ ORDER BY collection_id, added_at`).all(),s=lT(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:n,events:s};oT(aT,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Gs=ai(W,"auto-rules"),gT=ai(Gs,"rules.json"),fT=ai(Gs,"suggestions.json"),ri="Repositories",_T="Topics",Hu=3;var hT=5,ET=2,bT=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function ci(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 ST(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 TT(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return ri;case"tag":return _T;case"plan-file":return null}}function yT(e){let n=_().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(n)return n.id;let o=kn({name:e,icon:e===ri?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===ri?"0000-repos":"0001-topics"});return _().prepare(`INSERT INTO auto_collection_rules
1248
1287
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
1249
- VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(zo(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function DS(e,t,n){let s;if(n!==void 0)s=n;else{let o=MS(t);s=o?jS(o):null}return yn({name:e,parent_id:s}).id}function Bs(e){let t=h().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?Qo(t):null}function PS(e){let t=h();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(Ws(e.pattern)+"%").map(s=>s.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(Ws(e.pattern)+"%").map(s=>s.id);case"project-id":{let n=Number(e.pattern);return Number.isFinite(n)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(n).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(s=>s.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1250
- WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(s=>s.id)}}function fu(e,t,n=3){let s=h(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1288
+ VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(ii(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function wT(e,t,n){let s;if(n!==void 0)s=n;else{let o=TT(t);s=o?yT(o):null}return kn({name:e,parent_id:s}).id}function Ys(e){let t=_().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?ci(t):null}function RT(e){let t=_();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(zs(e.pattern)+"%").map(s=>s.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(zs(e.pattern)+"%").map(s=>s.id);case"project-id":{let n=Number(e.pattern);return Number.isFinite(n)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(n).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(s=>s.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1289
+ WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(s=>s.id)}}function Bu(e,t,n=3){let s=_(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1251
1290
  sa.alias AS alias, s.auto_title AS auto_title, s.first_user_message AS first_user_message`,o="LEFT JOIN session_aliases sa ON sa.session_id = s.id",a=[];switch(e){case"cwd-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1252
1291
  WHERE s.cwd IS NOT NULL AND s.cwd LIKE ? ESCAPE '\\'
1253
- ORDER BY s.started_at DESC LIMIT ?`).all(Ws(t)+"%",n);break;case"git-branch-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1292
+ ORDER BY s.started_at DESC LIMIT ?`).all(zs(t)+"%",n);break;case"git-branch-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1254
1293
  WHERE s.git_branch IS NOT NULL AND s.git_branch LIKE ? ESCAPE '\\'
1255
- ORDER BY s.started_at DESC LIMIT ?`).all(Ws(t)+"%",n);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1294
+ ORDER BY s.started_at DESC LIMIT ?`).all(zs(t)+"%",n);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1256
1295
  WHERE s.project_id = ?
1257
1296
  ORDER BY s.started_at DESC LIMIT ?`).all(c,n));break}case"tag":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1258
1297
  JOIN session_tags st ON st.session_id = s.id
@@ -1260,21 +1299,21 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1260
1299
  ORDER BY s.started_at DESC LIMIT ?`).all(t,n);break;case"plan-file":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1261
1300
  WHERE s.first_user_message IS NOT NULL
1262
1301
  AND s.first_user_message LIKE ?
1263
- ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",n);break}return a.map(c=>({id:c.id,title:FS(c),cwd:c.cwd,started_at:c.started_at}))}function FS(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return`session ${e.id.slice(0,8)}`;let n=t.split(`
1264
- `)[0].trim();return n.length>80?n.slice(0,80)+"\u2026":n}function $S(e,t,n=h()){switch(e.type){case"cwd-prefix":return!!t.cwd&&t.cwd.startsWith(e.pattern);case"git-branch-prefix":return!!t.git_branch&&t.git_branch.startsWith(e.pattern);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)&&t.project_id===s}case"tag":return!!n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(t.id,e.pattern);case"plan-file":return!!t.first_user_message&&t.first_user_message.includes(e.pattern)}}function _u(e){let t=h(),n=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!n)return{added:0};let s=t.prepare(`SELECT * FROM auto_collection_rules
1302
+ ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",n);break}return a.map(c=>({id:c.id,title:kT(c),cwd:c.cwd,started_at:c.started_at}))}function kT(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return`session ${e.id.slice(0,8)}`;let n=t.split(`
1303
+ `)[0].trim();return n.length>80?n.slice(0,80)+"\u2026":n}function AT(e,t,n=_()){switch(e.type){case"cwd-prefix":return!!t.cwd&&t.cwd.startsWith(e.pattern);case"git-branch-prefix":return!!t.git_branch&&t.git_branch.startsWith(e.pattern);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)&&t.project_id===s}case"tag":return!!n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(t.id,e.pattern);case"plan-file":return!!t.first_user_message&&t.first_user_message.includes(e.pattern)}}function Wu(e){let t=_(),n=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!n)return{added:0};let s=t.prepare(`SELECT * FROM auto_collection_rules
1265
1304
  WHERE enabled = 1 AND created_by != 'seed'
1266
- ORDER BY priority, created_at`).all(),r=0;for(let o of s){let a=Qo(o);if($S(a,n,t))try{Tn(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 Ko(e){if(!e.enabled)return{added:0};let t=0;for(let n of PS(e))try{Tn(e.collection_id,n,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(s){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${n}:`,s)}return{added:t}}function Zo(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let n=(e.pattern??"").trim();if(!n)throw new Error("pattern required");let s=h(),r=new Date().toISOString(),o=zo(),a=e.collection_id;a||(a=DS(t,e.type,e.parent_collection_id)),s.prepare(`INSERT INTO auto_collection_rules
1305
+ ORDER BY priority, created_at`).all(),r=0;for(let o of s){let a=ci(o);if(AT(a,n,t))try{An(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 oi(e){if(!e.enabled)return{added:0};let t=0;for(let n of RT(e))try{An(e.collection_id,n,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(s){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${n}:`,s)}return{added:t}}function li(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let n=(e.pattern??"").trim();if(!n)throw new Error("pattern required");let s=_(),r=new Date().toISOString(),o=ii(),a=e.collection_id;a||(a=wT(t,e.type,e.parent_collection_id)),s.prepare(`INSERT INTO auto_collection_rules
1267
1306
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
1268
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,n,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),s.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,n);let c=Bs(o);return Ko(c),Mt(),c}function hu(e={}){let t=e.includeSeed?"SELECT * FROM auto_collection_rules ORDER BY priority, created_at":"SELECT * FROM auto_collection_rules WHERE created_by != 'seed' ORDER BY priority, created_at";return h().prepare(t).all().map(Qo)}function Eu(e,t){let n=h(),s=Bs(e);if(!s)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():s.name,pattern:t.pattern!==void 0?t.pattern.trim():s.pattern,enabled:t.enabled!==void 0?t.enabled:s.enabled,priority:t.priority!==void 0?t.priority:s.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");n.prepare(`UPDATE auto_collection_rules
1307
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,n,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),s.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,n);let c=Ys(o);return oi(c),jt(),c}function qu(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 _().prepare(t).all().map(ci)}function Xu(e,t){let n=_(),s=Ys(e);if(!s)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():s.name,pattern:t.pattern!==void 0?t.pattern.trim():s.pattern,enabled:t.enabled!==void 0?t.enabled:s.enabled,priority:t.priority!==void 0?t.priority:s.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");n.prepare(`UPDATE auto_collection_rules
1269
1308
  SET name = ?, pattern = ?, enabled = ?, priority = ?
1270
- WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=Bs(e);return t.pattern!==void 0&&t.pattern!==s.pattern?(Us(e),o.enabled&&Ko(o)):t.enabled!==void 0&&t.enabled!==s.enabled&&(o.enabled?Ko(o):Us(e)),Mt(),o}function bu(e){let t=h();if(!Bs(e))return{removed:0};let s=Us(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),Mt(),s}function qs(e={}){let t=e.includeDismissed?"SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC":"SELECT * FROM auto_collection_suggestions WHERE dismissed = 0 ORDER BY detected_at DESC";return h().prepare(t).all().map(IS)}function Su(e){h().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),Mt()}function yu(e){let t=h(),n=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!n)throw new Error(`suggestion not found: ${e}`);if(n.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let s=Zo({name:n.suggested_name,type:n.type,pattern:n.pattern,parent_collection_id:n.suggested_parent_collection_id===null?void 0:n.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),Mt(),s}function Xs(){let e=h(),t=new Date().toISOString(),n=kS(),s=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...US(n,t).filter(c=>!s.has(c.pattern)),...HS(t),...BS(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),s.size>0){let c=Array.from(s).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(s))}let o=e.prepare("SELECT type, pattern FROM auto_collection_rules").all(),a=new Set(o.map(c=>`${c.type}:${c.pattern}`));for(let c of r){let u=`${c.type}:${c.pattern}`;if(a.has(u))continue;let d=e.prepare("SELECT id FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").get(c.type,c.pattern);d?e.prepare(`UPDATE auto_collection_suggestions
1309
+ WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=Ys(e);return t.pattern!==void 0&&t.pattern!==s.pattern?(Js(e),o.enabled&&oi(o)):t.enabled!==void 0&&t.enabled!==s.enabled&&(o.enabled?oi(o):Js(e)),jt(),o}function Ju(e){let t=_();if(!Ys(e))return{removed:0};let s=Js(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),jt(),s}function Ks(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 _().prepare(t).all().map(ST)}function Gu(e){_().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),jt()}function Yu(e){let t=_(),n=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!n)throw new Error(`suggestion not found: ${e}`);if(n.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let s=li({name:n.suggested_name,type:n.type,pattern:n.pattern,parent_collection_id:n.suggested_parent_collection_id===null?void 0:n.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),jt(),s}function Vs(){let e=_(),t=new Date().toISOString(),n=pT(),s=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...xT(n,t).filter(c=>!s.has(c.pattern)),...NT(t),...OT(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),s.size>0){let c=Array.from(s).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(s))}let o=e.prepare("SELECT type, pattern FROM auto_collection_rules").all(),a=new Set(o.map(c=>`${c.type}:${c.pattern}`));for(let c of r){let u=`${c.type}:${c.pattern}`;if(a.has(u))continue;let d=e.prepare("SELECT id FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").get(c.type,c.pattern);d?e.prepare(`UPDATE auto_collection_suggestions
1271
1310
  SET session_count = ?, detected_at = ?, suggested_name = ?, suggested_parent_collection_id = ?
1272
1311
  WHERE id = ?`).run(c.session_count,t,c.suggested_name,c.suggested_parent_collection_id,d.id):e.prepare(`INSERT INTO auto_collection_suggestions
1273
1312
  (id, type, pattern, suggested_name, suggested_parent_collection_id, session_count, detected_at, dismissed)
1274
- VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(zo(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return Mt(),qs()}function US(e,t){let s=h().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of s){let c=a.cwd.split("/").filter(Boolean),u="";for(let d of c){if(u=`${u}/${d}`,u===e||u==="/")continue;let p=r.get(u);p||(p=new Set,r.set(u,p)),p.add(a.id)}}let o=[];for(let[a,c]of r.entries()){if(c.size<gu)continue;let u=!1;for(let[d,p]of r.entries())if(d!==a&&d.startsWith(a+"/")&&p.size>=gu){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:AS(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function HS(e){return h().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(CS).map(n=>({type:"tag",pattern:n.tag,suggested_name:n.tag,suggested_parent_collection_id:null,session_count:n.n,detected_at:e,dismissed:!1}))}function BS(e){let t=h().prepare(`SELECT id, first_user_message FROM sessions
1275
- WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),n=new Map;for(let r of t)for(let o of vS){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=n.get(c);u||(u=new Set,n.set(c,u)),u.add(r.id)}}let s=[];for(let[r,o]of n.entries())o.size<LS||s.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return s}function Ws(e){return e.replace(/[\\%_]/g,"\\$&")}function Mt(){try{V(),wS(Hs)||RS(Hs,{recursive:!0});let e=h(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),n=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();mu(xS,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),mu(NS,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:n},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function Tu(){let e=h().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}var WS=/^[ \t]*<!--\s*claude-recall-alias\s*:\s*([^\n]+?)\s*-->[ \t]*\n?/;function ei(e){if(e==null)return{alias:null,stripped:""};let t=e.match(WS);if(!t)return{alias:null,stripped:e};let n=t[1].trim();return n?{alias:n.length>200?n.slice(0,200):n,stripped:e.slice(t[0].length)}:{alias:null,stripped:e.slice(t[0].length)}}var Js=new Map;function wu(e,t){let s=(Js.get(e)??Promise.resolve()).catch(()=>{}).then(t);return Js.set(e,s),s.catch(()=>{}).finally(()=>{Js.get(e)===s&&Js.delete(e)}),s}import{realpathSync as qS,statSync as XS,readFileSync as JS}from"node:fs";import{dirname as Ru,join as GS,parse as YS}from"node:path";var jt="(temporary sessions)";function Rn(e){return e?e.startsWith("/private/var/folders/")||e.startsWith("/var/folders/")||e.startsWith("/tmp/")||e.startsWith("/private/tmp/")||e==="/tmp"||e==="/private/tmp":!1}function ku(e,t){let n=e;for(;n!=="/"&&n!=="."&&n!=="";){if(t.has(n))return n;let s=Ru(n);if(s===n)break;n=s}return null}var wn=new Map;function Gs(e,t={}){let n=t.foldWorktrees===!0,s=`${n?"1":"0"}:${e}`,r=wn.get(s);if(r)return r;let o={root:e,isRepo:!1,isWorktree:!1,mainRepo:null},a;try{a=qS(e)}catch{return wn.set(s,o),o}let{root:c}=YS(a),u=a;for(;;){let d=GS(u,".git"),p=null;try{p=XS(d)}catch{p=null}if(p&&p.isDirectory()){let f={root:u,isRepo:!0,isWorktree:!1,mainRepo:null};return wn.set(s,f),f}if(p&&p.isFile()){let f=KS(d),b={root:n&&f?f:u,isRepo:!0,isWorktree:f!==null,mainRepo:f};return wn.set(s,b),b}if(u===c)break;u=Ru(u)}return wn.set(s,o),o}function KS(e){let t;try{t=JS(e,"utf8")}catch{return null}let n=t.match(/^gitdir:\s*(.+?)\s*$/m);if(!n)return null;let s=n[1].trim(),o=s.indexOf("/.git/worktrees/");return o<0?null:s.slice(0,o)}q();bt();q();function Au(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function zS(e){let t=[],o=0;for(;o<e.length;){let a=[],c=0;for(;a.length<5&&o<e.length;){let d=e[o],p=Au(d.content_text??"");if(c+p.length>2e3&&a.length>=3)break;a.push(d),c+=p.length,o++}if(a.length===0)break;let u=a.map(d=>{let p=d.role??"system",f=Au(d.content_text??"");return`[${p}] ${f}`}).join(`
1313
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(ii(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return jt(),Ks()}function xT(e,t){let s=_().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of s){let c=a.cwd.split("/").filter(Boolean),u="";for(let d of c){if(u=`${u}/${d}`,u===e||u==="/")continue;let p=r.get(u);p||(p=new Set,r.set(u,p)),p.add(a.id)}}let o=[];for(let[a,c]of r.entries()){if(c.size<Hu)continue;let u=!1;for(let[d,p]of r.entries())if(d!==a&&d.startsWith(a+"/")&&p.size>=Hu){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:mT(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function NT(e){return _().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(hT).map(n=>({type:"tag",pattern:n.tag,suggested_name:n.tag,suggested_parent_collection_id:null,session_count:n.n,detected_at:e,dismissed:!1}))}function OT(e){let t=_().prepare(`SELECT id, first_user_message FROM sessions
1314
+ WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),n=new Map;for(let r of t)for(let o of bT){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=n.get(c);u||(u=new Set,n.set(c,u)),u.add(r.id)}}let s=[];for(let[r,o]of n.entries())o.size<ET||s.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return s}function zs(e){return e.replace(/[\\%_]/g,"\\$&")}function jt(){try{K(),uT(Gs)||dT(Gs,{recursive:!0});let e=_(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),n=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();Uu(gT,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),Uu(fT,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:n},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function zu(){let e=_().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}var LT=/^[ \t]*<!--\s*claude-recall-alias\s*:\s*([^\n]+?)\s*-->[ \t]*\n?/;function ui(e){if(e==null)return{alias:null,stripped:""};let t=e.match(LT);if(!t)return{alias:null,stripped:e};let n=t[1].trim();return n?{alias:n.length>200?n.slice(0,200):n,stripped:e.slice(t[0].length)}:{alias:null,stripped:e.slice(t[0].length)}}var Qs=new Map;function Ku(e,t){let s=(Qs.get(e)??Promise.resolve()).catch(()=>{}).then(t);return Qs.set(e,s),s.catch(()=>{}).finally(()=>{Qs.get(e)===s&&Qs.delete(e)}),s}import{realpathSync as CT,statSync as vT,readFileSync as IT}from"node:fs";import{dirname as Vu,join as MT,parse as DT}from"node:path";var Pt="(temporary sessions)";function Nn(e){return e?e.startsWith("/private/var/folders/")||e.startsWith("/var/folders/")||e.startsWith("/tmp/")||e.startsWith("/private/tmp/")||e==="/tmp"||e==="/private/tmp":!1}function Qu(e,t){let n=e;for(;n!=="/"&&n!=="."&&n!=="";){if(t.has(n))return n;let s=Vu(n);if(s===n)break;n=s}return null}var xn=new Map;function Zs(e,t={}){let n=t.foldWorktrees===!0,s=`${n?"1":"0"}:${e}`,r=xn.get(s);if(r)return r;let o={root:e,isRepo:!1,isWorktree:!1,mainRepo:null},a;try{a=CT(e)}catch{return xn.set(s,o),o}let{root:c}=DT(a),u=a;for(;;){let d=MT(u,".git"),p=null;try{p=vT(d)}catch{p=null}if(p&&p.isDirectory()){let f={root:u,isRepo:!0,isWorktree:!1,mainRepo:null};return xn.set(s,f),f}if(p&&p.isFile()){let f=jT(d),S={root:n&&f?f:u,isRepo:!0,isWorktree:f!==null,mainRepo:f};return xn.set(s,S),S}if(u===c)break;u=Vu(u)}return xn.set(s,o),o}function jT(e){let t;try{t=IT(e,"utf8")}catch{return null}let n=t.match(/^gitdir:\s*(.+?)\s*$/m);if(!n)return null;let s=n[1].trim(),o=s.indexOf("/.git/worktrees/");return o<0?null:s.slice(0,o)}H();We();H();function Zu(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function PT(e){let t=[],o=0;for(;o<e.length;){let a=[],c=0;for(;a.length<5&&o<e.length;){let d=e[o],p=Zu(d.content_text??"");if(c+p.length>2e3&&a.length>=3)break;a.push(d),c+=p.length,o++}if(a.length===0)break;let u=a.map(d=>{let p=d.role??"system",f=Zu(d.content_text??"");return`[${p}] ${f}`}).join(`
1276
1315
 
1277
- `);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 xu(e){let n=h().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return zS(n)}q();function ti(e){let t=h();t.transaction(()=>{t.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()&&t.prepare("DELETE FROM vec_chunks_v1_backup WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(e)})()}function ri(){try{return!!h().prepare("SELECT 1 AS held FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: migration_state"))return!1;throw e}}function VS(){return h().prepare("UPDATE chunk_queue SET action = 'embed' WHERE action = 'pending_post_migration'").run().changes}function QS(){try{return!!h().prepare("SELECT 1 AS present FROM chunk_queue WHERE action = 'pending_post_migration' LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: chunk_queue"))return!1;throw e}}var Mu=2e3,ZS=1e4,mt=null,Ys=!1,oi=null,ni=0;function ju(e){ni=Math.max(0,Math.floor(e))}var Ie=new Set,Du=-1;function Pu(e){Ie.add(e)}function Fu(){Ie.add(Du)}function Nu(e){if(Ie.has(Du))return!0;if(Ie.size===0)return!1;let t=h().prepare("SELECT project_id FROM sessions WHERE id = ?").get(e);return t?Ie.has(t.project_id):!1}var Ou=10,ey=200,ty=2e3;function ny(){let e=Number(process.env.RECALL_VECTOR_HARD_CAP??"");return Number.isFinite(e)&&e>0?Math.min(ty,Math.floor(e)):ey}var sy=3,Cu=1e3,kn=new Map,si=new Set;function Lu(e,t,n,s){if(e.size>=Cu&&(e instanceof Map,!e.has(t))){let r=e.keys().next();r.done||(e.delete(r.value),console.warn(`[vector-worker] ${s??"tracking-map"} hit ${Cu}-entry cap; evicted oldest entry`))}e instanceof Map?e.set(t,n):e.add(t)}var An=[],ry=50,vu=.3,Dt=[],oy=50,Iu=!1,xn=null,iy=0;function ay(e){if(!Iu){Iu=!0;return}e<=0||(An.push({ts:Date.now(),durationMs:e}),An.length>ry&&An.shift(),xn=xn===null?e:vu*e+(1-vu)*xn,iy+=1)}function cy(e){e<=0||(Dt.push({ts:Date.now(),chunks:e}),Dt.length>oy&&Dt.shift())}function ii(){if(An.length<5||xn===null)return null;let e=1e3/xn,t=Dt.length>0?Dt.reduce((n,s)=>n+s.chunks,0)/Dt.length:30;return{samples:An.length,chunksPerSec:e,sessionsPerSec:e/Math.max(1,t),avgChunksPerSession:t,lastProcessedAt:oi}}function ly(){return h().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function uy(){if(ri())return!1;let e=h(),t=e.prepare("SELECT DISTINCT session_id FROM chunk_queue ORDER BY id LIMIT 1").get();if(!t)return Ie.size>0&&Ie.clear(),!1;if(Nu(t.session_id)||si.has(t.session_id))return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(t.session_id),!0;let n=t.session_id;try{ti(n);let s=xu(n),r=ni>0?ni:1/0,o=Math.min(r,ny()),a=o<s.length?s.slice(0,o):s;if(a.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),!0;let c=pe().modelId,u=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, ?, 768, 0, datetime('now'))"),d=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)"),p=0,f=!1;for(let S=0;S<a.length;S+=Ou){if(Nu(n)){f=!0;break}let y=Math.min(a.length,S+Ou),b=a.slice(S,y),R=b.map(v=>v.text),k=Date.now(),M=await Et(R),P=Date.now()-k;e.transaction(()=>{for(let v=0;v<b.length;v++){let F=u.run(n,JSON.stringify(b[v].messageUuids),b[v].text,c),U=Buffer.from(M[v].buffer,M[v].byteOffset,M[v].byteLength);d.run(BigInt(F.lastInsertRowid),U)}})();let D=b.length>0?P/b.length:0;for(let v=0;v<b.length;v++)ay(D);p+=b.length,await new Promise(v=>setImmediate(v))}f&&ti(n),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),oi=new Date().toISOString(),cy(p),kn.has(n)&&kn.delete(n)}catch(s){console.error("[vector-worker] failed for session",n,s);let r=(kn.get(n)??0)+1;Lu(kn,n,r,"failureCounts"),r>=sy&&(Lu(si,n,void 0,"blacklist"),kn.delete(n),console.error(`[vector-worker] blacklisted session ${n} after ${r} failures \u2014 will not retry until daemon restart`)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n)}finally{if(Ie.size>0)try{let s=e.prepare("SELECT project_id FROM sessions WHERE id = ?").get(n);s!==void 0&&Ie.has(s.project_id)&&(e.prepare("SELECT 1 FROM chunk_queue cq JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ? LIMIT 1").get(s.project_id)||Ie.delete(s.project_id))}catch(s){console.error("[vector-worker] cancelledProjects cleanup failed:",s)}}return!0}async function $u(){if(!pe().loaded)return;QS()&&VS();let e=await uy();mt!==null&&clearTimeout(mt),mt=setTimeout(()=>{$u()},e?Mu:ZS)}function Nn(){if(!Ys){if(!pe().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}Ys=!0,mt=setTimeout(()=>{$u()},Mu)}}function Uu(){Ys=!1,mt!==null&&(clearTimeout(mt),mt=null)}function Me(){return{running:Ys,queueDepth:ly(),lastProcessedAt:oi,blacklistedCount:si.size,pausedForMigration:ri()}}var je=new Map;function Hu(e,t=Date.now()){let n=je.get(e);if(n?n.count+=1:je.set(e,{count:1,firstSeenAt:t}),je.size>1e4&&(Bu(t),je.size>1e4)){let s=[...je.entries()].sort((o,a)=>o[1].firstSeenAt-a[1].firstSeenAt),r=je.size-1e4;for(let o=0;o<r;o+=1){let a=s[o];a&&je.delete(a[0])}}}function Bu(e){let t=e-36e5;for(let[n,s]of je)s.firstSeenAt<t&&je.delete(n)}function Ks(e=10,t=Date.now()){Bu(t);let n=[];for(let[s,r]of je)n.push({path:s,count:r.count,firstSeenAt:r.firstSeenAt});return n.sort((s,r)=>r.count-s.count),n.slice(0,e)}var gy=1500,ai=new Map;function ci(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}function qu(e){return e.replace(/\\/g,"/").includes("/subagents/")}function fy(e){let s=h().prepare("SELECT COUNT(*) AS n FROM sessions WHERE file_path = ?").get(e).n;console.log(`[watcher] indexed ${zs(e)} (${s} session${s===1?"":"s"})`)}function _y(e,t){let n=zs(e).replace(/\.jsonl$/,"");if(!n)return;let s=ci(e);if(!s)return;let o=`parse_failed:${t instanceof Error&&t.name||"Error"}`,a=h(),c=a.prepare("SELECT id, skipped_reason FROM sessions WHERE id = ? LIMIT 1").get(n);if(c?.skipped_reason)return;let u=0;try{u=Vs(e).mtimeMs}catch{}if(c){a.prepare("UPDATE sessions SET skipped_reason = ? WHERE id = ?").run(o,n);return}if(u===0)return;let d=s.replace(/^-/,"/").replace(/-/g,"/"),p=zs(d)||s,{id:f}=a.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1316
+ `);t.push({messageUuids:a.map(d=>d.uuid),text:u}),o<e.length&&a.length>=3&&(o=Math.max(o-1,o-1))}return t}function ed(e){let n=_().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 PT(n)}H();function di(e){let t=_();t.transaction(()=>{t.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()&&t.prepare("DELETE FROM vec_chunks_v1_backup WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(e)})()}function gi(){try{return!!_().prepare("SELECT 1 AS held FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: migration_state"))return!1;throw e}}function ad(){return _().prepare("UPDATE chunk_queue SET action = 'embed' WHERE action = 'pending_post_migration'").run().changes}function FT(){try{return!!_().prepare("SELECT 1 AS present FROM chunk_queue WHERE action = 'pending_post_migration' LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: chunk_queue"))return!1;throw e}}var cd=2e3,$T=1e4,ht=null,er=!1,fi=null,pi=0;function ld(e){pi=Math.max(0,Math.floor(e))}var Ie=new Set,ud=-1;function dd(e){Ie.add(e)}function pd(){Ie.add(ud)}function td(e){if(Ie.has(ud))return!0;if(Ie.size===0)return!1;let t=_().prepare("SELECT project_id FROM sessions WHERE id = ?").get(e);return t?Ie.has(t.project_id):!1}var nd=10,UT=200,HT=2e3;function BT(){let e=Number(process.env.RECALL_VECTOR_HARD_CAP??"");return Number.isFinite(e)&&e>0?Math.min(HT,Math.floor(e)):UT}var WT=3,sd=1e3,On=new Map,mi=new Set;function rd(e,t,n,s){if(e.size>=sd&&(e instanceof Map,!e.has(t))){let r=e.keys().next();r.done||(e.delete(r.value),console.warn(`[vector-worker] ${s??"tracking-map"} hit ${sd}-entry cap; evicted oldest entry`))}e instanceof Map?e.set(t,n):e.add(t)}var Ln=[],qT=50,od=.3,Ft=[],XT=50,id=!1,Cn=null,JT=0;function GT(e){if(!id){id=!0;return}e<=0||(Ln.push({ts:Date.now(),durationMs:e}),Ln.length>qT&&Ln.shift(),Cn=Cn===null?e:od*e+(1-od)*Cn,JT+=1)}function YT(e){e<=0||(Ft.push({ts:Date.now(),chunks:e}),Ft.length>XT&&Ft.shift())}function _i(){if(Ln.length<5||Cn===null)return null;let e=1e3/Cn,t=Ft.length>0?Ft.reduce((n,s)=>n+s.chunks,0)/Ft.length:30;return{samples:Ln.length,chunksPerSec:e,sessionsPerSec:e/Math.max(1,t),avgChunksPerSession:t,lastProcessedAt:fi}}function zT(){return _().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function KT(){if(gi())return!1;let e=_(),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(td(t.session_id)||mi.has(t.session_id))return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(t.session_id),!0;let n=t.session_id;try{di(n);let s=ed(n),r=pi>0?pi:1/0,o=Math.min(r,BT()),a=o<s.length?s.slice(0,o):s;if(a.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),!0;let c=le().modelId,u=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, ?, 768, 0, datetime('now'))"),d=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)"),p=0,f=!1;for(let b=0;b<a.length;b+=nd){if(td(n)){f=!0;break}let T=Math.min(a.length,b+nd),S=a.slice(b,T),w=S.map(F=>F.text),A=Date.now(),I=await Ve(w),j=Date.now()-A;e.transaction(()=>{for(let F=0;F<S.length;F++){let ne=u.run(n,JSON.stringify(S[F].messageUuids),S[F].text,c),U=Buffer.from(I[F].buffer,I[F].byteOffset,I[F].byteLength);d.run(BigInt(ne.lastInsertRowid),U)}})();let M=S.length>0?j/S.length:0;for(let F=0;F<S.length;F++)GT(M);p+=S.length,await new Promise(F=>setImmediate(F))}f&&di(n),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),fi=new Date().toISOString(),YT(p),On.has(n)&&On.delete(n)}catch(s){console.error("[vector-worker] failed for session",n,s);let r=(On.get(n)??0)+1;rd(On,n,r,"failureCounts"),r>=WT&&(rd(mi,n,void 0,"blacklist"),On.delete(n),console.error(`[vector-worker] blacklisted session ${n} after ${r} failures \u2014 will not retry until daemon restart`)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n)}finally{if(Ie.size>0)try{let s=e.prepare("SELECT project_id FROM sessions WHERE id = ?").get(n);s!==void 0&&Ie.has(s.project_id)&&(e.prepare("SELECT 1 FROM chunk_queue cq JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ? LIMIT 1").get(s.project_id)||Ie.delete(s.project_id))}catch(s){console.error("[vector-worker] cancelledProjects cleanup failed:",s)}}return!0}async function md(){if(!le().loaded)return;FT()&&ad();let e=await KT();ht!==null&&clearTimeout(ht),ht=setTimeout(()=>{md()},e?cd:$T)}function vn(){if(!er){if(!le().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}er=!0,ht=setTimeout(()=>{md()},cd)}}function gd(){er=!1,ht!==null&&(clearTimeout(ht),ht=null)}function Me(){return{running:er,queueDepth:zT(),lastProcessedAt:fi,blacklistedCount:mi.size,pausedForMigration:gi()}}var De=new Map;function fd(e,t,n=Date.now()){let s=De.get(e);if(s?(s.count+=1,t>s.lastSize?s.noProgressCount=0:s.noProgressCount+=1,s.lastSize=t):De.set(e,{count:1,noProgressCount:0,firstSeenAt:n,lastSize:t}),De.size>1e4&&(_d(n),De.size>1e4)){let r=[...De.entries()].sort((a,c)=>a[1].firstSeenAt-c[1].firstSeenAt),o=De.size-1e4;for(let a=0;a<o;a+=1){let c=r[a];c&&De.delete(c[0])}}}function _d(e){let t=e-36e5;for(let[n,s]of De)s.firstSeenAt<t&&De.delete(n)}function tr(e=10,t=Date.now()){_d(t);let n=[];for(let[s,r]of De)n.push({path:s,count:r.count,noProgressCount:r.noProgressCount,firstSeenAt:r.firstSeenAt});return n.sort((s,r)=>r.noProgressCount-s.noProgressCount||r.count-s.count||s.path.localeCompare(r.path)),n.slice(0,e)}function hd(e,t,n=1500,s=8e3){return t===void 0?n:Math.max(n,t+s-e)}function hi(e){return e.replace(/\\/g,"/").includes("/subagents/")}function Ei(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}function bi(e){let{filePath:t,existing:n,currentMtime:s}=e;return hi(t)?{kind:"skip-durable",reason:"subagent_path"}:Ei(t)?s===null?{kind:"skip-transient",reason:"file_vanished"}:n?.skipped_reason?{kind:"skip-already-marked"}:n&&n.file_mtime>=s?{kind:"skip-transient",reason:"unchanged_mtime"}:{kind:"process"}:{kind:"skip-durable",reason:"no_encoded_project"}}function nr(e,t,n,s={}){switch(n.kind){case"process":case"skip-transient":case"skip-already-marked":return;case"skip-durable":{VT(e,t,n.reason,n.detail,s);return}default:{let r=n;return}}}function VT(e,t,n,s,r){switch(n){case"subagent_path":return;case"no_encoded_project":{console.warn(`[watcher] cannot persist marker for ${t} \u2014 no encoded-project segment`);return}case"daemon_spawn_phantom":return;case"parse_failed":return;default:{let o=n;return}}}var Si=new Map,Sd=new Map;var Ti=Ei,rr=hi;function ty(e){let s=_().prepare("SELECT COUNT(*) AS n FROM sessions WHERE file_path = ?").get(e).n;console.log(`[watcher] indexed ${sr(e)} (${s} session${s===1?"":"s"})`)}function ny(e,t){let n=sr(e).replace(/\.jsonl$/,"");if(!n)return;let s=Ti(e);if(!s)return;let o=`parse_failed:${t instanceof Error&&t.name||"Error"}`,a=_(),c=a.prepare("SELECT id, skipped_reason FROM sessions WHERE id = ? LIMIT 1").get(n);if(c?.skipped_reason)return;let u=0;try{u=$t(e).mtimeMs}catch{}if(c){a.prepare("UPDATE sessions SET skipped_reason = ? WHERE id = ?").run(o,n);return}if(u===0)return;let d=s.replace(/^-/,"/").replace(/-/g,"/"),p=sr(d)||s,{id:f}=a.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1278
1317
  VALUES (?, ?, ?, NULL, NULL, 0, 0)
1279
1318
  ON CONFLICT(encoded_path) DO UPDATE SET decoded_path = excluded.decoded_path
1280
1319
  RETURNING id`).get(s,d,p);a.prepare(`INSERT INTO sessions (
@@ -1283,16 +1322,16 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1283
1322
  user_message_count, assistant_message_count,
1284
1323
  first_user_message, cwd, git_branch, version, indexed_at,
1285
1324
  skipped_reason
1286
- ) VALUES (?, ?, ?, ?, NULL, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, ?, ?)`).run(n,f,e,u,new Date().toISOString(),o)}async function hy(e){let t=0;try{t=Vs(e).mtimeMs}catch{return}let n=ci(e);if(!n)return;let s=h(),r=s.prepare("SELECT id, file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1").get(e);if(r?.skipped_reason||r&&r.file_mtime>=t)return;let o=new Map,a=null;for await(let v of bl(e)){let F=o.get(v.sessionId);if(F||(F={sessionId:v.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},o.set(v.sessionId,F)),F.entries.push(v),v.timestamp&&((!F.earliest||v.timestamp<F.earliest)&&(F.earliest=v.timestamp),(!F.latest||v.timestamp>F.latest)&&(F.latest=v.timestamp)),v.role==="user"&&!v.isSidechain){if(F.users+=1,!F.firstUser&&v.contentText){let U=v.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();U&&!/^<local-command-caveat>/.test(U)&&(F.firstUser=Se(U).redacted.slice(0,2e3))}}else v.role==="assistant"&&!v.isSidechain&&(F.assistants+=1);!F.cwd&&v.cwd&&(F.cwd=v.cwd),!F.branch&&v.gitBranch&&(F.branch=v.gitBranch),!F.version&&v.version&&(F.version=v.version),!a&&v.cwd&&(a=v.cwd)}let c=a?zs(a)||a:n,u=a??n.replace(/^-/,"/").replace(/-/g,"/"),d=null,p=null,f=0,S=0;if(Rn(u))d=jt;else{let v=Gs(u);v.isRepo&&(d=v.root,p=v.mainRepo,f=1,S=v.isWorktree?1:0)}let y=s.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1287
- VALUES (?, ?, ?, ?, ?, ?, ?)
1325
+ ) VALUES (?, ?, ?, ?, NULL, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, ?, ?)`).run(n,f,e,u,new Date().toISOString(),o)}async function sy(e){let t=null;try{t=$t(e).mtimeMs}catch{}let n=_(),s=n.prepare("SELECT id, file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1").get(e),r=bi({filePath:e,existing:s?{file_mtime:s.file_mtime,skipped_reason:s.skipped_reason}:null,currentMtime:t});if(r.kind!=="process"){nr(n,e,r);return}let o=Ti(e);if(!o)return;let a=t,c=$t(e,{bigint:!0}),u=Gl(n,e),d=u?Cs(e,u.byteOffset)===u.prefixHash:!1,p=ql(u,{size:Number(c.size),inode:Number(c.ino)},d),f=p.mode==="incremental"?p.start:0,{entries:b,endOffset:T}=await Wl(e,f);if(p.mode==="incremental"&&b.length===0&&T===f){n.prepare("UPDATE sessions SET file_mtime = ? WHERE file_path = ?").run(a,e);let x=n.prepare("SELECT line_count FROM file_cursor WHERE file_path = ? LIMIT 1").get(e)?.line_count??0;Bo(n,e,{byteOffset:T,sizeBytes:Number(c.size),inode:Number(c.ino),prefixHash:u?.prefixHash??Cs(e,T),mtime:a,lineCount:x});return}let S=new Map,w=null;for(let x of b){let k=S.get(x.sessionId);if(k||(k={sessionId:x.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},S.set(x.sessionId,k)),k.entries.push(x),x.timestamp&&((!k.earliest||x.timestamp<k.earliest)&&(k.earliest=x.timestamp),(!k.latest||x.timestamp>k.latest)&&(k.latest=x.timestamp)),x.role==="user"&&!x.isSidechain){if(k.users+=1,!k.firstUser&&x.contentText){let L=x.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();L&&!/^<local-command-caveat>/.test(L)&&(k.firstUser=be(L).redacted.slice(0,2e3))}}else x.role==="assistant"&&!x.isSidechain&&(k.assistants+=1);!k.cwd&&x.cwd&&(k.cwd=x.cwd),!k.branch&&x.gitBranch&&(k.branch=x.gitBranch),!k.version&&x.version&&(k.version=x.version),!w&&x.cwd&&(w=x.cwd)}let A=w!==null,I=w?sr(w)||w:o,j=w??o.replace(/^-/,"/").replace(/-/g,"/"),J=null,M=null,F=0,ne=0;if(Nn(j))J=Pt;else{let x=Zs(j);x.isRepo&&(J=x.root,M=x.mainRepo,F=1,ne=x.isWorktree?1:0)}let U=n.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1326
+ VALUES (@encoded_path, @decoded_path, @name, @repo_root, @main_repo, @is_repo, @is_worktree)
1288
1327
  ON CONFLICT(encoded_path) DO UPDATE SET
1289
- decoded_path = excluded.decoded_path,
1290
- name = excluded.name,
1291
- repo_root = excluded.repo_root,
1292
- main_repo = excluded.main_repo,
1293
- is_repo = excluded.is_repo,
1294
- is_worktree = excluded.is_worktree
1295
- RETURNING id`),b=s.prepare(`
1328
+ decoded_path = CASE WHEN @have_cwd = 1 THEN excluded.decoded_path ELSE projects.decoded_path END,
1329
+ name = CASE WHEN @have_cwd = 1 THEN excluded.name ELSE projects.name END,
1330
+ repo_root = CASE WHEN @have_cwd = 1 THEN excluded.repo_root ELSE projects.repo_root END,
1331
+ main_repo = CASE WHEN @have_cwd = 1 THEN excluded.main_repo ELSE projects.main_repo END,
1332
+ is_repo = CASE WHEN @have_cwd = 1 THEN excluded.is_repo ELSE projects.is_repo END,
1333
+ is_worktree = CASE WHEN @have_cwd = 1 THEN excluded.is_worktree ELSE projects.is_worktree END
1334
+ RETURNING id`),v=n.prepare(`
1296
1335
  INSERT INTO sessions (
1297
1336
  id, project_id, file_path, file_mtime,
1298
1337
  started_at, ended_at, message_count,
@@ -1314,13 +1353,13 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1314
1353
  git_branch = excluded.git_branch,
1315
1354
  version = excluded.version,
1316
1355
  indexed_at = excluded.indexed_at
1317
- `),R=s.prepare(`
1356
+ `),i=n.prepare(`
1318
1357
  INSERT INTO messages (uuid, session_id, parent_uuid, type, role, timestamp,
1319
1358
  is_sidechain, content_text, tool_names, raw_json)
1320
1359
  VALUES (@uuid, @session_id, @parent_uuid, @type, @role, @timestamp,
1321
1360
  @is_sidechain, @content_text, @tool_names, @raw_json)
1322
1361
  ON CONFLICT(uuid) DO NOTHING
1323
- `),k=s.prepare("DELETE FROM messages WHERE session_id = ?"),M=s.prepare(`
1362
+ `),l=n.prepare("DELETE FROM messages WHERE session_id = ?"),m=n.prepare(`
1324
1363
  INSERT INTO sessions (
1325
1364
  id, project_id, file_path, file_mtime,
1326
1365
  started_at, ended_at, message_count,
@@ -1335,16 +1374,25 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1335
1374
  file_mtime = excluded.file_mtime,
1336
1375
  indexed_at = excluded.indexed_at,
1337
1376
  skipped_reason = 'daemon_spawn_phantom'
1338
- `),P=new Date().toISOString(),G=new Set;if(s.transaction(()=>{let{id:v}=y.get(n,u,c,d,p,f,S);for(let F of o.values()){if(Hl(F.firstUser)){console.log(`[watcher] skipping daemon-spawn phantom ${F.sessionId} (first message matches autonomous-spawn pattern)`),k.run(F.sessionId),M.run({id:F.sessionId,project_id:v,file_path:e,file_mtime:t,started_at:F.earliest,ended_at:F.latest,cwd:F.cwd,indexed_at:P}),G.add(F.sessionId);continue}let U=ei(F.firstUser),L=U.alias?U.stripped:F.firstUser;if(b.run({id:F.sessionId,project_id:v,file_path:e,file_mtime:t,started_at:F.earliest,ended_at:F.latest,message_count:F.entries.length,user_message_count:F.users,assistant_message_count:F.assistants,first_user_message:L,cwd:F.cwd,git_branch:F.branch,version:F.version,indexed_at:P}),U.alias&&!ye(F.sessionId))try{he(F.sessionId,U.alias)}catch(i){console.error(`[watcher] header-alias setAlias failed for ${F.sessionId}:`,i)}k.run(F.sessionId);for(let i of F.entries)R.run(Ey(i));Il(s,F.sessionId,F.entries),Cs(s,F.sessionId)}})(),dt().heuristicEnabled)for(let v of o.values()){if(G.has(v.sessionId))continue;let F=ei(v.firstUser).stripped,U=It(F);U&&Ee(v.sessionId,U,"heuristic")}}function Xu(e){return wu(e,()=>hy(e)).finally(()=>{Hu(e)})}function Ey(e){let t=Se(e.contentText).redacted,n=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:n}}function Wu(e){let t=ai.get(e);t?.timer&&clearTimeout(t.timer);let n={timer:null};n.timer=setTimeout(()=>{ai.delete(e),Xu(e).then(async()=>{fy(e),Ns(e);try{let r=h().prepare("SELECT id FROM sessions WHERE file_path = ? AND skipped_reason IS NULL").all(e);for(let o of r){Gc(o.id),Dl(o.id);try{_u(o.id)}catch(a){console.error("[watcher] auto-collections apply failed:",a)}}}catch(s){console.error("[watcher] semantic dispatch failed:",s)}}).catch(s=>{console.error(`[watcher] reindex failed for ${e}:`,s)})},gy),ai.set(e,n)}function Ju(){let e=dy(nn,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:t=>{if(t.endsWith(".jsonl"))return!1;try{if(Vs(t).isDirectory())return!1}catch{}return!0}});return e.on("add",t=>t.endsWith(".jsonl")&&Wu(t)),e.on("change",t=>t.endsWith(".jsonl")&&Wu(t)),e.on("ready",()=>{console.log(`[watcher] ready, watching ${nn}`)}),e.on("error",t=>{console.error("[watcher] chokidar error:",t)}),process.env.RECALL_WATCHER_DEBUG==="1"&&(e.on("add",t=>console.log(`[watcher.debug] add: ${t}`)),e.on("change",t=>console.log(`[watcher.debug] change: ${t}`)),e.on("unlink",t=>console.log(`[watcher.debug] unlink: ${t}`))),e}var by=4;function*li(e){let t;try{t=py(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=my(e,n.name);n.isDirectory()?yield*li(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}async function ui(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},n=h(),s=[],r=n.prepare("SELECT file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1");for(let d of li(nn)){if(t.scanned+=1,qu(d)){t.skipped+=1;continue}let p;try{p=Vs(d).mtimeMs}catch{t.errors+=1;continue}let f=r.get(d);if(f?.skipped_reason){t.skipped+=1;continue}if(f&&f.file_mtime>=p){t.upToDate+=1;continue}s.push(d)}if(s.length===0)return t.durationMs=Date.now()-e,t;let o=s.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await Xu(d),t.reindexed+=1}catch(p){t.errors+=1;let f=p instanceof Error?p.message:String(p);try{_y(d,p)}catch(S){console.error(`[ingestion-sweep] marker write failed for ${d}:`,S)}console.error(`[ingestion-sweep] failed for ${d}: ${f}`)}}},c=Math.min(by,s.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}async function Gu(){try{let e=await ui();e.reindexed>0&&console.log(`[safety-sweep] reindexed ${e.reindexed} file(s) the watcher missed (scanned=${e.scanned}, upToDate=${e.upToDate})`)}catch(e){console.error("[safety-sweep] failed:",e)}}import{execFileSync as zu}from"node:child_process";var Sy=["dist/mcp-server.js","dist/mcp/server.js"];function yy(e){for(let t of Sy)if(e.includes(t))return!0;return!1}function Pt(e={}){let t=e.psOutput??Ty(),n=e.isProcessAlive??wy,s=e.getParentCommand??Ry,r=[];for(let o of t.split(`
1339
- `)){let a=o.trim();if(!a||!yy(a)||ky(a))continue;let c=a.split(/\s+/);if(c.length<5)continue;let u=Number(c[0]),d=Number(c[1]),p=c[2],f=c[3],S=c[4],y=0,b=0;if(/^\d+$/.test(f)&&(S.includes(".")||/^\d+$/.test(S))?(y=Number(f),b=Number(S)):b=Number(f),!Number.isFinite(u)||!Number.isFinite(d))continue;let k=d>1&&n(d);r.push({pid:u,ppid:d,parentAlive:k,etimeSeconds:Ay(p),pcpu:Number.isFinite(b)?b:0,rssKb:Number.isFinite(y)?y:0,orphan:!k,parentCommand:k?s(d):null})}return r}function Ty(){try{return zu("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -axo failed: ${t}
1340
- `),""}}function wy(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function Ry(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=zu("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return n.length?n.slice(0,200):null}catch{return null}}function ky(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=[t[5]??"",t[4]??""];for(let s of n)if(s&&(s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"))return!0;return!1}function Ay(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Yu(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Yu),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Yu(e){let t=Number(e);return Number.isFinite(t)?t:0}var xy=50,Ny=60;function di(e){return e.pcpu>=xy&&e.etimeSeconds>=Ny}var Ku=4,Oy=1024*1024;function pi(e){let t=e??Pt(),n=t.length,s=t.reduce((d,p)=>d+(Number.isFinite(p.rssKb)&&p.rssKb>0?p.rssKb:0),0),r=n>Ku,o=s>Oy;if(!(r||o))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,message:null};let c=[];r&&c.push(`${n} MCP children (threshold ${Ku})`),o&&c.push(`${Cy(s)} aggregate RSS (threshold 1 GiB)`);let u=`Zombie MCP threshold breached: ${c.join(" + ")}. Each MCP child holds a SQLite read connection and can pin WAL checkpoints. Run \`recall mcp-prune --all\` to reap (note: pre-2026-05-23 builds may miss processes from stale install paths -- \`pkill -f mcp-server.js\` is the nuclear option).`;return{flagged:!0,severity:"high",count:n,aggregateRssKb:s,message:u}}function Cy(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}import{execFileSync as Ly}from"node:child_process";var vy=["dist/daemon/entrypoint.js"];function Iy(e){for(let t of vy)if(e.includes(t))return!0;return!1}function My(e){if(e.length<5)return!1;let t=[e[5]??"",e[4]??""];for(let n of t)if(n&&(n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"))return!0;return!1}function mi(e={}){let t=e.psOutput??jy(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
1341
- `)){let o=r.trim();if(!o||!Iy(o))continue;let a=o.split(/\s+/);if(My(a)||a.length<5)continue;let c=Number(a[0]),u=Number(a[1]),d=a[2];!Number.isFinite(c)||!Number.isFinite(u)||n.has(c)||s.push({pid:c,ppid:u,etimeSeconds:Dy(d),etime:d,command:o})}return s.sort((r,o)=>r.etimeSeconds-o.etimeSeconds),s}function jy(){try{return Ly("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] ps -axo failed: ${t}
1342
- `),""}}function Dy(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Vu(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Vu),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Vu(e){let t=Number(e);return Number.isFinite(t)?t:0}ne();import{existsSync as Qs,readFileSync as $L,writeFileSync as $t,unlinkSync as Wy}from"node:fs";import{basename as Zs,join as gi}from"node:path";import{randomBytes as Hy}from"node:crypto";ne();import{join as By}from"node:path";ne();import{appendFileSync as Py}from"node:fs";import{join as Fy}from"node:path";var $y=Fy(W,"daemon.log"),Uy="[state-files-audit]";function Ft(e,t){let s=(new Error().stack??"").split(`
1343
- `).slice(2,4).map(a=>a.trim().replace(/file:\/\/.*?(\bdist\b|\bsrc\b)/g,"$1")).join(" << "),o=`${new Date().toISOString()} ${Uy} ${e}: ${t} | caller: ${s}
1344
- `;try{Py($y,o)}catch{}}var Qu=By(W,"daemon.token");function Zu(){let e=Hy(32).toString("hex");return Ft("mint-token",`length=${e.length}`),e}var qy=gi(W,"daemon.pid"),Xy=gi(W,"daemon.port"),XL=gi(W,"daemon.log");function On(){return{pid:qy,port:Xy,token:Qu}}function ed(e,t={}){let n=t.paths??On();V(),$t(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),$t(n.port,String(e.port),{encoding:"utf8",mode:384}),$t(n.token,e.token,{encoding:"utf8",mode:384}),Ft("write-info-atomic",`pid=${e.pid} port=${e.port}`)}function fi(e={}){let t=e.paths??On(),n=[];for(let s of[t.pid,t.port,t.token])if(Qs(s))try{Wy(s),n.push(Zs(s))}catch{}return Ft("clear-force",`cleared: ${n.join(",")||"(none -- files already absent)"}`),{deleted:!0,reason:`cleared ${n.length} file(s)`,cleared:n}}function td(e){return Jy(e)}function Jy(e){try{return process.kill(e,0),!0}catch{return!1}}function nd(e,t={}){let n=t.paths??On(),s=[];return V(),Qs(n.pid)||($t(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),s.push(Zs(n.pid))),Qs(n.port)||($t(n.port,String(e.port),{encoding:"utf8",mode:384}),s.push(Zs(n.port))),Qs(n.token)||($t(n.token,e.token,{encoding:"utf8",mode:384}),s.push(Zs(n.token))),s.length>0&&Ft("heal",`restored: ${s.join(",")}`),{restored:s}}import{statSync as Gy}from"node:fs";function _i(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var Yy=6e4,Ky=5*6e4,sd=1073741824,zy=5368709120,Vy=6e4,Qy=100;function hi(e,t="PASSIVE",n){let s=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=eT(r),a={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-s};return n&&(t==="PASSIVE"&&a.log>=Qy&&a.moved===0?n(`[wal-maintenance] PASSIVE checkpoint blocked: log=${a.log} frames pending, moved=0 (readers holding snapshots)`):a.moved>0&&n(`[wal-maintenance] ${t} checkpoint: log=${a.log} moved=${a.moved} busy=${a.busy} (${a.durationMs}ms)`)),a}function rd(e){let t=e.db,n=e.walPath,s=e.checkpointEveryMs??Yy,r=e.sizeCheckEveryMs??Ky,o=e.warnBytes??sd,a=e.errorBytes??zy,c=e.forceRestartCooldownMs??Vy,u=e.logger??(M=>{process.stderr.write(M+`
1345
- `)}),d=e.statFn??(M=>Gy(M)),p=e.describeSuspectsFn??Zy,f=0,S=null,y=()=>{try{hi(t,"PASSIVE",u).moved>0&&(S=Date.now())}catch(M){u(`[wal-maintenance] PASSIVE checkpoint threw: ${_i(M)}`)}},b=()=>{let M;try{M=d(n).size}catch(P){let G=P.code;if(G!=="ENOENT"){let D=P instanceof Error?P.message:String(P);u(`[wal-maintenance] WAL stat failed (${G??"unknown"}): ${D}`)}return}if(M>=a){u(`[wal-maintenance] ERROR: WAL ${er(M)} exceeds error threshold ${er(a)}`);let P=p();P&&u(`[wal-maintenance] ${P}`);let G=Date.now();if(G-f>=c){f=G;try{let D=hi(t,"RESTART",u);u(`[wal-maintenance] forced RESTART: moved=${D.moved} busy=${D.busy} log=${D.log} (${D.durationMs}ms)`),D.moved>0&&(S=Date.now())}catch(D){u(`[wal-maintenance] forced RESTART threw: ${_i(D)}`)}}}else M>=o&&u(`[wal-maintenance] WARN: WAL ${er(M)} exceeds warn threshold ${er(o)}`)},R=setInterval(y,s),k=setInterval(b,r);return typeof R.unref=="function"&&R.unref(),typeof k.unref=="function"&&k.unref(),{stop:()=>{clearInterval(R),clearInterval(k)},checkpointNow:(M="PASSIVE")=>{let P=hi(t,M,u);return P.moved>0&&(S=Date.now()),P},walSizeBytes:()=>{try{return d(n).size}catch{return 0}},lastCheckpointAt:()=>S}}function Zy(e={}){let n=(e.list??Pt)();if(n.length===0)return null;let s=n.find(di);if(s)return`pin suspect: pid ${s.pid} at ${s.pcpu.toFixed(0)}%cpu, elapsed ${s.etimeSeconds}s (parent ${s.parentCommand??s.ppid})`;let r=n.slice().sort((o,a)=>a.etimeSeconds-o.etimeSeconds)[0];return`pin suspect: pid ${r.pid}, elapsed ${r.etimeSeconds}s (parent ${r.parentCommand??r.ppid})`}function eT(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:Ei(t.busy),log:Ei(t.log),checkpointed:Ei(t.checkpointed)}}function Ei(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}function er(e){return e>=1e9?`${(e/1e9).toFixed(2)} GB`:e>=1e6?`${(e/1e6).toFixed(0)} MB`:e>=1e3?`${(e/1e3).toFixed(0)} KB`:`${e} B`}ne();q();import{existsSync as tT,readFileSync as nT,renameSync as id,writeFileSync as sT}from"node:fs";import{join as rT}from"node:path";var oT=24,iT=3600*1e3,aT=1e3,cT=1e3,lT=1e4,uT=5e3,dT=1440*60*1e3,od=1e4;function ad(){return rT(W,"doctor-state.json")}function cd(){let e=ad();if(!tT(e))return{chunkQueue:{samples:[]}};let t;try{t=nT(e,"utf8")}catch{return{chunkQueue:{samples:[]}}}try{let n=JSON.parse(t),s=n.chunkQueue?.samples,r=Array.isArray(s)?s.filter(u=>typeof u=="object"&&u!==null&&typeof u.ts=="string"&&typeof u.size=="number"&&typeof u.semanticEnabled=="boolean"):[],o=n.autoPruneCounters?.events,a=Array.isArray(o)?o.filter(u=>{if(!u||typeof u!="object")return!1;let d=u;return!(typeof d.ts!="string"||typeof d.pid!="number"||!Number.isFinite(d.pid)||d.action!=="would_kill"&&d.action!=="killed"&&d.action!=="failed"||d.reason!=="orphan_10min"&&d.reason!=="runaway_cpu_5min")}):[],c={chunkQueue:{samples:r}};return(a.length>0||o!==void 0)&&(c.autoPruneCounters={events:a}),c}catch{try{id(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function ld(e){let t=ad(),n=`${t}.tmp`;try{sT(n,JSON.stringify(e,null,2),{mode:384}),id(n,t)}catch{}}function bi(){let e=h(),t=0;try{t=e.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let n=!1;try{n=e.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}let s=cd(),r=Date.now(),o=s.chunkQueue.samples,a=o.map(b=>({s:b,ms:Date.parse(b.ts)})).filter(b=>Number.isFinite(b.ms)&&r-b.ms<=iT).sort((b,R)=>b.ms-R.ms),c=null;a.length>0&&(c=t-a[0].s.size);let u=a.length,p={chunkQueue:{samples:[...o,{ts:new Date(r).toISOString(),size:t,semanticEnabled:n}].slice(-oT)}};s.autoPruneCounters&&(p.autoPruneCounters=s.autoPruneCounters),ld(p);let f="ok",S=`chunk_queue growth: ok (current ${t.toLocaleString()} row${t===1?"":"s"}, semantic ${n?"on":"off"}, ${u} prior sample${u===1?"":"s"} in last hour).`,y=null;return t>aT&&!n&&c!==null&&c>cT?(f="critical",S=`chunk_queue growth: CRITICAL \u2014 ${t.toLocaleString()} rows with semantic disabled, grew by ${c.toLocaleString()} in the last hour. Schema-sync race shape.`,y="Schema-sync race likely \u2014 Phase 1.1 fix shipped on `feat/daemon-state-integrity`; if rows persist after a clean restart, re-investigate. Inspect with `sqlite3 ~/.recall/db.sqlite 'SELECT action, COUNT(*) FROM chunk_queue GROUP BY action;'`."):t>lT?(f="high",S=`chunk_queue growth: HIGH \u2014 ${t.toLocaleString()} rows pending (semantic ${n?"on":"off"}`+(c!==null?`, last-hour \u0394 ${c>=0?"+":""}${c.toLocaleString()}`:"")+").",y="Queue is large but stable \u2014 operator-authorized `recall semantic backfill` (or the daemon's embed worker, if intentionally on) would drain."):c!==null&&c>uT&&(f="medium",S=`chunk_queue growth: MEDIUM \u2014 grew by ${c.toLocaleString()} in the last hour (current ${t.toLocaleString()}, semantic ${n?"on":"off"}).`,y="Growth is fast even though the absolute size is small. Re-run `recall doctor` in 10\u201330 min; if growth continues without semantic on, investigate the schema-sync gate."),{status:f,currentSize:t,semanticEnabled:n,lastHourGrowth:c,priorSampleCount:u,message:S,remediation:y}}function ud(e,t=Date.now()){if(e.length===0)return;let n=cd(),r=[...n.autoPruneCounters?.events??[],...e],o=pT(r,t),a={chunkQueue:n.chunkQueue,autoPruneCounters:{events:o}};ld(a)}function pT(e,t){let n=e.filter(s=>{let r=Date.parse(s.ts);return Number.isFinite(r)&&t-r<=dT});return n.length>od?n.slice(-od):n}var dd="dry-run",mT=600,gT=95,fT=300,_T=5e3;function tr(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return dd;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:dd}async function gd(e={}){let t=e.mode??tr(),n=e.log??ST,s=e.now??Date.now,r={mode:t,candidates:[],wouldHaveKilled:[],killed:[],failed:[]};if(t==="off")return r;let o=e.list??Pt,a;try{a=o()}catch(p){return n(`[auto-prune] inventory scan failed: ${Ke(p)}`),r}for(let p of a){let f=hT(p);f!==null&&r.candidates.push({pid:p.pid,reason:f,proc:p})}if(r.candidates.length===0)return r;if(t==="dry-run"){for(let p of r.candidates)n(Si("WOULD kill",p.pid,p.reason,p.proc)),r.wouldHaveKilled.push({pid:p.pid,reason:p.reason});return md(r,s()),r}let c=e.kill??ET,u=e.sleep??bT,d=[];for(let p of r.candidates)try{c(p.pid,"SIGTERM"),d.push({pid:p.pid,reason:p.reason})}catch(f){f?.code==="ESRCH"?r.killed.push({pid:p.pid,reason:p.reason}):(n(`[auto-prune] kill failed pid=${p.pid} reason=${p.reason} error=${Ke(f)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ke(f)}))}await u(_T);for(let p of d){let f=!1;try{c(p.pid,0),f=!0}catch(S){S?.code==="ESRCH"?r.killed.push({pid:p.pid,reason:p.reason}):(n(`[auto-prune] liveness probe failed pid=${p.pid} reason=${p.reason} error=${Ke(S)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ke(S)}))}if(f)try{c(p.pid,"SIGKILL"),r.killed.push({pid:p.pid,reason:p.reason}),n(Si("killed",p.pid,p.reason,pd(r.candidates,p.pid)))}catch(S){n(`[auto-prune] SIGKILL failed pid=${p.pid} reason=${p.reason} error=${Ke(S)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ke(S)})}else n(Si("killed",p.pid,p.reason,pd(r.candidates,p.pid)))}return md(r,s()),r}function hT(e){return e.orphan&&e.etimeSeconds>mT?"orphan_10min":e.pcpu>gT&&e.etimeSeconds>fT?"runaway_cpu_5min":null}function pd(e,t){return e.find(s=>s.pid===t).proc}function Si(e,t,n,s){return`[auto-prune] ${e} pid=${t} reason=${n} etime=${s.etimeSeconds}s pcpu=${s.pcpu}% rssKb=${s.rssKb} ppid=${s.ppid} parentAlive=${s.parentAlive}`}function md(e,t){let n=new Date(t).toISOString(),s=[];for(let r of e.wouldHaveKilled)s.push({ts:n,pid:r.pid,action:"would_kill",reason:r.reason});for(let r of e.killed)s.push({ts:n,pid:r.pid,action:"killed",reason:r.reason});for(let r of e.failed)s.push({ts:n,pid:r.pid,action:"failed",reason:r.reason});if(s.length!==0)try{ud(s,t)}catch(r){process.stderr.write(`[auto-prune] counter persist failed: ${Ke(r)}
1346
- `)}}function ET(e,t){process.kill(e,t)}function bT(e){return new Promise(t=>{setTimeout(t,e)})}function ST(e){process.stderr.write(e+`
1347
- `)}function Ke(e){if(e instanceof Error){let t=e.code;return t?`${t} ${e.message}`:e.message}return String(e)}ne();import{existsSync as yT,readFileSync as TT,renameSync as _d,writeFileSync as wT}from"node:fs";import{createHash as RT}from"node:crypto";import{join as kT}from"node:path";function hd(){return kT(W,"doctor-alerts.json")}function AT(e,t){let n=Object.keys(t).sort(),s={};for(let o of n)s[o]=t[o];let r=RT("sha256");return r.update(e),r.update("\0"),r.update(JSON.stringify(s)),r.digest("hex")}function yi(){let e=hd();if(!yT(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=TT(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return fd(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return fd(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter(xT):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function fd(e){try{_d(e,`${e}.corrupt.${Date.now()}`)}catch{}}function xT(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&typeof t.check=="string"&&(t.severity==="critical"||t.severity==="high"||t.severity==="medium")&&typeof t.message=="string"&&typeof t.remediation=="string"&&typeof t.firstSeenAt=="string"&&typeof t.lastSeenAt=="string"&&typeof t.seenCount=="number"&&typeof t.acknowledged=="boolean"}function Ti(e){let t=hd(),n=`${t}.tmp`;try{wT(n,JSON.stringify(e,null,2),{mode:384}),_d(n,t)}catch{}}function Ed(e,t,n=new Date){let s=n.toISOString(),r=new Map;for(let o of e.alerts)r.set(o.id,o);for(let o of t){let a=AT(o.check,o.keyFacts),c=r.get(a);c?r.set(a,{...c,severity:o.severity,message:o.message,remediation:o.remediation,lastSeenAt:s,seenCount:c.seenCount+1}):r.set(a,{id:a,check:o.check,severity:o.severity,message:o.message,remediation:o.remediation,firstSeenAt:s,lastSeenAt:s,seenCount:1,acknowledged:!1})}return{version:1,lastTickAt:s,alerts:[...r.values()]}}var LT=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],Iv=new RegExp(`^(${LT.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var Td=50;var Mv=5*6e4;function vT(e){return e==="db.sqlite"||e==="db.sqlite-wal"||e==="db.sqlite-shm"?!1:!!(e.startsWith("db.sqlite.")||e.endsWith(".bak")||e.includes(".bak."))}function wd(e=W){let t=0,n=0;try{let y=Sd(e);t=Number(y.bavail)*Number(y.bsize),n=Number(y.blocks)*Number(y.bsize)}catch{}let s=n>0?t/n*100:100,r=[];try{r=NT(e)}catch{r=[]}let o=0,a=0,c=[],u=Date.now(),d=720*60*60*1e3;for(let y of r){if(!vT(y))continue;let b;try{b=OT(yd(e,y))}catch{continue}if(!b.isFile())continue;a+=1,o+=b.size;let R=Math.max(0,u-b.mtimeMs),k=Math.floor(R/(1440*60*1e3));R>=d&&c.push({name:y,sizeBytes:b.size,ageDays:k})}c.sort((y,b)=>b.sizeBytes!==y.sizeBytes?b.sizeBytes-y.sizeBytes:b.ageDays-y.ageDays);let p=2*1024**3,f=5*1024**3,S="ok";return n>0&&s<10||o>f?S="high":n>0&&s<20||o>p?S="medium":a>0&&(S="low"),{freeBytes:t,totalBytes:n,freePercent:s,backupTotalBytes:o,backupFileCount:a,oldFiles:c,severity:S}}function Rd(e={}){if((e.liveDaemons??mi({excludePids:[process.pid]})).length===0)return{flagged:!1,severity:"ok",daemonAlive:!1,missing:[],message:null,remediation:null};let n=e.paths??On(),s=e.existsSync??wi,r=e.isProcessAlive??td,o=!1;if(s(n.pid))try{let u=JSON.parse(bd(n.pid,"utf8"));typeof u.pid=="number"&&r(u.pid)&&(o=!0)}catch{}if(!o)return{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null};let a=[];return s(n.port)||a.push("daemon.port"),s(n.token)||a.push("daemon.token"),a.length===0?{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null}:{flagged:!0,severity:"critical",daemonAlive:!0,missing:a,message:`Daemon process is alive (per pidfile) but companion state file${a.length===1?"":"s"} ${a.join(", ")} missing. Web UI and authenticated CLI calls will return 401 until the 30s heal tick restores them.`,remediation:"The 30-second heal tick re-creates these files automatically. To force immediate recovery: recall stop && recall start. Inspect ~/.recall/daemon.log for [state-files-audit] entries to identify the deletion source."}}function kd(e=yd(CT(),".claude.json")){let t={status:"ok",configPath:e,configExists:wi(e),findings:[]};if(!t.configExists)return t;let n;try{n=bd(e,"utf8")}catch{return t}let s;try{s=JSON.parse(n)}catch{return t}if(!s||typeof s!="object"||Array.isArray(s))return t;let r=s.mcpServers;if(!r||typeof r!="object"||Array.isArray(r))return t;for(let[o,a]of Object.entries(r)){if(!a||typeof a!="object")continue;let c=a.args;if(!Array.isArray(c)||c.length===0)continue;let u=c[0];if(typeof u!="string"||u.length===0||!u.startsWith("/")&&!u.startsWith("~")||wi(u))continue;let d=o==="recall";t.findings.push({name:o,stalePath:u,severity:d?"HIGH":"MEDIUM",remediation:d?"restart daemon (`recall stop && recall start`) \u2014 auto-repoint runs on boot now":"this is a third-party MCP \u2014 the vendor's package may have moved; reinstall their package"})}return t.findings.some(o=>o.severity==="HIGH")?t.status="fail":t.findings.length>0&&(t.status="warn"),t}function Ri(){try{let e=Sd(W);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function Ad(e){let{projects:t,sessions:n,messages:s,port:r,version:o}=e;return`<!DOCTYPE html>
1377
+ `),g=n.prepare("SELECT 1 FROM sessions WHERE id = ? LIMIT 1"),h=n.prepare(`
1378
+ UPDATE sessions SET
1379
+ message_count = (SELECT COUNT(*) FROM messages WHERE session_id = @id),
1380
+ user_message_count = (SELECT COUNT(*) FROM messages WHERE session_id = @id AND role='user' AND is_sidechain=0),
1381
+ assistant_message_count = (SELECT COUNT(*) FROM messages WHERE session_id = @id AND role='assistant' AND is_sidechain=0),
1382
+ ended_at = COALESCE((SELECT MAX(timestamp) FROM messages WHERE session_id = @id AND timestamp IS NOT NULL), ended_at),
1383
+ file_mtime = @file_mtime,
1384
+ indexed_at = @indexed_at
1385
+ WHERE id = @id
1386
+ `),E=new Date().toISOString(),y=new Set,R=new Set;n.transaction(()=>{let{id:x}=U.get({encoded_path:o,decoded_path:j,name:I,repo_root:J,main_repo:M,is_repo:F,is_worktree:ne,have_cwd:A?1:0});for(let k of S.values()){if(gu(k.firstUser)){console.log(`[watcher] skipping daemon-spawn phantom ${k.sessionId} (first message matches autonomous-spawn pattern)`),l.run(k.sessionId),m.run({id:k.sessionId,project_id:x,file_path:e,file_mtime:a,started_at:k.earliest,ended_at:k.latest,cwd:k.cwd,indexed_at:E}),nr(n,e,{kind:"skip-durable",reason:"daemon_spawn_phantom"}),y.add(k.sessionId);continue}if(p.mode==="incremental"&&g.get(k.sessionId)){R.add(k.sessionId);for(let q of k.entries)i.run(Ed(q));h.run({id:k.sessionId,file_mtime:a,indexed_at:E}),Go(n,k.sessionId,k.entries,{insertOnly:!0}),yn(n,k.sessionId);continue}let L=ui(k.firstUser),$=L.alias?L.stripped:k.firstUser;if(v.run({id:k.sessionId,project_id:x,file_path:e,file_mtime:a,started_at:k.earliest,ended_at:k.latest,message_count:k.entries.length,user_message_count:k.users,assistant_message_count:k.assistants,first_user_message:$,cwd:k.cwd,git_branch:k.branch,version:k.version,indexed_at:E}),L.alias&&!Se(k.sessionId))try{_e(k.sessionId,L.alias)}catch(q){console.error(`[watcher] header-alias setAlias failed for ${k.sessionId}:`,q)}l.run(k.sessionId);for(let q of k.entries)i.run(Ed(q));Go(n,k.sessionId,k.entries),yn(n,k.sessionId)}})();let N=Cs(e,T),C=n.prepare("SELECT COUNT(*) AS n FROM messages m JOIN sessions s ON s.id = m.session_id WHERE s.file_path = ?").get(e);if(Bo(n,e,{byteOffset:T,sizeBytes:Number(c.size),inode:Number(c.ino),prefixHash:N,mtime:a,lineCount:C.n}),ft().heuristicEnabled)for(let x of S.values()){if(y.has(x.sessionId)||R.has(x.sessionId))continue;let k=ui(x.firstUser).stripped,L=Dt(k);L&&he(x.sessionId,L,"heuristic")}}function Td(e){return Sd.set(e,Date.now()),Ku(e,()=>sy(e)).finally(()=>{let t=0;try{t=$t(e).size}catch{}fd(e,t)})}function Ed(e){let t=be(e.contentText).redacted,n=be(e.raw).redacted;return{uuid:e.uuid,session_id:e.sessionId,parent_uuid:e.parentUuid,type:e.type,role:e.role,timestamp:e.timestamp,is_sidechain:e.isSidechain?1:0,content_text:t,tool_names:e.toolNames.join(","),raw_json:n}}function bd(e){let t=Si.get(e);t?.timer&&clearTimeout(t.timer);let n=hd(Date.now(),Sd.get(e)),s={timer:null};s.timer=setTimeout(()=>{Si.delete(e),Td(e).then(async()=>{ty(e),Ds(e);try{let o=_().prepare("SELECT id FROM sessions WHERE file_path = ? AND skipped_reason IS NULL").all(e);for(let a of o){dl(a.id),lu(a.id);try{Wu(a.id)}catch(c){console.error("[watcher] auto-collections apply failed:",c)}}}catch(r){console.error("[watcher] semantic dispatch failed:",r)}}).catch(r=>{console.error(`[watcher] reindex failed for ${e}:`,r)})},n),Si.set(e,s)}function ry(e){if(e.endsWith(".jsonl"))return rr(e);try{if($t(e).isDirectory())return!1}catch{}return!0}function yd(){let e=QT(on,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:ry});return e.on("add",t=>t.endsWith(".jsonl")&&!rr(t)&&bd(t)),e.on("change",t=>t.endsWith(".jsonl")&&!rr(t)&&bd(t)),e.on("ready",()=>{console.log(`[watcher] ready, watching ${on}`)}),e.on("error",t=>{console.error("[watcher] chokidar error:",t)}),process.env.RECALL_WATCHER_DEBUG==="1"&&(e.on("add",t=>console.log(`[watcher.debug] add: ${t}`)),e.on("change",t=>console.log(`[watcher.debug] change: ${t}`)),e.on("unlink",t=>console.log(`[watcher.debug] unlink: ${t}`))),e}var oy=4;function*yi(e){let t;try{t=ZT(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=ey(e,n.name);n.isDirectory()?yield*yi(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}async function wi(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},n=_(),s=[],r=n.prepare("SELECT file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1");for(let d of yi(on)){t.scanned+=1;let p;try{p=$t(d).mtimeMs}catch{p=null}let f=r.get(d),b=bi({filePath:d,existing:f??null,currentMtime:p});switch(b.kind){case"skip-durable":nr(n,d,b),t.skipped+=1;continue;case"skip-already-marked":t.skipped+=1;continue;case"skip-transient":b.reason==="unchanged_mtime"?t.upToDate+=1:t.errors+=1;continue;case"process":s.push(d);continue}}if(s.length===0)return t.durationMs=Date.now()-e,t;let o=s.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await Td(d),t.reindexed+=1}catch(p){t.errors+=1;let f=p instanceof Error?p.message:String(p);try{ny(d,p)}catch(b){console.error(`[ingestion-sweep] marker write failed for ${d}:`,b)}console.error(`[ingestion-sweep] failed for ${d}: ${f}`)}}},c=Math.min(oy,s.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}async function wd(){try{let e=await wi();e.reindexed>0&&console.log(`[safety-sweep] reindexed ${e.reindexed} file(s) the watcher missed (scanned=${e.scanned}, upToDate=${e.upToDate})`)}catch(e){console.error("[safety-sweep] failed:",e)}}import{execFileSync as xd}from"node:child_process";var iy=["dist/mcp-server.js","dist/mcp/server.js"];function ay(e){for(let t of iy)if(e.includes(t))return!0;return!1}function Ut(e={}){let t=e.psOutput??cy(),n=e.isProcessAlive??ly,s=e.getParentCommand??uy,r=[];for(let o of t.split(`
1387
+ `)){let a=o.trim();if(!a||!ay(a)||dy(a))continue;let c=a.split(/\s+/);if(c.length<5)continue;let u=Number(c[0]),d=Number(c[1]),p=c[2],f=c[3],b=c[4],T=0,S=0;if(/^\d+$/.test(f)&&(b.includes(".")||/^\d+$/.test(b))?(T=Number(f),S=Number(b)):S=Number(f),!Number.isFinite(u)||!Number.isFinite(d))continue;let A=d>1&&n(d);r.push({pid:u,ppid:d,parentAlive:A,etimeSeconds:py(p),pcpu:Number.isFinite(S)?S:0,rssKb:Number.isFinite(T)?T:0,orphan:!A,parentCommand:A?s(d):null})}return r}function cy(){try{return xd("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -axo failed: ${t}
1388
+ `),""}}function ly(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function uy(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=xd("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return n.length?n.slice(0,200):null}catch{return null}}function dy(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=[t[5]??"",t[4]??""];for(let s of n)if(s&&(s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"))return!0;return!1}function py(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Rd(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Rd),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Rd(e){let t=Number(e);return Number.isFinite(t)?t:0}var my=50,gy=60;function Ri(e){return e.pcpu>=my&&e.etimeSeconds>=gy}var kd=4,fy=1024*1024;function Ad(e){return e.reduce((t,n)=>t+(Number.isFinite(n.rssKb)&&n.rssKb>0?n.rssKb:0),0)}function ki(e){let t=e??Ut(),n=t.length,s=Ad(t),r=t.filter(b=>b.orphan),o=r.length,a=Ad(r),c=o>kd,u=a>fy;if(!(c||u))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,orphanCount:o,orphanRssKb:a,message:null};let p=[];c&&p.push(`${o} orphaned MCP children (threshold ${kd})`),u&&p.push(`${_y(a)} aggregate RSS across orphaned children (threshold 1 GiB)`);let f=`Zombie MCP threshold breached: ${p.join(" + ")}. Each orphaned MCP child (its parent claude/VS Code tab has exited) still holds a SQLite read connection and can pin WAL checkpoints. Run \`recall mcp-prune --all\` to reap (note: pre-2026-05-23 builds may miss processes from stale install paths -- \`pkill -f mcp-server.js\` is the nuclear option).`;return{flagged:!0,severity:"high",count:n,aggregateRssKb:s,orphanCount:o,orphanRssKb:a,message:f}}function _y(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}import{execFileSync as hy}from"node:child_process";var Ey=["dist/daemon/entrypoint.js"];function by(e){for(let t of Ey)if(e.includes(t))return!0;return!1}function Sy(e){if(e.length<5)return!1;let t=[e[5]??"",e[4]??""];for(let n of t)if(n&&(n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"))return!0;return!1}function Ai(e={}){let t=e.psOutput??Ty(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
1389
+ `)){let o=r.trim();if(!o||!by(o))continue;let a=o.split(/\s+/);if(Sy(a)||a.length<5)continue;let c=Number(a[0]),u=Number(a[1]),d=a[2];!Number.isFinite(c)||!Number.isFinite(u)||n.has(c)||s.push({pid:c,ppid:u,etimeSeconds:yy(d),etime:d,command:o})}return s.sort((r,o)=>r.etimeSeconds-o.etimeSeconds),s}function Ty(){try{return hy("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] ps -axo failed: ${t}
1390
+ `),""}}function yy(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Nd(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Nd),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Nd(e){let t=Number(e);return Number.isFinite(t)?t:0}Z();import{existsSync as or,readFileSync as Wv,writeFileSync as Bt,unlinkSync as Oy}from"node:fs";import{basename as ir,join as xi}from"node:path";import{randomBytes as xy}from"node:crypto";Z();import{join as Ny}from"node:path";Z();import{appendFileSync as wy}from"node:fs";import{join as Ry}from"node:path";var ky=Ry(W,"daemon.log"),Ay="[state-files-audit]";function Ht(e,t){let s=(new Error().stack??"").split(`
1391
+ `).slice(2,4).map(a=>a.trim().replace(/file:\/\/.*?(\bdist\b|\bsrc\b)/g,"$1")).join(" << "),o=`${new Date().toISOString()} ${Ay} ${e}: ${t} | caller: ${s}
1392
+ `;try{wy(ky,o)}catch{}}var Od=Ny(W,"daemon.token");function Ld(){let e=xy(32).toString("hex");return Ht("mint-token",`length=${e.length}`),e}var Ly=xi(W,"daemon.pid"),Cy=xi(W,"daemon.port"),zv=xi(W,"daemon.log");function In(){return{pid:Ly,port:Cy,token:Od}}function Cd(e,t={}){let n=t.paths??In();K(),Bt(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),Bt(n.port,String(e.port),{encoding:"utf8",mode:384}),Bt(n.token,e.token,{encoding:"utf8",mode:384}),Ht("write-info-atomic",`pid=${e.pid} port=${e.port}`)}function Ni(e={}){let t=e.paths??In(),n=[];for(let s of[t.pid,t.port,t.token])if(or(s))try{Oy(s),n.push(ir(s))}catch{}return Ht("clear-force",`cleared: ${n.join(",")||"(none -- files already absent)"}`),{deleted:!0,reason:`cleared ${n.length} file(s)`,cleared:n}}function vd(e){return vy(e)}function vy(e){try{return process.kill(e,0),!0}catch{return!1}}function Id(e,t={}){let n=t.paths??In(),s=[];return K(),or(n.pid)||(Bt(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),s.push(ir(n.pid))),or(n.port)||(Bt(n.port,String(e.port),{encoding:"utf8",mode:384}),s.push(ir(n.port))),or(n.token)||(Bt(n.token,e.token,{encoding:"utf8",mode:384}),s.push(ir(n.token))),s.length>0&&Ht("heal",`restored: ${s.join(",")}`),{restored:s}}import{statSync as Iy}from"node:fs";function Oi(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var My=6e4,Dy=5*6e4,Md=1073741824,jy=5368709120,Py=6e4,Fy=100;function Li(e,t="PASSIVE",n){let s=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=Uy(r),a={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-s};return n&&(t==="PASSIVE"&&a.log>=Fy&&a.moved===0?n(`[wal-maintenance] PASSIVE checkpoint blocked: log=${a.log} frames pending, moved=0 (readers holding snapshots)`):a.moved>0&&n(`[wal-maintenance] ${t} checkpoint: log=${a.log} moved=${a.moved} busy=${a.busy} (${a.durationMs}ms)`)),a}function Dd(e){let t=e.db,n=e.walPath,s=e.checkpointEveryMs??My,r=e.sizeCheckEveryMs??Dy,o=e.warnBytes??Md,a=e.errorBytes??jy,c=e.forceRestartCooldownMs??Py,u=e.logger??(I=>{process.stderr.write(I+`
1393
+ `)}),d=e.statFn??(I=>Iy(I)),p=e.describeSuspectsFn??$y,f=0,b=null,T=()=>{try{Li(t,"PASSIVE",u).moved>0&&(b=Date.now())}catch(I){u(`[wal-maintenance] PASSIVE checkpoint threw: ${Oi(I)}`)}},S=()=>{let I;try{I=d(n).size}catch(j){let J=j.code;if(J!=="ENOENT"){let M=j instanceof Error?j.message:String(j);u(`[wal-maintenance] WAL stat failed (${J??"unknown"}): ${M}`)}return}if(I>=a){u(`[wal-maintenance] ERROR: WAL ${ar(I)} exceeds error threshold ${ar(a)}`);let j=p();j&&u(`[wal-maintenance] ${j}`);let J=Date.now();if(J-f>=c){f=J;try{let M=Li(t,"RESTART",u);u(`[wal-maintenance] forced RESTART: moved=${M.moved} busy=${M.busy} log=${M.log} (${M.durationMs}ms)`),M.moved>0&&(b=Date.now())}catch(M){u(`[wal-maintenance] forced RESTART threw: ${Oi(M)}`)}}}else I>=o&&u(`[wal-maintenance] WARN: WAL ${ar(I)} exceeds warn threshold ${ar(o)}`)},w=setInterval(T,s),A=setInterval(S,r);return typeof w.unref=="function"&&w.unref(),typeof A.unref=="function"&&A.unref(),{stop:()=>{clearInterval(w),clearInterval(A)},checkpointNow:(I="PASSIVE")=>{let j=Li(t,I,u);return j.moved>0&&(b=Date.now()),j},walSizeBytes:()=>{try{return d(n).size}catch{return 0}},lastCheckpointAt:()=>b}}function $y(e={}){let n=(e.list??Ut)();if(n.length===0)return null;let s=n.find(Ri);if(s)return`pin suspect: pid ${s.pid} at ${s.pcpu.toFixed(0)}%cpu, elapsed ${s.etimeSeconds}s (parent ${s.parentCommand??s.ppid})`;let r=n.slice().sort((o,a)=>a.etimeSeconds-o.etimeSeconds)[0];return`pin suspect: pid ${r.pid}, elapsed ${r.etimeSeconds}s (parent ${r.parentCommand??r.ppid})`}function Uy(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:Ci(t.busy),log:Ci(t.log),checkpointed:Ci(t.checkpointed)}}function Ci(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}function ar(e){return e>=1e9?`${(e/1e9).toFixed(2)} GB`:e>=1e6?`${(e/1e6).toFixed(0)} MB`:e>=1e3?`${(e/1e3).toFixed(0)} KB`:`${e} B`}Z();H();import{existsSync as Hy,readFileSync as By,renameSync as Pd,writeFileSync as Wy}from"node:fs";import{join as qy}from"node:path";var Xy=24,Jy=3600*1e3,Gy=1e3,Yy=1e3,zy=1e4,Ky=5e3,Vy=1440*60*1e3,jd=1e4;function Fd(){return qy(W,"doctor-state.json")}function $d(){let e=Fd();if(!Hy(e))return{chunkQueue:{samples:[]}};let t;try{t=By(e,"utf8")}catch{return{chunkQueue:{samples:[]}}}try{let n=JSON.parse(t),s=n.chunkQueue?.samples,r=Array.isArray(s)?s.filter(u=>typeof u=="object"&&u!==null&&typeof u.ts=="string"&&typeof u.size=="number"&&typeof u.semanticEnabled=="boolean"):[],o=n.autoPruneCounters?.events,a=Array.isArray(o)?o.filter(u=>{if(!u||typeof u!="object")return!1;let d=u;return!(typeof d.ts!="string"||typeof d.pid!="number"||!Number.isFinite(d.pid)||d.action!=="would_kill"&&d.action!=="killed"&&d.action!=="failed"||d.reason!=="orphan_10min"&&d.reason!=="runaway_cpu_5min")}):[],c={chunkQueue:{samples:r}};return(a.length>0||o!==void 0)&&(c.autoPruneCounters={events:a}),c}catch{try{Pd(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function Ud(e){let t=Fd(),n=`${t}.tmp`;try{Wy(n,JSON.stringify(e,null,2),{mode:384}),Pd(n,t)}catch{}}function vi(){let e=_(),t=0;try{t=e.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let n=!1;try{n=e.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}let s=$d(),r=Date.now(),o=s.chunkQueue.samples,a=o.map(S=>({s:S,ms:Date.parse(S.ts)})).filter(S=>Number.isFinite(S.ms)&&r-S.ms<=Jy).sort((S,w)=>S.ms-w.ms),c=null;a.length>0&&(c=t-a[0].s.size);let u=a.length,p={chunkQueue:{samples:[...o,{ts:new Date(r).toISOString(),size:t,semanticEnabled:n}].slice(-Xy)}};s.autoPruneCounters&&(p.autoPruneCounters=s.autoPruneCounters),Ud(p);let f="ok",b=`chunk_queue growth: ok (current ${t.toLocaleString()} row${t===1?"":"s"}, semantic ${n?"on":"off"}, ${u} prior sample${u===1?"":"s"} in last hour).`,T=null;return t>Gy&&!n&&c!==null&&c>Yy?(f="critical",b=`chunk_queue growth: CRITICAL \u2014 ${t.toLocaleString()} rows with semantic disabled, grew by ${c.toLocaleString()} in the last hour. Schema-sync race shape.`,T="Schema-sync race likely \u2014 Phase 1.1 fix shipped on `feat/daemon-state-integrity`; if rows persist after a clean restart, re-investigate. Inspect with `sqlite3 ~/.recall/db.sqlite 'SELECT action, COUNT(*) FROM chunk_queue GROUP BY action;'`."):t>zy?(f="high",b=`chunk_queue growth: HIGH \u2014 ${t.toLocaleString()} rows pending (semantic ${n?"on":"off"}`+(c!==null?`, last-hour \u0394 ${c>=0?"+":""}${c.toLocaleString()}`:"")+").",T="Queue is large but stable \u2014 operator-authorized `recall semantic backfill` (or the daemon's embed worker, if intentionally on) would drain."):c!==null&&c>Ky&&(f="medium",b=`chunk_queue growth: MEDIUM \u2014 grew by ${c.toLocaleString()} in the last hour (current ${t.toLocaleString()}, semantic ${n?"on":"off"}).`,T="Growth is fast even though the absolute size is small. Re-run `recall doctor` in 10\u201330 min; if growth continues without semantic on, investigate the schema-sync gate."),{status:f,currentSize:t,semanticEnabled:n,lastHourGrowth:c,priorSampleCount:u,message:b,remediation:T}}function Hd(e,t=Date.now()){if(e.length===0)return;let n=$d(),r=[...n.autoPruneCounters?.events??[],...e],o=Qy(r,t),a={chunkQueue:n.chunkQueue,autoPruneCounters:{events:o}};Ud(a)}function Qy(e,t){let n=e.filter(s=>{let r=Date.parse(s.ts);return Number.isFinite(r)&&t-r<=Vy});return n.length>jd?n.slice(-jd):n}var Bd="dry-run",Zy=600,ew=95,tw=300,nw=5e3;function cr(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return Bd;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:Bd}async function Xd(e={}){let t=e.mode??cr(),n=e.log??iw,s=e.now??Date.now,r={mode:t,candidates:[],wouldHaveKilled:[],killed:[],failed:[]};if(t==="off")return r;let o=e.list??Ut,a;try{a=o()}catch(p){return n(`[auto-prune] inventory scan failed: ${Ze(p)}`),r}for(let p of a){let f=sw(p);f!==null&&r.candidates.push({pid:p.pid,reason:f,proc:p})}if(r.candidates.length===0)return r;if(t==="dry-run"){for(let p of r.candidates)n(Ii("WOULD kill",p.pid,p.reason,p.proc)),r.wouldHaveKilled.push({pid:p.pid,reason:p.reason});return qd(r,s()),r}let c=e.kill??rw,u=e.sleep??ow,d=[];for(let p of r.candidates)try{c(p.pid,"SIGTERM"),d.push({pid:p.pid,reason:p.reason})}catch(f){f?.code==="ESRCH"?r.killed.push({pid:p.pid,reason:p.reason}):(n(`[auto-prune] kill failed pid=${p.pid} reason=${p.reason} error=${Ze(f)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ze(f)}))}await u(nw);for(let p of d){let f=!1;try{c(p.pid,0),f=!0}catch(b){b?.code==="ESRCH"?r.killed.push({pid:p.pid,reason:p.reason}):(n(`[auto-prune] liveness probe failed pid=${p.pid} reason=${p.reason} error=${Ze(b)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ze(b)}))}if(f)try{c(p.pid,"SIGKILL"),r.killed.push({pid:p.pid,reason:p.reason}),n(Ii("killed",p.pid,p.reason,Wd(r.candidates,p.pid)))}catch(b){n(`[auto-prune] SIGKILL failed pid=${p.pid} reason=${p.reason} error=${Ze(b)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ze(b)})}else n(Ii("killed",p.pid,p.reason,Wd(r.candidates,p.pid)))}return qd(r,s()),r}function sw(e){return e.orphan&&e.etimeSeconds>Zy?"orphan_10min":e.pcpu>ew&&e.etimeSeconds>tw?"runaway_cpu_5min":null}function Wd(e,t){return e.find(s=>s.pid===t).proc}function Ii(e,t,n,s){return`[auto-prune] ${e} pid=${t} reason=${n} etime=${s.etimeSeconds}s pcpu=${s.pcpu}% rssKb=${s.rssKb} ppid=${s.ppid} parentAlive=${s.parentAlive}`}function qd(e,t){let n=new Date(t).toISOString(),s=[];for(let r of e.wouldHaveKilled)s.push({ts:n,pid:r.pid,action:"would_kill",reason:r.reason});for(let r of e.killed)s.push({ts:n,pid:r.pid,action:"killed",reason:r.reason});for(let r of e.failed)s.push({ts:n,pid:r.pid,action:"failed",reason:r.reason});if(s.length!==0)try{Hd(s,t)}catch(r){process.stderr.write(`[auto-prune] counter persist failed: ${Ze(r)}
1394
+ `)}}function rw(e,t){process.kill(e,t)}function ow(e){return new Promise(t=>{setTimeout(t,e)})}function iw(e){process.stderr.write(e+`
1395
+ `)}function Ze(e){if(e instanceof Error){let t=e.code;return t?`${t} ${e.message}`:e.message}return String(e)}Z();import{existsSync as aw,readFileSync as cw,renameSync as Gd,writeFileSync as lw}from"node:fs";import{createHash as uw}from"node:crypto";import{join as dw}from"node:path";function Yd(){return dw(W,"doctor-alerts.json")}function pw(e,t){let n=Object.keys(t).sort(),s={};for(let o of n)s[o]=t[o];let r=uw("sha256");return r.update(e),r.update("\0"),r.update(JSON.stringify(s)),r.digest("hex")}function Mi(){let e=Yd();if(!aw(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=cw(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return Jd(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return Jd(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter(mw):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function Jd(e){try{Gd(e,`${e}.corrupt.${Date.now()}`)}catch{}}function mw(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&typeof t.check=="string"&&(t.severity==="critical"||t.severity==="high"||t.severity==="medium")&&typeof t.message=="string"&&typeof t.remediation=="string"&&typeof t.firstSeenAt=="string"&&typeof t.lastSeenAt=="string"&&typeof t.seenCount=="number"&&typeof t.acknowledged=="boolean"}function Di(e){let t=Yd(),n=`${t}.tmp`;try{lw(n,JSON.stringify(e,null,2),{mode:384}),Gd(n,t)}catch{}}function zd(e,t,n=new Date){let s=n.toISOString(),r=new Map;for(let o of e.alerts)r.set(o.id,o);for(let o of t){let a=pw(o.check,o.keyFacts),c=r.get(a);c?r.set(a,{...c,severity:o.severity,message:o.message,remediation:o.remediation,lastSeenAt:s,seenCount:c.seenCount+1}):r.set(a,{id:a,check:o.check,severity:o.severity,message:o.message,remediation:o.remediation,firstSeenAt:s,lastSeenAt:s,seenCount:1,acknowledged:!1})}return{version:1,lastTickAt:s,alerts:[...r.values()]}}var hw=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],FI=new RegExp(`^(${hw.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var Zd=50;var $I=5*6e4;function Ew(e){return e==="db.sqlite"||e==="db.sqlite-wal"||e==="db.sqlite-shm"?!1:!!(e.startsWith("db.sqlite.")||e.endsWith(".bak")||e.includes(".bak."))}function ep(e=W){let t=0,n=0;try{let T=Vd(e);t=Number(T.bavail)*Number(T.bsize),n=Number(T.blocks)*Number(T.bsize)}catch{}let s=n>0?t/n*100:100,r=[];try{r=gw(e)}catch{r=[]}let o=0,a=0,c=[],u=Date.now(),d=720*60*60*1e3;for(let T of r){if(!Ew(T))continue;let S;try{S=fw(Qd(e,T))}catch{continue}if(!S.isFile())continue;a+=1,o+=S.size;let w=Math.max(0,u-S.mtimeMs),A=Math.floor(w/(1440*60*1e3));w>=d&&c.push({name:T,sizeBytes:S.size,ageDays:A})}c.sort((T,S)=>S.sizeBytes!==T.sizeBytes?S.sizeBytes-T.sizeBytes:S.ageDays-T.ageDays);let p=2*1024**3,f=5*1024**3,b="ok";return n>0&&s<10||o>f?b="high":n>0&&s<20||o>p?b="medium":a>0&&(b="low"),{freeBytes:t,totalBytes:n,freePercent:s,backupTotalBytes:o,backupFileCount:a,oldFiles:c,severity:b}}function tp(e={}){if((e.liveDaemons??Ai({excludePids:[process.pid]})).length===0)return{flagged:!1,severity:"ok",daemonAlive:!1,missing:[],message:null,remediation:null};let n=e.paths??In(),s=e.existsSync??ji,r=e.isProcessAlive??vd,o=!1;if(s(n.pid))try{let u=JSON.parse(Kd(n.pid,"utf8"));typeof u.pid=="number"&&r(u.pid)&&(o=!0)}catch{}if(!o)return{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null};let a=[];return s(n.port)||a.push("daemon.port"),s(n.token)||a.push("daemon.token"),a.length===0?{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null}:{flagged:!0,severity:"critical",daemonAlive:!0,missing:a,message:`Daemon process is alive (per pidfile) but companion state file${a.length===1?"":"s"} ${a.join(", ")} missing. Web UI and authenticated CLI calls will return 401 until the 30s heal tick restores them.`,remediation:"The 30-second heal tick re-creates these files automatically. To force immediate recovery: recall stop && recall start. Inspect ~/.recall/daemon.log for [state-files-audit] entries to identify the deletion source."}}function np(e=Qd(_w(),".claude.json")){let t={status:"ok",configPath:e,configExists:ji(e),findings:[]};if(!t.configExists)return t;let n;try{n=Kd(e,"utf8")}catch{return t}let s;try{s=JSON.parse(n)}catch{return t}if(!s||typeof s!="object"||Array.isArray(s))return t;let r=s.mcpServers;if(!r||typeof r!="object"||Array.isArray(r))return t;for(let[o,a]of Object.entries(r)){if(!a||typeof a!="object")continue;let c=a.args;if(!Array.isArray(c)||c.length===0)continue;let u=c[0];if(typeof u!="string"||u.length===0||!u.startsWith("/")&&!u.startsWith("~")||ji(u))continue;let d=o==="recall";t.findings.push({name:o,stalePath:u,severity:d?"HIGH":"MEDIUM",remediation:d?"restart daemon (`recall stop && recall start`) \u2014 auto-repoint runs on boot now":"this is a third-party MCP \u2014 the vendor's package may have moved; reinstall their package"})}return t.findings.some(o=>o.severity==="HIGH")?t.status="fail":t.findings.length>0&&(t.status="warn"),t}function Pi(){try{let e=Vd(W);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function sp(e){let{projects:t,sessions:n,messages:s,port:r,version:o}=e;return`<!DOCTYPE html>
1348
1396
  <html lang="en">
1349
1397
  <head>
1350
1398
  <meta charset="utf-8" />
@@ -1399,32 +1447,32 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1399
1447
  </footer>
1400
1448
  </main>
1401
1449
  </body>
1402
- </html>`}var IT=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,MT=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,jT=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function DT(e){return e.replace(IT,"").trim()}function PT(e){let t=e.replace(MT,"[tool call]");return t=t.replace(jT,"[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,`
1450
+ </html>`}var bw=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,Sw=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,Tw=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function yw(e){return e.replace(bw,"").trim()}function ww(e){let t=e.replace(Sw,"[tool call]");return t=t.replace(Tw,"[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,`
1403
1451
 
1404
- `),t.trim()}function FT(e){return e.role??e.type??"message"}function xd(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,o=n.since?Date.parse(n.since):0,a=t.filter(p=>!(!r&&p.is_sidechain===1||o&&p.timestamp&&Date.parse(p.timestamp)<o)),c=[];n.prelude&&(c.push(n.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${s})`),c.push(""),c.push(`- **Project**: ${e.project_name}`),e.decoded_path&&c.push(`- **Path**: \`${e.decoded_path}\``),c.push(`- **Session ID**: \`${e.id}\``),e.started_at&&c.push(`- **Started**: ${e.started_at}`),e.ended_at&&c.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&c.push(`- **Branch**: \`${e.git_branch}\``),c.push(`- **Messages**: ${a.length}`),c.push(""),c.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),c.push(""),c.push("---"),c.push("");let u=0,d=0;for(let p of a){let f=p.content_text??"",S=DT(f);s==="condensed"&&(S=PT(S));let y=S.length>0,b=!!p.tool_names&&p.tool_names.length>0;if(!y&&!b){d+=1;continue}let R=FT(p),k=p.timestamp?` \`${p.timestamp}\``:"";c.push(`## ${R}${k}`),c.push(""),b&&s==="condensed"&&(c.push(`_tools used: ${p.tool_names}_`),c.push("")),y&&(c.push(S),c.push("")),u+=1}return c.push("---"),c.push(""),c.push(`_${u} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),c.join(`
1405
- `)}function Cn(e){if(!e.sessionStartedAt)return{allowed:!1,reason:"missing-session-started-at"};if(!e.terminalOpenedAt)return{allowed:!1,reason:"missing-terminal-opened-at"};let t=Date.parse(e.sessionStartedAt),n=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(n)?n-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}q();ne();import{writeFileSync as $T,mkdirSync as UT,existsSync as HT}from"node:fs";import{join as Od}from"node:path";var ki=Od(W,"notes"),Nd=200,BT=12e3,WT=800,qT=8e3;function Cd(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Ld(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&typeof n.synopsis=="string"&&typeof n.replaced_at=="string"):[]}catch{return[]}}function XT(){V(),HT(ki)||UT(ki,{recursive:!0})}function JT(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:Cd(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:Ld(e.auto_synopsis_history)}}var GT="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function nr(e){let t=h().prepare(`SELECT ${GT} FROM session_notes WHERE session_id = ?`).get(e);return t?JT(t):null}function vd(e,t){let n=h(),s=new Date().toISOString(),r=n.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=Cd(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:s})),n.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1452
+ `),t.trim()}function Rw(e){return e.role??e.type??"message"}function rp(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,o=n.since?Date.parse(n.since):0,a=t.filter(p=>!(!r&&p.is_sidechain===1||o&&p.timestamp&&Date.parse(p.timestamp)<o)),c=[];n.prelude&&(c.push(n.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${s})`),c.push(""),c.push(`- **Project**: ${e.project_name}`),e.decoded_path&&c.push(`- **Path**: \`${e.decoded_path}\``),c.push(`- **Session ID**: \`${e.id}\``),e.started_at&&c.push(`- **Started**: ${e.started_at}`),e.ended_at&&c.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&c.push(`- **Branch**: \`${e.git_branch}\``),c.push(`- **Messages**: ${a.length}`),c.push(""),c.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),c.push(""),c.push("---"),c.push("");let u=0,d=0;for(let p of a){let f=p.content_text??"",b=yw(f);s==="condensed"&&(b=ww(b));let T=b.length>0,S=!!p.tool_names&&p.tool_names.length>0;if(!T&&!S){d+=1;continue}let w=Rw(p),A=p.timestamp?` \`${p.timestamp}\``:"";c.push(`## ${w}${A}`),c.push(""),S&&s==="condensed"&&(c.push(`_tools used: ${p.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(`
1453
+ `)}function Mn(e){if(!e.sessionStartedAt)return{allowed:!1,reason:"missing-session-started-at"};if(!e.terminalOpenedAt)return{allowed:!1,reason:"missing-terminal-opened-at"};let t=Date.parse(e.sessionStartedAt),n=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(n)?n-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}H();Z();import{writeFileSync as kw,mkdirSync as Aw,existsSync as xw}from"node:fs";import{join as ip}from"node:path";var Fi=ip(W,"notes"),op=200,Nw=12e3,Ow=800,Lw=8e3;function ap(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function cp(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&typeof n.synopsis=="string"&&typeof n.replaced_at=="string"):[]}catch{return[]}}function Cw(){K(),xw(Fi)||Aw(Fi,{recursive:!0})}function vw(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:ap(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:cp(e.auto_synopsis_history)}}var Iw="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function lr(e){let t=_().prepare(`SELECT ${Iw} FROM session_notes WHERE session_id = ?`).get(e);return t?vw(t):null}function lp(e,t){let n=_(),s=new Date().toISOString(),r=n.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=ap(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:s})),n.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1406
1454
  VALUES (?, ?, ?, ?)
1407
1455
  ON CONFLICT(session_id) DO UPDATE SET
1408
1456
  content = excluded.content,
1409
1457
  updated_at = excluded.updated_at,
1410
- previous_versions = excluded.previous_versions`).run(e,t,s,JSON.stringify(o)),KT(e,t,s),nr(e)??{session_id:e,content:t,updated_at:s,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function Id(e){let n=h().prepare(`SELECT rowid AS rid, role, content_text
1458
+ previous_versions = excluded.previous_versions`).run(e,t,s,JSON.stringify(o)),Dw(e,t,s),lr(e)??{session_id:e,content:t,updated_at:s,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function up(e){let n=_().prepare(`SELECT rowid AS rid, role, content_text
1411
1459
  FROM messages
1412
1460
  WHERE session_id = ? AND is_sidechain = 0
1413
1461
  AND content_text IS NOT NULL AND content_text != ''
1414
1462
  AND role IN ('user', 'assistant')
1415
1463
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1416
- LIMIT ?`).all(e,Nd);if(n.length===0)throw new Error("no messages available to summarise");let s=BT,r=[];for(let S of n){if(s<=0)break;let y=(S.content_text??"").slice(0,WT);r.push({rid:S.rid,role:S.role,content:y}),s-=y.length}r.reverse();let o=n.length===Nd||s<=0,a=r.map(S=>`**${S.role}**: ${S.content}`).join(`
1464
+ LIMIT ?`).all(e,op);if(n.length===0)throw new Error("no messages available to summarise");let s=Nw,r=[];for(let b of n){if(s<=0)break;let T=(b.content_text??"").slice(0,Ow);r.push({rid:b.rid,role:b.role,content:T}),s-=T.length}r.reverse();let o=n.length===op||s<=0,a=r.map(b=>`**${b.role}**: ${b.content}`).join(`
1417
1465
 
1418
1466
  `),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(`
1419
- `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(Te(),ct));if(!d())throw new Error("claude CLI not found on PATH");let p=await u(c,[],{});if(!p.success)throw new Error(`claude CLI exited ${p.exitCode}: ${p.stderr.slice(-500)}`);let f=YT(p.stdout);if(!f)throw new Error("claude CLI returned an empty synopsis");return f.slice(0,qT)}function YT(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return s.trim()}}catch{}return t}function Md(e,t){let n=h(),s=new Date().toISOString(),r=Date.now(),o=n.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=Ld(o?.auto_synopsis_history??null);return o?.auto_synopsis&&o.auto_synopsis!==t&&o.auto_synopsis.length>0&&a.push({synopsis:o.auto_synopsis,replaced_at:s}),o?n.prepare(`UPDATE session_notes
1467
+ `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(we(),pt));if(!d())throw new Error("claude CLI not found on PATH");let p=await u(c,[],{});if(!p.success)throw new Error(`claude CLI exited ${p.exitCode}: ${p.stderr.slice(-500)}`);let f=Mw(p.stdout);if(!f)throw new Error("claude CLI returned an empty synopsis");return f.slice(0,Lw)}function Mw(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return s.trim()}}catch{}return t}function dp(e,t){let n=_(),s=new Date().toISOString(),r=Date.now(),o=n.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=cp(o?.auto_synopsis_history??null);return o?.auto_synopsis&&o.auto_synopsis!==t&&o.auto_synopsis.length>0&&a.push({synopsis:o.auto_synopsis,replaced_at:s}),o?n.prepare(`UPDATE session_notes
1420
1468
  SET auto_synopsis = ?,
1421
1469
  auto_synopsis_generated_at = ?,
1422
1470
  auto_synopsis_history = ?
1423
1471
  WHERE session_id = ?`).run(t,r,JSON.stringify(a),e):n.prepare(`INSERT INTO session_notes
1424
1472
  (session_id, content, updated_at, previous_versions, auto_synopsis,
1425
1473
  auto_synopsis_generated_at, auto_synopsis_history)
1426
- VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,s,t,r,JSON.stringify(a)),nr(e)}function KT(e,t,n){try{XT();let s=Od(ki,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${n} -->
1427
- `;$T(s,r+t)}catch(s){console.error("[notes] mirror write failed:",s)}}wt();q();ne();import{randomUUID as jd}from"node:crypto";import{writeFileSync as Dd,readFileSync as Jv,existsSync as zT,mkdirSync as VT}from"node:fs";import{join as Ai}from"node:path";var sr=Ai(W,"threads"),QT=Ai(sr,"index.json");function Pd(){V(),zT(sr)||VT(sr,{recursive:!0})}function xi(e,t,n){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:n?.project??null,project_count:n?.project_count??0,folder_id:e.folder_id??null}}function Fd(e){let t=new Map;if(e.length===0)return t;let n=h(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
1474
+ VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,s,t,r,JSON.stringify(a)),lr(e)}function Dw(e,t,n){try{Cw();let s=ip(Fi,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${n} -->
1475
+ `;kw(s,r+t)}catch(s){console.error("[notes] mirror write failed:",s)}}kt();H();Z();import{randomUUID as pp}from"node:crypto";import{writeFileSync as mp,readFileSync as VI,existsSync as jw,mkdirSync as Pw}from"node:fs";import{join as $i}from"node:path";var ur=$i(W,"threads"),Fw=$i(ur,"index.json");function gp(){K(),jw(ur)||Pw(ur,{recursive:!0})}function Ui(e,t,n){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:n?.project??null,project_count:n?.project_count??0,folder_id:e.folder_id??null}}function fp(e){let t=new Map;if(e.length===0)return t;let n=_(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
1428
1476
  p.name AS project,
1429
1477
  COUNT(*) AS n,
1430
1478
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
@@ -1432,7 +1480,7 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1432
1480
  LEFT JOIN sessions s ON s.id = te.session_id
1433
1481
  LEFT JOIN projects p ON p.id = s.project_id
1434
1482
  WHERE te.thread_id IN (${s})
1435
- GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let a of r){let c=o.get(a.thread_id);c||(c=[],o.set(a.thread_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(f=>f.project!==null),d=u.length,p=null;u.length>0&&(p=[...u].sort((S,y)=>y.n-S.n||y.origin_n-S.origin_n||(S.project??"").localeCompare(y.project??""))[0].project),t.set(a,{project:p,project_count:d})}return t}function $d(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 Ud(e){let n=h().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1483
+ GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let a of r){let c=o.get(a.thread_id);c||(c=[],o.set(a.thread_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(f=>f.project!==null),d=u.length,p=null;u.length>0&&(p=[...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:p,project_count:d})}return t}function _p(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 hp(e){let n=_().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1436
1484
  s.auto_title AS auto_title,
1437
1485
  s.auto_title_source AS auto_title_source,
1438
1486
  s.first_user_message AS first_user_message,
@@ -1440,11 +1488,11 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1440
1488
  FROM (SELECT ? AS sid) q
1441
1489
  LEFT JOIN sessions s ON s.id = q.sid
1442
1490
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
1443
- LEFT JOIN projects p ON p.id = s.project_id`).get(e),s=n?.auto_title_source??null;return{alias:n?.alias??null,auto_title:n?.auto_title??null,auto_title_source:s==="agent"||s==="heuristic"?s:null,first_user_message:n?.first_user_message??null,project:n?.project??null}}function Ni(e){let n=h().prepare(`SELECT
1491
+ LEFT JOIN projects p ON p.id = s.project_id`).get(e),s=n?.auto_title_source??null;return{alias:n?.alias??null,auto_title:n?.auto_title??null,auto_title_source:s==="agent"||s==="heuristic"?s:null,first_user_message:n?.first_user_message??null,project:n?.project??null}}function Hi(e){let n=_().prepare(`SELECT
1444
1492
  COUNT(*) AS session_count,
1445
1493
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
1446
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function ke(e){let t=ce(e);t&&(Pd(),Dd(Ai(sr,`${e}.json`),JSON.stringify(t,null,2)),Hd())}function Hd(){Pd();let e=Oi({includeArchived:!0});Dd(QT,JSON.stringify({threads:e},null,2))}function rr(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=h(),s=jd(),r=new Date().toISOString();n.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(s,t,e.summary?.trim()||null,r),e.originSessionId&&n.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1447
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),ke(s);let o=ce(s);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Oi(e={}){let t=h(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=Fd(s.map(o=>o.id));return s.map(o=>xi(o,Ni(o.id),r.get(o.id)))}function ce(e){let t=h(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
1494
+ FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function Ae(e){let t=ce(e);t&&(gp(),mp($i(ur,`${e}.json`),JSON.stringify(t,null,2)),Ep())}function Ep(){gp();let e=Bi({includeArchived:!0});mp(Fw,JSON.stringify({threads:e},null,2))}function dr(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=_(),s=pp(),r=new Date().toISOString();n.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(s,t,e.summary?.trim()||null,r),e.originSessionId&&n.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1495
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),Ae(s);let o=ce(s);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Bi(e={}){let t=_(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=fp(s.map(o=>o.id));return s.map(o=>Ui(o,Hi(o.id),r.get(o.id)))}function ce(e){let t=_(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
1448
1496
  NULLIF(sa.alias, '') AS alias,
1449
1497
  s.auto_title AS auto_title,
1450
1498
  s.auto_title_source AS auto_title_source,
@@ -1455,10 +1503,10 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1455
1503
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1456
1504
  LEFT JOIN projects p ON p.id = s.project_id
1457
1505
  WHERE e.thread_id = ?
1458
- ORDER BY e.added_at ASC`).all(e).map($d),r=Fd([e]).get(e);return{...xi(n,Ni(n.id),r),edges:s}}function Bd(e){return h().prepare(`SELECT t.* FROM threads t
1506
+ ORDER BY e.added_at ASC`).all(e).map(_p),r=fp([e]).get(e);return{...Ui(n,Hi(n.id),r),edges:s}}function bp(e){return _().prepare(`SELECT t.* FROM threads t
1459
1507
  JOIN thread_edges e ON e.thread_id = t.id
1460
1508
  WHERE e.session_id = ? AND t.archived = 0
1461
- ORDER BY t.created_at DESC`).all(e).map(s=>xi(s,Ni(s.id)))}function or(e){let t=h();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),a=e.confidence??1,c=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
1509
+ ORDER BY t.created_at DESC`).all(e).map(s=>Ui(s,Hi(s.id)))}function pr(e){let t=_();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),a=e.confidence??1,c=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
1462
1510
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1463
1511
  VALUES (?, ?, ?, ?, ?, ?, ?)
1464
1512
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -1466,22 +1514,22 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1466
1514
  role = excluded.role,
1467
1515
  confidence = excluded.confidence,
1468
1516
  source = excluded.source,
1469
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,s),ke(e.threadId);let u=Ud(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:s,alias:u.alias,auto_title:u.auto_title,auto_title_source:u.auto_title_source,alias_source:u.alias?"manual":null,first_user_message:u.first_user_message,project:u.project}}function Wd(e,t){let s=h().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&ke(e),{removed:s.changes}}function Ln(e,t,n){let s=h(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let c=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=n,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=n?"child":"origin";s.prepare(`UPDATE thread_edges
1517
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,s),Ae(e.threadId);let u=hp(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:s,alias:u.alias,auto_title:u.auto_title,auto_title_source:u.auto_title_source,alias_source:u.alias?"manual":null,first_user_message:u.first_user_message,project:u.project}}function Sp(e,t){let s=_().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&Ae(e),{removed:s.changes}}function Dn(e,t,n){let s=_(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let c=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=n,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=n?"child":"origin";s.prepare(`UPDATE thread_edges
1470
1518
  SET parent_session_id = ?, role = ?, added_at = ?
1471
- WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),ke(e);let a=Ud(t);return $d({...r,parent_session_id:n,role:o,added_at:new Date().toISOString(),alias:a.alias,auto_title:a.auto_title,auto_title_source:a.auto_title_source,first_user_message:a.first_user_message,project:a.project})}function qd(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");h().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),ke(e);let r=ce(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Xd(e){h().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),ke(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Jd(e){h().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),ke(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Gd(e){h().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),ke(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Yd(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=h(),s=new Date().toISOString();n.transaction(()=>{let o=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)n.prepare(`INSERT INTO thread_edges
1519
+ WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),Ae(e);let a=hp(t);return _p({...r,parent_session_id:n,role:o,added_at:new Date().toISOString(),alias:a.alias,auto_title:a.auto_title,auto_title_source:a.auto_title_source,first_user_message:a.first_user_message,project:a.project})}function Tp(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");_().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),Ae(e);let r=ce(e);if(!r)throw new Error(`thread ${e} not found`);return r}function yp(e){_().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function wp(e){_().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Rp(e){_().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Ae(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function kp(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=_(),s=new Date().toISOString();n.transaction(()=>{let o=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)n.prepare(`INSERT INTO thread_edges
1472
1520
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1473
1521
  VALUES (?, ?, ?, ?, ?, ?, ?)
1474
1522
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
1475
1523
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
1476
1524
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
1477
1525
  confidence = MAX(thread_edges.confidence, excluded.confidence),
1478
- source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),ke(t),Hd();let r=ce(t);if(!r)throw new Error("merge destination disappeared");return r}function Kd(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=h(),n=new Date().toISOString(),s=jd();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let o of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);a&&(t.prepare(`INSERT INTO thread_edges
1526
+ source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Ae(t),Ep();let r=ce(t);if(!r)throw new Error("merge destination disappeared");return r}function Ap(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=_(),n=new Date().toISOString(),s=pp();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let o of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);a&&(t.prepare(`INSERT INTO thread_edges
1479
1527
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1480
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,o,a.parent_session_id,a.role,a.confidence,a.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),ke(e.threadId),ke(s);let r=ce(s);if(!r)throw new Error("split destination disappeared");return r}q();import{execFile as mw}from"node:child_process";import{promisify as gw}from"node:util";import{readlink as fw,readFile as ep}from"node:fs/promises";import{platform as ur}from"node:os";import{readFileSync as ZT,statSync as ew}from"node:fs";var tw=200*1024*1024,ar=.7,cr=.5,Qd=cr,nw=[{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"}],sw=["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 zd(e){if(!e)return null;if(e.startsWith("/")){let n=e.split(" \xB7 ");if(n.length>1)return n[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function rw(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of nw)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function ow(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let o=r.toLowerCase();for(let a of sw)if(o.includes(a))return{weight:t[s],matched:a,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function Ci(e,t){if(!e||!t||e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function iw(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let o=Ci(s,r);o>n&&(n=o)}return n}function aw(e,t){let n=Ci(e.mean_embedding,t.mean_embedding),s=Ci(e.tail_pool,t.head_pool),r=iw(e.sample_chunks,t.sample_chunks),o=0,a=null;if(n>o&&(o=n,a="mean"),s>o&&(o=s,a="asymmetric"),r>o&&(o=r,a="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:a};let c=(o-.65)/.2*.3;return{weight:Math.round(c*100)/100,cosine:o,mode:a}}function cw(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 lw(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let n=0;for(let r of t)e.has(r)&&n++;return n===0?{weight:0,count:0}:{weight:Math.min(.4,n*.1),count:n}}function uw(e,t){let n=zd(e),s=zd(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function Vd(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function dw(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1481
- `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){n+=.5,s=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Vd(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Vd(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function pw(e,t,n=Qd){if(t.started_at_ms<=e.started_at_ms)return null;let s=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<s)return null;let r=rw(s,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=ow(o),c=lw(e.touched_files,t.touched_files),u=uw(e.auto_title,t.auto_title),d=aw(e,t),p=cw(e,t),f=dw(e,t),S=r.weight+a.weight+c.weight+u.weight+d.weight+p.weight+f.weight;if(S<n)return null;let y=[];if(r.label&&y.push(`temporal ${r.label} (+${r.weight})`),a.matched){let b=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;y.push(`continuation phrase "${a.matched}" in ${b} (+${a.weight})`)}if(c.count>0&&y.push(`${c.count} file${c.count===1?"":"s"} overlap (+${c.weight.toFixed(1)})`),u.brand&&y.push(`shared brand "${u.brand}" (+${u.weight})`),d.weight>0&&d.mode&&y.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),p.same&&y.push(`same cluster (+${p.weight})`),f.weight>0){let b=[];f.pathMatch&&b.push(`opened authored path "${f.pathMatch.split("/").pop()}"`),f.contentMatch&&b.push("verbatim-paste of authored content"),y.push(`doc-authorship: ${b.join(" + ")} (+${f.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,S),signals:{temporal:r.weight,continuation:a.weight,file_overlap:c.weight,same_brand:u.weight,semantic:d.weight,cluster:p.weight,doc_authorship:f.weight},reasons:y}}function Ut(e,t=Qd){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=null;for(let a=0;a<s;a++){let c=e[a],u=pw(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&n.push(o)}return n}function Zd(e,t){let n=new Map,s=c=>{let u=c;for(;n.get(u)!==u;)u=n.get(u);let d=c;for(;n.get(d)!==u;){let p=n.get(d);n.set(d,u),d=p}return u},r=(c,u)=>{let d=s(c),p=s(u);d!==p&&n.set(d,p)};for(let c of e)n.has(c.parent_id)||n.set(c.parent_id,c.parent_id),n.has(c.child_id)||n.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of n.keys()){let u=s(c),d=o.get(u);d||(d=[],o.set(u,d)),d.push(c)}let a=new Map;for(let c of t)a.set(c.id,c.started_at_ms);return Array.from(o.values()).map(c=>(c.sort((u,d)=>(a.get(u)??0)-(a.get(d)??0)),{rootId:c[0],sessionIds:c}))}function lr(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if(ew(e).size>tw)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=ZT(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 p=u.indexOf(`
1482
- `,d),f=p===-1?u.length:p,S=u.slice(d,f);if(d=p===-1?u.length:p+1,!S.trim())continue;let y;try{y=JSON.parse(S)}catch{continue}let b=y;if(b.type==="user"&&b.message?.role==="user"&&typeof b.message.content=="string"&&o.length<n){let k=b.message.content.trim();k&&o.push(k.length>s?k.slice(0,s):k)}let R=b.message?.content;if(Array.isArray(R))for(let k of R){if(!k||typeof k!="object")continue;let M=k;if(M.type!=="tool_use")continue;let P=M.input??{},G=typeof P.file_path=="string"?P.file_path:null;if(G){let D=ir(G);D&&r.add(D)}if((M.name==="Write"||M.name==="Edit"||M.name==="MultiEdit")&&G){let D=ir(G);D&&a.add(D);let v=typeof P.content=="string"?P.content:typeof P.new_string=="string"?P.new_string:null;v&&v.length>=200&&c.push(v.length>4096?v.slice(0,4096):v)}if(M.name==="Bash"&&typeof P.command=="string")for(let D of P.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let v=ir(D[1]);v&&r.add(v)}if((M.name==="Glob"||M.name==="Grep")&&typeof P.pattern=="string"){let D=ir(P.pattern);D&&!D.includes("*")&&r.add(D)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function ir(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var vi=gw(mw),_w=6,tp="Active ",np=" sessions \u2014 ",hw=6e4;async function Ew(){if(ur()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await vi(t,["-eo","pid=,comm="],{timeout:2e3}),s=[];for(let r of n.split(`
1483
- `)){let o=r.trim().match(/^(\d+)\s+(.+)$/);if(!o)continue;let a=Number(o[1]),c=o[2].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&Number.isFinite(a)&&s.push(a)}return s}catch{continue}return[]}async function bw(e){let t=ur();if(t==="linux")try{return(await fw(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:s}=await vi(n,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of s.split(`
1484
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Sw(e){let t=ur();if(t==="linux")try{let n=await ep(`/proc/${e}/stat`,"utf8"),s=n.lastIndexOf(")");if(s===-1)return null;let r=n.slice(s+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await ep("/proc/uptime","utf8"),c=Number(a.split(/\s+/)[0]);return Number.isFinite(c)?Date.now()-c*1e3+o/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await vi(n,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(s.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function yw(e,t){let n=await Ew();if(n.length===0)return null;let s=e.replace(/\/+$/,""),r=[];for(let a of n){let c=await bw(a);if(c&&(c===s||c.startsWith(s+"/"))){let u=await Sw(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=hw;for(let d of t){if(o.has(d.session_id)||!d.started_at)continue;let p=Date.parse(d.started_at);if(!Number.isFinite(p))continue;let f=Math.abs(p-a);f<u&&(u=f,c=d)}c&&o.add(c.session_id)}return o}function Tw(e){let n=h().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!n)throw new Error(`project ${e} not found`);return n}function ww(e,t){let n=h(),s=`${tp}${t}${np}%`,r=n.prepare(`SELECT t.id
1528
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,o,a.parent_session_id,a.role,a.confidence,a.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),Ae(e.threadId),Ae(s);let r=ce(s);if(!r)throw new Error("split destination disappeared");return r}H();import{execFile as Zw}from"node:child_process";import{promisify as eR}from"node:util";import{readlink as tR,readFile as Cp}from"node:fs/promises";import{platform as hr}from"node:os";import{readFileSync as $w,statSync as Uw}from"node:fs";var Hw=200*1024*1024,gr=.7,fr=.5,Op=fr,Bw=[{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"}],Ww=["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 xp(e){if(!e)return null;if(e.startsWith("/")){let n=e.split(" \xB7 ");if(n.length>1)return n[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function qw(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of Bw)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function Xw(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let o=r.toLowerCase();for(let a of Ww)if(o.includes(a))return{weight:t[s],matched:a,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function Wi(e,t){if(!e||!t||e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function Jw(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let o=Wi(s,r);o>n&&(n=o)}return n}function Gw(e,t){let n=Wi(e.mean_embedding,t.mean_embedding),s=Wi(e.tail_pool,t.head_pool),r=Jw(e.sample_chunks,t.sample_chunks),o=0,a=null;if(n>o&&(o=n,a="mean"),s>o&&(o=s,a="asymmetric"),r>o&&(o=r,a="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:a};let c=(o-.65)/.2*.3;return{weight:Math.round(c*100)/100,cosine:o,mode:a}}function Yw(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 zw(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let n=0;for(let r of t)e.has(r)&&n++;return n===0?{weight:0,count:0}:{weight:Math.min(.4,n*.1),count:n}}function Kw(e,t){let n=xp(e),s=xp(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function Np(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Vw(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1529
+ `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){n+=.5,s=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Np(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Np(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function Qw(e,t,n=Op){if(t.started_at_ms<=e.started_at_ms)return null;let s=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<s)return null;let r=qw(s,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=Xw(o),c=zw(e.touched_files,t.touched_files),u=Kw(e.auto_title,t.auto_title),d=Gw(e,t),p=Yw(e,t),f=Vw(e,t),b=r.weight+a.weight+c.weight+u.weight+d.weight+p.weight+f.weight;if(b<n)return null;let T=[];if(r.label&&T.push(`temporal ${r.label} (+${r.weight})`),a.matched){let S=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;T.push(`continuation phrase "${a.matched}" in ${S} (+${a.weight})`)}if(c.count>0&&T.push(`${c.count} file${c.count===1?"":"s"} overlap (+${c.weight.toFixed(1)})`),u.brand&&T.push(`shared brand "${u.brand}" (+${u.weight})`),d.weight>0&&d.mode&&T.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),p.same&&T.push(`same cluster (+${p.weight})`),f.weight>0){let S=[];f.pathMatch&&S.push(`opened authored path "${f.pathMatch.split("/").pop()}"`),f.contentMatch&&S.push("verbatim-paste of authored content"),T.push(`doc-authorship: ${S.join(" + ")} (+${f.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,b),signals:{temporal:r.weight,continuation:a.weight,file_overlap:c.weight,same_brand:u.weight,semantic:d.weight,cluster:p.weight,doc_authorship:f.weight},reasons:T}}function Wt(e,t=Op){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=null;for(let a=0;a<s;a++){let c=e[a],u=Qw(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&n.push(o)}return n}function Lp(e,t){let n=new Map,s=c=>{let u=c;for(;n.get(u)!==u;)u=n.get(u);let d=c;for(;n.get(d)!==u;){let p=n.get(d);n.set(d,u),d=p}return u},r=(c,u)=>{let d=s(c),p=s(u);d!==p&&n.set(d,p)};for(let c of e)n.has(c.parent_id)||n.set(c.parent_id,c.parent_id),n.has(c.child_id)||n.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of n.keys()){let u=s(c),d=o.get(u);d||(d=[],o.set(u,d)),d.push(c)}let a=new Map;for(let c of t)a.set(c.id,c.started_at_ms);return Array.from(o.values()).map(c=>(c.sort((u,d)=>(a.get(u)??0)-(a.get(d)??0)),{rootId:c[0],sessionIds:c}))}function _r(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if(Uw(e).size>Hw)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=$w(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 p=u.indexOf(`
1530
+ `,d),f=p===-1?u.length:p,b=u.slice(d,f);if(d=p===-1?u.length:p+1,!b.trim())continue;let T;try{T=JSON.parse(b)}catch{continue}let S=T;if(S.type==="user"&&S.message?.role==="user"&&typeof S.message.content=="string"&&o.length<n){let A=S.message.content.trim();A&&o.push(A.length>s?A.slice(0,s):A)}let w=S.message?.content;if(Array.isArray(w))for(let A of w){if(!A||typeof A!="object")continue;let I=A;if(I.type!=="tool_use")continue;let j=I.input??{},J=typeof j.file_path=="string"?j.file_path:null;if(J){let M=mr(J);M&&r.add(M)}if((I.name==="Write"||I.name==="Edit"||I.name==="MultiEdit")&&J){let M=mr(J);M&&a.add(M);let F=typeof j.content=="string"?j.content:typeof j.new_string=="string"?j.new_string:null;F&&F.length>=200&&c.push(F.length>4096?F.slice(0,4096):F)}if(I.name==="Bash"&&typeof j.command=="string")for(let M of j.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let F=mr(M[1]);F&&r.add(F)}if((I.name==="Glob"||I.name==="Grep")&&typeof j.pattern=="string"){let M=mr(j.pattern);M&&!M.includes("*")&&r.add(M)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function mr(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Xi=eR(Zw),nR=6,vp="Active ",Ip=" sessions \u2014 ",sR=6e4;async function rR(){if(hr()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Xi(t,["-eo","pid=,comm="],{timeout:2e3}),s=[];for(let r of n.split(`
1531
+ `)){let o=r.trim().match(/^(\d+)\s+(.+)$/);if(!o)continue;let a=Number(o[1]),c=o[2].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&Number.isFinite(a)&&s.push(a)}return s}catch{continue}return[]}async function oR(e){let t=hr();if(t==="linux")try{return(await tR(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:s}=await Xi(n,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of s.split(`
1532
+ `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function iR(e){let t=hr();if(t==="linux")try{let n=await Cp(`/proc/${e}/stat`,"utf8"),s=n.lastIndexOf(")");if(s===-1)return null;let r=n.slice(s+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await Cp("/proc/uptime","utf8"),c=Number(a.split(/\s+/)[0]);return Number.isFinite(c)?Date.now()-c*1e3+o/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Xi(n,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(s.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function aR(e,t){let n=await rR();if(n.length===0)return null;let s=e.replace(/\/+$/,""),r=[];for(let a of n){let c=await oR(a);if(c&&(c===s||c.startsWith(s+"/"))){let u=await iR(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=sR;for(let d of t){if(o.has(d.session_id)||!d.started_at)continue;let p=Date.parse(d.started_at);if(!Number.isFinite(p))continue;let f=Math.abs(p-a);f<u&&(u=f,c=d)}c&&o.add(c.session_id)}return o}function cR(e){let n=_().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!n)throw new Error(`project ${e} not found`);return n}function lR(e,t){let n=_(),s=`${vp}${t}${Ip}%`,r=n.prepare(`SELECT t.id
1485
1533
  FROM threads t
1486
1534
  WHERE t.archived = 0
1487
1535
  AND t.name LIKE ?
@@ -1492,7 +1540,7 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1492
1540
  WHERE s.project_id = ?
1493
1541
  AND t.archived = 0
1494
1542
  AND t.name LIKE ?
1495
- LIMIT 1`).get(e,s);return o?ce(o.id):null}function sp(e){let t=e?new Date(e):new Date,n=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${s}-${r}`}function Li(e,t){let n=h(),s=t>0,r=s?Date.now()-t*60*60*1e3:0;return s?n.prepare(`SELECT s.id AS session_id,
1543
+ LIMIT 1`).get(e,s);return o?ce(o.id):null}function Mp(e){let t=e?new Date(e):new Date,n=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${s}-${r}`}function qi(e,t){let n=_(),s=t>0,r=s?Date.now()-t*60*60*1e3:0;return s?n.prepare(`SELECT s.id AS session_id,
1496
1544
  sa.alias AS alias,
1497
1545
  s.auto_title AS auto_title,
1498
1546
  s.first_user_message AS first_user_message,
@@ -1512,19 +1560,19 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1512
1560
  FROM sessions s
1513
1561
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1514
1562
  WHERE s.project_id = ?
1515
- ORDER BY s.started_at ASC`).all(e)}function Rw(e){let t=h(),n=[];for(let s of e){if(!s.started_at)continue;let r=Date.parse(s.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(s.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=lr(o.file_path,{maxUserMessages:7});n.push({id:s.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:s.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:s.auto_title,touched_files:c.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:c.authored_paths,authored_content:c.authored_content})}return n}function kw(e){let n=h().prepare(`SELECT session_id, parent_session_id, source
1563
+ ORDER BY s.started_at ASC`).all(e)}function uR(e){let t=_(),n=[];for(let s of e){if(!s.started_at)continue;let r=Date.parse(s.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(s.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=_r(o.file_path,{maxUserMessages:7});n.push({id:s.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:s.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:s.auto_title,touched_files:c.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:c.authored_paths,authored_content:c.authored_content})}return n}function dR(e){let n=_().prepare(`SELECT session_id, parent_session_id, source
1516
1564
  FROM thread_edges
1517
- WHERE thread_id = ?`).all(e),s=new Map;for(let r of n)s.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return s}async function rp(e,t={}){let n=Tw(e),s=t.windowHours??_w,r=t.scoreThreshold??cr,o=t.useLivePids??!0,a=[],c=[];if(o&&n.decoded_path){let R=Li(e,0),k=await yw(n.decoded_path,R);if(k===null){let P=ur()==="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(P),c=Li(e,s)}else k.size===0?(a.push(`No active terminals open in ${n.name} (cwd=${n.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=R.filter(M=>k.has(M.session_id))}else c=Li(e,s);c.length===0&&!a.length&&a.push(`No active sessions in ${n.name} within the last ${s}h.`);let u=ww(e,n.name),d=new Set(u?u.edges.map(R=>R.session_id):[]),p=c.filter(R=>!d.has(R.session_id)),f=Rw(c);f.sort((R,k)=>R.started_at_ms-k.started_at_ms);let S=Ut(f,r),y=u?u.edges.filter(R=>R.source!=="auto-active"&&(R.parent_session_id||R.role==="origin")).map(R=>({session_id:R.session_id,parent_session_id:R.parent_session_id})):[],b=u?u.name:`${tp}${n.name}${np}${sp(t.todayIso)}`;return{project:n,thread:{id:u?.id??null,name:b,exists:!!u,existing_session_count:u?.edges.length??0},candidates:c,proposed_additions:p,proposed_edges:S,preserved_manual_edges:y,warnings:a}}function op(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},n;e.thread.exists&&e.thread.id?n=e.thread.id:n=rr({name:e.thread.name,summary:`Auto-captured by sync-active on ${sp()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=n;let s=kw(n);for(let a of e.candidates)s.has(a.session_id)||(or({threadId:n,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,s.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=s.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&s.has(a.parent_id))try{Ln(n,a.child_id,a.parent_id),t.edges_set++,s.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=h().prepare(`SELECT session_id FROM thread_edges
1565
+ WHERE thread_id = ?`).all(e),s=new Map;for(let r of n)s.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return s}async function Dp(e,t={}){let n=cR(e),s=t.windowHours??nR,r=t.scoreThreshold??fr,o=t.useLivePids??!0,a=[],c=[];if(o&&n.decoded_path){let w=qi(e,0),A=await aR(n.decoded_path,w);if(A===null){let j=hr()==="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(j),c=qi(e,s)}else A.size===0?(a.push(`No active terminals open in ${n.name} (cwd=${n.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=w.filter(I=>A.has(I.session_id))}else c=qi(e,s);c.length===0&&!a.length&&a.push(`No active sessions in ${n.name} within the last ${s}h.`);let u=lR(e,n.name),d=new Set(u?u.edges.map(w=>w.session_id):[]),p=c.filter(w=>!d.has(w.session_id)),f=uR(c);f.sort((w,A)=>w.started_at_ms-A.started_at_ms);let b=Wt(f,r),T=u?u.edges.filter(w=>w.source!=="auto-active"&&(w.parent_session_id||w.role==="origin")).map(w=>({session_id:w.session_id,parent_session_id:w.parent_session_id})):[],S=u?u.name:`${vp}${n.name}${Ip}${Mp(t.todayIso)}`;return{project:n,thread:{id:u?.id??null,name:S,exists:!!u,existing_session_count:u?.edges.length??0},candidates:c,proposed_additions:p,proposed_edges:b,preserved_manual_edges:T,warnings:a}}function jp(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},n;e.thread.exists&&e.thread.id?n=e.thread.id:n=dr({name:e.thread.name,summary:`Auto-captured by sync-active on ${Mp()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=n;let s=dR(n);for(let a of e.candidates)s.has(a.session_id)||(pr({threadId:n,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,s.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=s.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&s.has(a.parent_id))try{Dn(n,a.child_id,a.parent_id),t.edges_set++,s.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=_().prepare(`SELECT session_id FROM thread_edges
1518
1566
  WHERE thread_id = ?
1519
1567
  AND source = 'auto-active'
1520
1568
  AND parent_session_id IS NULL
1521
- AND role = 'child'`).all(n);for(let a of o)try{Ln(n,a.session_id,null)}catch{}return t}q();ne();import{randomUUID as Aw}from"node:crypto";import{writeFileSync as xw}from"node:fs";import{join as Nw}from"node:path";var Ow=Nw(W,"thread-folders.json");function ip(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 vn(){try{V();let e=Ii({includeArchived:!0});xw(Ow,JSON.stringify({folders:e},null,2))}catch{}}function Ii(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return h().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(ip)}function gt(e){let t=h().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?ip(t):null}function ap(e){let t=e.name.trim();if(!t)throw new Error("folder name cannot be empty");if(t.length>200)throw new Error("folder name too long (200 char max)");let n=e.parentFolderId??null,s=e.projectScope??null;if(n){let c=gt(n);if(!c)throw new Error(`parent folder ${n} not found`);s=c.project_scope}let r=Aw(),o=new Date().toISOString(),a=cp(n,s);return h().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1522
- VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,n,s,o,a),vn(),{id:r,name:t,parent_folder_id:n,project_scope:s,created_at:o,archived:!1,sort_order:a}}function cp(e,t){return h().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1569
+ AND role = 'child'`).all(n);for(let a of o)try{Dn(n,a.session_id,null)}catch{}return t}H();Z();import{randomUUID as pR}from"node:crypto";import{writeFileSync as mR}from"node:fs";import{join as gR}from"node:path";var fR=gR(W,"thread-folders.json");function Pp(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 jn(){try{K();let e=Ji({includeArchived:!0});mR(fR,JSON.stringify({folders:e},null,2))}catch{}}function Ji(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return _().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(Pp)}function Et(e){let t=_().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?Pp(t):null}function Fp(e){let t=e.name.trim();if(!t)throw new Error("folder name cannot be empty");if(t.length>200)throw new Error("folder name too long (200 char max)");let n=e.parentFolderId??null,s=e.projectScope??null;if(n){let c=Et(n);if(!c)throw new Error(`parent folder ${n} not found`);s=c.project_scope}let r=pR(),o=new Date().toISOString(),a=$p(n,s);return _().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1570
+ VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,n,s,o,a),jn(),{id:r,name:t,parent_folder_id:n,project_scope:s,created_at:o,archived:!1,sort_order:a}}function $p(e,t){return _().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1523
1571
  FROM thread_folders
1524
1572
  WHERE parent_folder_id IS ?
1525
- AND project_scope IS ?`).get(e,t)?.next??0}function lp(e,t){let n=t.trim();if(!n)throw new Error("folder name cannot be empty");if(n.length>200)throw new Error("folder name too long (200 char max)");let s=gt(e);if(!s)throw new Error(`folder ${e} not found`);return h().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(n,e),vn(),{...s,name:n}}function up(e,t){let n=gt(e);if(!n)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let s=n.project_scope;if(t!==null){let o=gt(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=gt(a);if(!u)break;a=u.parent_folder_id,c++}s=o.project_scope}let r=cp(t,s);return h().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,s,r,e),vn(),{...n,parent_folder_id:t,project_scope:s,sort_order:r}}function dp(e,t,n){if(!Array.isArray(n)||n.length===0)throw new Error("ordered_ids must be a non-empty array");let s=new Set;for(let d of n){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(s.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);s.add(d)}let r=h(),o=r.prepare(`SELECT id FROM thread_folders
1573
+ AND project_scope IS ?`).get(e,t)?.next??0}function Up(e,t){let n=t.trim();if(!n)throw new Error("folder name cannot be empty");if(n.length>200)throw new Error("folder name too long (200 char max)");let s=Et(e);if(!s)throw new Error(`folder ${e} not found`);return _().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(n,e),jn(),{...s,name:n}}function Hp(e,t){let n=Et(e);if(!n)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let s=n.project_scope;if(t!==null){let o=Et(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=Et(a);if(!u)break;a=u.parent_folder_id,c++}s=o.project_scope}let r=$p(t,s);return _().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,s,r,e),jn(),{...n,parent_folder_id:t,project_scope:s,sort_order:r}}function Bp(e,t,n){if(!Array.isArray(n)||n.length===0)throw new Error("ordered_ids must be a non-empty array");let s=new Set;for(let d of n){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(s.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);s.add(d)}let r=_(),o=r.prepare(`SELECT id FROM thread_folders
1526
1574
  WHERE parent_folder_id IS ?
1527
- AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==n.length)throw new Error(`ordered_ids length ${n.length} does not match sibling count ${a.size}`);for(let d of n)if(!a.has(d))throw new Error(`folder ${d} is not in the named sibling bucket`);let c=r.prepare("UPDATE thread_folders SET sort_order = ? WHERE id = ?");r.transaction(d=>{d.forEach((p,f)=>c.run(f*100,p))})(n),vn()}function pp(e){if(!gt(e))throw new Error(`folder ${e} not found`);h().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),vn()}function mp(e,t){if(t!==null&&!gt(t))throw new Error(`folder ${t} not found`);if(h().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function gp(e,t){let n=new Set,s=[];for(let r of t){if(n.has(r.id))continue;let o=null,a="";if(r.source_session_id===e)o="outbound",a=r.target_session_id;else if(r.target_session_id===e)o="inbound",a=r.source_session_id;else continue;n.add(r.id),s.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return s.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),s}q();var Cw=4e3,Lw=2,vw=30,Iw=.2,Mw={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function dr(e){return e?Math.ceil(e.length/4):0}function fp(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/vw);return Math.max(Iw,t)}function _p(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function hp(e){return h().prepare(`SELECT s.id,
1575
+ AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==n.length)throw new Error(`ordered_ids length ${n.length} does not match sibling count ${a.size}`);for(let d of n)if(!a.has(d))throw new Error(`folder ${d} is not in the named sibling bucket`);let c=r.prepare("UPDATE thread_folders SET sort_order = ? WHERE id = ?");r.transaction(d=>{d.forEach((p,f)=>c.run(f*100,p))})(n),jn()}function Wp(e){if(!Et(e))throw new Error(`folder ${e} not found`);_().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),jn()}function qp(e,t){if(t!==null&&!Et(t))throw new Error(`folder ${t} not found`);if(_().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function Xp(e,t){let n=new Set,s=[];for(let r of t){if(n.has(r.id))continue;let o=null,a="";if(r.source_session_id===e)o="outbound",a=r.target_session_id;else if(r.target_session_id===e)o="inbound",a=r.source_session_id;else continue;n.add(r.id),s.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return s.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),s}H();var _R=4e3,hR=2,ER=30,bR=.2,SR={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function Er(e){return e?Math.ceil(e.length/4):0}function Jp(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/ER);return Math.max(bR,t)}function Gp(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function Yp(e){return _().prepare(`SELECT s.id,
1528
1576
  NULLIF(sa.alias, '') AS alias,
1529
1577
  s.auto_title,
1530
1578
  s.auto_title_source,
@@ -1535,72 +1583,72 @@ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1535
1583
  FROM sessions s
1536
1584
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1537
1585
  LEFT JOIN projects p ON p.id = s.project_id
1538
- WHERE s.id = ?`).get(e)??null}function Ep(e){let n=h().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!n||!n.summary)return null;let s=n.summary.trim();return s.length>0?s:null}function bp(e){let t=e.alias?.trim(),n=e.auto_title?.trim(),s=e.first_user_message?.trim();return n&&e.auto_title_source==="agent"?n:t||n||(s?s.slice(0,80):e.id.slice(0,8))}function jw(e){let n=h().prepare(`SELECT id, auto_title, started_at
1586
+ WHERE s.id = ?`).get(e)??null}function zp(e){let n=_().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!n||!n.summary)return null;let s=n.summary.trim();return s.length>0?s:null}function Kp(e){let t=e.alias?.trim(),n=e.auto_title?.trim(),s=e.first_user_message?.trim();return n&&e.auto_title_source==="agent"?n:t||n||(s?s.slice(0,80):e.id.slice(0,8))}function TR(e){let n=_().prepare(`SELECT id, auto_title, started_at
1539
1587
  FROM sessions
1540
1588
  WHERE project_id = ?
1541
- ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,o=[];for(let S of n){if(!S.auto_title||!S.auto_title.startsWith("/")){o.push({id:S.id,brand:null,skill:null});continue}let y=S.auto_title.split(" \xB7 "),b=y[0].trim(),R=y.length>1?y.slice(1).join(" \xB7 ").trim():null;o.push({id:S.id,brand:R||null,skill:b||null}),R&&s.add(R),b&&r.add(b)}let a=[...s].sort(),c=new Map;a.forEach((S,y)=>c.set(S,y));let u=[...r].sort(),d=new Map;u.forEach((S,y)=>d.set(S,y));let p=new Map,f=new Map;for(let S of o){if(!S.brand||!S.skill)continue;let y=c.get(S.brand),b=d.get(S.skill);if(y===void 0||b===void 0)continue;let R=`${y}.${b}`,k=(p.get(R)??0)+1;p.set(R,k),f.set(S.id,`${y}.${b}.${k}`)}return{byId:f}}function Dw(e){return{table:e!==null?jw(e):null,originProjectId:e,cache:new Map}}function pr(e,t){let n=e.cache.get(t);if(n)return n;let s=hp(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:s.id,title:bp(s),decimal:r,summary:Ep(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,o),o}function Pw(e,t){let s=h().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1589
+ ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,o=[];for(let b of n){if(!b.auto_title||!b.auto_title.startsWith("/")){o.push({id:b.id,brand:null,skill:null});continue}let T=b.auto_title.split(" \xB7 "),S=T[0].trim(),w=T.length>1?T.slice(1).join(" \xB7 ").trim():null;o.push({id:b.id,brand:w||null,skill:S||null}),w&&s.add(w),S&&r.add(S)}let a=[...s].sort(),c=new Map;a.forEach((b,T)=>c.set(b,T));let u=[...r].sort(),d=new Map;u.forEach((b,T)=>d.set(b,T));let p=new Map,f=new Map;for(let b of o){if(!b.brand||!b.skill)continue;let T=c.get(b.brand),S=d.get(b.skill);if(T===void 0||S===void 0)continue;let w=`${T}.${S}`,A=(p.get(w)??0)+1;p.set(w,A),f.set(b.id,`${T}.${S}.${A}`)}return{byId:f}}function yR(e){return{table:e!==null?TR(e):null,originProjectId:e,cache:new Map}}function br(e,t){let n=e.cache.get(t);if(n)return n;let s=Yp(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:s.id,title:Kp(s),decimal:r,summary:zp(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,o),o}function wR(e,t){let s=_().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1542
1590
  FROM thread_edges te
1543
1591
  WHERE te.session_id = ?
1544
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let a=pr(e,o.pid);a&&r.push(a)}return r}function Fw(e,t){let s=h().prepare(`SELECT DISTINCT te.session_id AS sid
1592
+ AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let a=br(e,o.pid);a&&r.push(a)}return r}function RR(e,t){let s=_().prepare(`SELECT DISTINCT te.session_id AS sid
1545
1593
  FROM thread_edges te
1546
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let a=pr(e,o.sid);a&&r.push(a)}return r}function Sp(e){let t=Mw[e.linkType]??.5,n=Ht(e.confidence),s=t*n,r=fp(e.daysApart),o=e.embeddingCosine??.5,a=Ht(e.pagerank);if(e.scoring==="pagerank")return Ht(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Ht(s):Ht(o);let c=.35*s+.2*r+.2*o+.25*a;return Ht(c)}function Ht(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function $w(e,t,n,s,r){let o=new Map;function a(c,u){if(c===u)return;let d=o.get(c);d||(d=new Set,o.set(c,d)),d.add(u)}for(let c of t)a(c.source_session_id,c.target_session_id),a(c.target_session_id,c.source_session_id);for(let c of n)a(e,c.session_id);for(let c of n)a(c.session_id,e);for(let c of s)a(e,c.session_id);for(let c of s)a(c.session_id,e);if(r>1){let c=new Set([e]),u=new Set([e]);for(let d=1;d<r;d++){let p=new Set;for(let f of c){let S=o.get(f);if(S)for(let y of S){if(u.has(y))continue;let b=un(y).filter(R=>R.approved);for(let R of b)a(R.source_session_id,R.target_session_id),a(R.target_session_id,R.source_session_id);u.add(y),p.add(y)}}if(p.size===0)break;for(let f of p)c.add(f)}}return{edges:o}}function Uw(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,a=new Map(r.map(d=>[d,o]));for(let d=0;d<n;d++){let p=new Map(r.map(f=>[f,(1-s)/r.length]));for(let f of r){let S=e.edges.get(f);if(!S||S.size===0)continue;let y=(a.get(f)??0)/S.size;for(let b of S)p.set(b,(p.get(b)??0)+s*y)}a=p}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,p]of a)u.set(d,p/c);return u}var yp=240;function Tp(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function Hw(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let o=Tp(e.summary,yp);return`${r}
1547
- ${o}`}return r}function Bw(e,t,n){let s=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(s.push(c),o+=dr(c),e.summary){let u=Tp(e.summary,yp*4);s.push(u),o+=dr(u)}s.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,p=dr(d),f=[],S=0;for(let y of u.refs){let b=Hw(y),R=dr(b);if(o+p+S+R>n){r.push({session_id:y.session_id,title:y.title,decimal:y.decimal,summary:y.summary,project:y.project,started_at:y.started_at});continue}f.push(b),S+=R}if(f.length>0){s.push(d);for(let y of f)s.push(y);s.push(""),o+=p+S}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
1594
+ WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let a=br(e,o.sid);a&&r.push(a)}return r}function Vp(e){let t=SR[e.linkType]??.5,n=qt(e.confidence),s=t*n,r=Jp(e.daysApart),o=e.embeddingCosine??.5,a=qt(e.pagerank);if(e.scoring==="pagerank")return qt(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?qt(s):qt(o);let c=.35*s+.2*r+.2*o+.25*a;return qt(c)}function qt(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function kR(e,t,n,s,r){let o=new Map;function a(c,u){if(c===u)return;let d=o.get(c);d||(d=new Set,o.set(c,d)),d.add(u)}for(let c of t)a(c.source_session_id,c.target_session_id),a(c.target_session_id,c.source_session_id);for(let c of n)a(e,c.session_id);for(let c of n)a(c.session_id,e);for(let c of s)a(e,c.session_id);for(let c of s)a(c.session_id,e);if(r>1){let c=new Set([e]),u=new Set([e]);for(let d=1;d<r;d++){let p=new Set;for(let f of c){let b=o.get(f);if(b)for(let T of b){if(u.has(T))continue;let S=mn(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),p.add(T)}}if(p.size===0)break;for(let f of p)c.add(f)}}return{edges:o}}function AR(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,a=new Map(r.map(d=>[d,o]));for(let d=0;d<n;d++){let p=new Map(r.map(f=>[f,(1-s)/r.length]));for(let f of r){let b=e.edges.get(f);if(!b||b.size===0)continue;let T=(a.get(f)??0)/b.size;for(let S of b)p.set(S,(p.get(S)??0)+s*T)}a=p}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,p]of a)u.set(d,p/c);return u}var Qp=240;function Zp(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function xR(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let o=Zp(e.summary,Qp);return`${r}
1595
+ ${o}`}return r}function NR(e,t,n){let s=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(s.push(c),o+=Er(c),e.summary){let u=Zp(e.summary,Qp*4);s.push(u),o+=Er(u)}s.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,p=Er(d),f=[],b=0;for(let T of u.refs){let S=xR(T),w=Er(S);if(o+p+b+w>n){r.push({session_id:T.session_id,title:T.title,decimal:T.decimal,summary:T.summary,project:T.project,started_at:T.started_at});continue}f.push(S),b+=w}if(f.length>0){s.push(d);for(let T of f)s.push(T);s.push(""),o+=p+b}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
1548
1596
  `)+`
1549
- `,budgetUsed:o,truncated:r}}function Ww(e,t,n,s,r,o){let a=[];for(let c of n){if(s&&!s.has(c.link_type))continue;let u=null;if(c.source_session_id===t.session_id?u=c.target_session_id:c.target_session_id===t.session_id&&(u=c.source_session_id),!u)continue;let d=pr(e,u);if(!d)continue;let p=_p(t.started_at,d.started_at),f=Sp({confidence:c.confidence,linkType:c.link_type,daysApart:p,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:f,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(p)}d apart`,link_type:c.link_type})}return a}function mr(e,t={}){let n=Math.max(100,Math.floor(t.budget??Cw)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Lw)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=hp(e);if(!u)throw new Error(`session not found: ${e}`);let d=Dw(u.project_id),p={session_id:u.id,title:bp(u),decimal:d.table?.byId.get(u.id)??null,summary:Ep(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,p);let f=Pw(d,e),S=Fw(d,e),y=un(e).filter(U=>U.approved).filter(U=>!c||c.has(U.link_type)).filter(U=>o||U.link_type!=="wiki_link"),b=$w(e,y,f,S,r),R=Uw(b),k=[],M=[],P=[],G=[];for(let U of y){let L=U.source_session_id===e?U.target_session_id:U.source_session_id,i=pr(d,L);if(!i)continue;let l=_p(p.started_at,i.started_at),m=Sp({confidence:U.confidence,linkType:U.link_type,daysApart:l,embeddingCosine:null,pagerank:R.get(L)??0,scoring:s}),g=fp(l),_=`${U.link_type} confidence=${U.confidence.toFixed(2)} recency=${g.toFixed(2)} (${Math.round(l)}d apart)`,E={...i,score:m,evidence:_,link_type:U.link_type};U.link_type==="citation"?k.push(E):U.link_type==="similar"?M.push(E):U.link_type==="wiki_link"?G.push(E):P.push(E)}if(a){let U=St({sourceSessionId:e,status:"pending",limit:100}),L=St({targetSessionId:e,status:"pending",limit:100}),i=[...U,...L],l=new Set,m=i.filter(_=>l.has(_.id)?!1:(l.add(_.id),!0)),g=Ww(d,p,m,c,s,R);for(let _ of g)_.link_type==="citation"?k.push(_):_.link_type==="similar"?M.push(_):_.link_type==="wiki_link"?G.push(_):P.push(_)}let D=(U,L)=>L.score-U.score;k.sort(D),M.sort(D),P.sort(D),G.sort(D);let F=Bw(p,[{heading:"Parents",refs:f},{heading:"Children",refs:S},{heading:"Citations (approved)",refs:k},{heading:"Similar sessions",refs:M},{heading:"Cousins (skill track + temporal)",refs:P},{heading:"Wiki links (manual)",refs:G}],n);return{origin:p,parents:f,children:S,citations:k,similar:M,cousins:P,wikiLinks:G,bundle:F.bundle,budgetUsed:F.budgetUsed,budgetRemaining:Math.max(0,n-F.budgetUsed),truncated:F.truncated}}import{randomUUID as zw}from"node:crypto";var qw=50;function Xw(e){if(e.length===0)return[];let t=[...e].sort((u,d)=>u.added_at<d.added_at?-1:u.added_at>d.added_at?1:0),n=new Map,s=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=n.get(u.parent_session_id)??[];d.push(u),n.set(u.parent_session_id,d)}let o=t.filter(u=>u.role==="origin"),c=[...o.length>0?o:t.filter(u=>!u.parent_session_id)];for(;c.length>0;){let u=c.shift();if(s.has(u.session_id))continue;s.add(u.session_id),r.push(u.session_id);let d=n.get(u.session_id)??[];for(let p of d)s.has(p.session_id)||c.push(p)}for(let u of t)s.has(u.session_id)||(s.add(u.session_id),r.push(u.session_id));return r}function Jw(e){let t=Xo(e.sessionId),n=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
1597
+ `,budgetUsed:o,truncated:r}}function OR(e,t,n,s,r,o){let a=[];for(let c of n){if(s&&!s.has(c.link_type))continue;let u=null;if(c.source_session_id===t.session_id?u=c.target_session_id:c.target_session_id===t.session_id&&(u=c.source_session_id),!u)continue;let d=br(e,u);if(!d)continue;let p=Gp(t.started_at,d.started_at),f=Vp({confidence:c.confidence,linkType:c.link_type,daysApart:p,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:f,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(p)}d apart`,link_type:c.link_type})}return a}function Sr(e,t={}){let n=Math.max(100,Math.floor(t.budget??_R)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??hR)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=Yp(e);if(!u)throw new Error(`session not found: ${e}`);let d=yR(u.project_id),p={session_id:u.id,title:Kp(u),decimal:d.table?.byId.get(u.id)??null,summary:zp(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,p);let f=wR(d,e),b=RR(d,e),T=mn(e).filter(U=>U.approved).filter(U=>!c||c.has(U.link_type)).filter(U=>o||U.link_type!=="wiki_link"),S=kR(e,T,f,b,r),w=AR(S),A=[],I=[],j=[],J=[];for(let U of T){let v=U.source_session_id===e?U.target_session_id:U.source_session_id,i=br(d,v);if(!i)continue;let l=Gp(p.started_at,i.started_at),m=Vp({confidence:U.confidence,linkType:U.link_type,daysApart:l,embeddingCosine:null,pagerank:w.get(v)??0,scoring:s}),g=Jp(l),h=`${U.link_type} confidence=${U.confidence.toFixed(2)} recency=${g.toFixed(2)} (${Math.round(l)}d apart)`,E={...i,score:m,evidence:h,link_type:U.link_type};U.link_type==="citation"?A.push(E):U.link_type==="similar"?I.push(E):U.link_type==="wiki_link"?J.push(E):j.push(E)}if(a){let U=yt({sourceSessionId:e,status:"pending",limit:100}),v=yt({targetSessionId:e,status:"pending",limit:100}),i=[...U,...v],l=new Set,m=i.filter(h=>l.has(h.id)?!1:(l.add(h.id),!0)),g=OR(d,p,m,c,s,w);for(let h of g)h.link_type==="citation"?A.push(h):h.link_type==="similar"?I.push(h):h.link_type==="wiki_link"?J.push(h):j.push(h)}let M=(U,v)=>v.score-U.score;A.sort(M),I.sort(M),j.sort(M),J.sort(M);let ne=NR(p,[{heading:"Parents",refs:f},{heading:"Children",refs:b},{heading:"Citations (approved)",refs:A},{heading:"Similar sessions",refs:I},{heading:"Cousins (skill track + temporal)",refs:j},{heading:"Wiki links (manual)",refs:J}],n);return{origin:p,parents:f,children:b,citations:A,similar:I,cousins:j,wikiLinks:J,bundle:ne.bundle,budgetUsed:ne.budgetUsed,budgetRemaining:Math.max(0,n-ne.budgetUsed),truncated:ne.truncated}}import{randomUUID as jR}from"node:crypto";var LR=50;function CR(e){if(e.length===0)return[];let t=[...e].sort((u,d)=>u.added_at<d.added_at?-1:u.added_at>d.added_at?1:0),n=new Map,s=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=n.get(u.parent_session_id)??[];d.push(u),n.set(u.parent_session_id,d)}let o=t.filter(u=>u.role==="origin"),c=[...o.length>0?o:t.filter(u=>!u.parent_session_id)];for(;c.length>0;){let u=c.shift();if(s.has(u.session_id))continue;s.add(u.session_id),r.push(u.session_id);let d=n.get(u.session_id)??[];for(let p of d)s.has(p.session_id)||c.push(p)}for(let u of t)s.has(u.session_id)||(s.add(u.session_id),r.push(u.session_id));return r}function vR(e){let t=ti(e.sessionId),n=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
1550
1598
  `);return`${t}
1551
- ${n}`}function Gw(e){return ve(e)?.auto_title_source??null}async function Yw(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(Te(),ct));if(!n())throw new Error("claude CLI not found on PATH");let s=await t(e.prompt,[],{model:e.model});if(!s.success)throw new Error(`claude CLI exited ${s.exitCode}: ${s.stderr.slice(-500)}`);let r=Kw(s.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,qw)}function Kw(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return wp(s)}}catch{}return wp(t)}function wp(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function kp(e,t={}){let n=ce(e);if(!n)throw new Error(`thread not found: ${e}`);let s=Xw(n.edges),r=s.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let p=0;p<s.length;p++){let f=s[p],S=p+1;if(a?.aborted){let b={sessionId:f,reason:"cancelled"};u.push(b),t.onSkipped?.(b);continue}if(!o&&Gw(f)==="agent"){let b={sessionId:f,reason:"already-titled"};u.push(b),t.onSkipped?.(b);continue}let y;try{if(Rp)y=await Rp({sessionId:f,current:S,total:r});else{let b=Jw({sessionId:f,current:S,total:r});y=await Yw({prompt:b,model:t.model})}}catch(b){let R=b instanceof Error?b.message:String(b??"unknown error"),k={sessionId:f,error:R};d.push(k),t.onFailed?.(k);continue}try{Ee(f,y,"agent")}catch(b){let R=b instanceof Error?b.message:String(b??"unknown error"),k={sessionId:f,error:`setAutoTitle failed: ${R}`};d.push(k),t.onFailed?.(k);continue}c.push(f),t.onProgress?.({current:S,total:r,sessionId:f,title:y})}return{generated:c,skipped:u,failed:d}}var Rp=null;var Mn=new Map,Vw=300*1e3;function In(e,t,n){let s=e.events.length+1;e.events.push({id:s,kind:t,data:n});for(let r of e.waiters)r.resolve();e.waiters.clear()}function Qw(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{Mn.delete(e.jobId)},Vw)}function Zw(e){let t=0,n=0,s=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(n+=1),r.kind==="error"&&(s+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:n,failed:s,result:e.result}}function Ap(e){let t=zw(),n=new AbortController,s={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:n,result:null,cleanupTimer:null};return Mn.set(t,s),(async()=>{await Promise.resolve();try{let r=await kp(e.threadId,{force:e.force??!1,signal:n.signal,model:e.model,onProgress:o=>{s.total=o.total,In(s,"progress",o)},onSkipped:o=>{In(s,"skipped",o)},onFailed:o=>{In(s,"error",o)}});s.result=r,s.status=n.signal.aborted?"cancelled":"done",s.endedAt=new Date().toISOString(),In(s,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");s.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},s.status="failed",s.endedAt=new Date().toISOString(),In(s,"done",s.result)}finally{Qw(s)}})(),t}async function*xp(e,t=0){let n=Mn.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(s+=1,yield r,r.kind==="done")return}if(n.endedAt)return;await new Promise(r=>{n.waiters.add({resolve:r})})}}function Np(e){let t=Mn.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Mi(e){let t=Mn.get(e);return t?Zw(t):null}import{existsSync as Op,mkdirSync as e0,readFileSync as t0,writeFileSync as n0,chmodSync as s0}from"node:fs";import{homedir as r0}from"node:os";import{join as Cp}from"node:path";import{z as ze}from"zod";function Lp(){return process.env.RECALL_HOME??Cp(r0(),".recall")}function o0(){let e=Lp();Op(e)||e0(e,{recursive:!0})}function vp(){return Cp(Lp(),"config.json")}var fr=ze.object({enabled:ze.boolean().default(!1),backend:ze.enum(["api","mcp"]).default("api"),apiKey:ze.string().optional(),model:ze.string().default("claude-opus-4-7"),maxTagsPerSession:ze.number().int().min(1).max(10).default(4),minTagsPerSession:ze.number().int().min(1).max(10).default(2),autopilot:ze.boolean().default(!1)}),gr={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function Ip(){let e=vp();if(!Op(e))return{};try{return JSON.parse(t0(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Be(){let e=Ip().autoTag;if(!e)return{...gr};let t=fr.safeParse({...gr,...e});return t.success?t.data:{...gr}}function Mp(e){o0();let t=Ip(),n=fr.parse({...gr,...t.autoTag??{},...e}),s={...t,autoTag:n},r=vp();n0(r,JSON.stringify(s,null,2));try{s0(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return n}function ji(e){let{apiKey:t,...n}=e;return{...n,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}q();var _r="claude-haiku-4-5-20251001",jp=80,i0=2e3,Bt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},Dp=null;async function a0(e,t){if(Dp)return Dp(e,t);let{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(Te(),ct));return s()?n(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function c0(e){let n=h().prepare(`SELECT s.id,
1599
+ ${n}`}function IR(e){return ve(e)?.auto_title_source??null}async function MR(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(we(),pt));if(!n())throw new Error("claude CLI not found on PATH");let s=await t(e.prompt,[],{model:e.model});if(!s.success)throw new Error(`claude CLI exited ${s.exitCode}: ${s.stderr.slice(-500)}`);let r=DR(s.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,LR)}function DR(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return em(s)}}catch{}return em(t)}function em(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function nm(e,t={}){let n=ce(e);if(!n)throw new Error(`thread not found: ${e}`);let s=CR(n.edges),r=s.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let p=0;p<s.length;p++){let f=s[p],b=p+1;if(a?.aborted){let S={sessionId:f,reason:"cancelled"};u.push(S),t.onSkipped?.(S);continue}if(!o&&IR(f)==="agent"){let S={sessionId:f,reason:"already-titled"};u.push(S),t.onSkipped?.(S);continue}let T;try{if(tm)T=await tm({sessionId:f,current:b,total:r});else{let S=vR({sessionId:f,current:b,total:r});T=await MR({prompt:S,model:t.model})}}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),A={sessionId:f,error:w};d.push(A),t.onFailed?.(A);continue}try{he(f,T,"agent")}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),A={sessionId:f,error:`setAutoTitle failed: ${w}`};d.push(A),t.onFailed?.(A);continue}c.push(f),t.onProgress?.({current:b,total:r,sessionId:f,title:T})}return{generated:c,skipped:u,failed:d}}var tm=null;var Fn=new Map,PR=300*1e3;function Pn(e,t,n){let s=e.events.length+1;e.events.push({id:s,kind:t,data:n});for(let r of e.waiters)r.resolve();e.waiters.clear()}function FR(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{Fn.delete(e.jobId)},PR)}function $R(e){let t=0,n=0,s=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(n+=1),r.kind==="error"&&(s+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:n,failed:s,result:e.result}}function sm(e){let t=jR(),n=new AbortController,s={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:n,result:null,cleanupTimer:null};return Fn.set(t,s),(async()=>{await Promise.resolve();try{let r=await nm(e.threadId,{force:e.force??!1,signal:n.signal,model:e.model,onProgress:o=>{s.total=o.total,Pn(s,"progress",o)},onSkipped:o=>{Pn(s,"skipped",o)},onFailed:o=>{Pn(s,"error",o)}});s.result=r,s.status=n.signal.aborted?"cancelled":"done",s.endedAt=new Date().toISOString(),Pn(s,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");s.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},s.status="failed",s.endedAt=new Date().toISOString(),Pn(s,"done",s.result)}finally{FR(s)}})(),t}async function*rm(e,t=0){let n=Fn.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(s+=1,yield r,r.kind==="done")return}if(n.endedAt)return;await new Promise(r=>{n.waiters.add({resolve:r})})}}function om(e){let t=Fn.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Gi(e){let t=Fn.get(e);return t?$R(t):null}import{existsSync as im,mkdirSync as UR,readFileSync as HR,writeFileSync as BR,chmodSync as WR}from"node:fs";import{homedir as qR}from"node:os";import{join as am}from"node:path";import{z as et}from"zod";function cm(){return process.env.RECALL_HOME??am(qR(),".recall")}function XR(){let e=cm();im(e)||UR(e,{recursive:!0})}function lm(){return am(cm(),"config.json")}var yr=et.object({enabled:et.boolean().default(!1),backend:et.enum(["api","mcp"]).default("api"),apiKey:et.string().optional(),model:et.string().default("claude-opus-4-7"),maxTagsPerSession:et.number().int().min(1).max(10).default(4),minTagsPerSession:et.number().int().min(1).max(10).default(2),autopilot:et.boolean().default(!1)}),Tr={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function um(){let e=lm();if(!im(e))return{};try{return JSON.parse(HR(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function qe(){let e=um().autoTag;if(!e)return{...Tr};let t=yr.safeParse({...Tr,...e});return t.success?t.data:{...Tr}}function dm(e){XR();let t=um(),n=yr.parse({...Tr,...t.autoTag??{},...e}),s={...t,autoTag:n},r=lm();BR(r,JSON.stringify(s,null,2));try{WR(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return n}function Yi(e){let{apiKey:t,...n}=e;return{...n,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}H();var wr="claude-haiku-4-5-20251001",pm=80,JR=2e3,Xt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},mm=null;async function GR(e,t){if(mm)return mm(e,t);let{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(we(),pt));return s()?n(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function YR(e){let n=_().prepare(`SELECT s.id,
1552
1600
  s.auto_title,
1553
1601
  s.auto_title_source,
1554
1602
  CASE WHEN sa.alias IS NOT NULL AND sa.alias != ''
1555
1603
  THEN 1 ELSE 0 END AS has_alias_int
1556
1604
  FROM sessions s
1557
1605
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1558
- WHERE s.id = ?`).get(e);return n?{id:n.id,auto_title:n.auto_title,auto_title_source:n.auto_title_source??null,has_alias:n.has_alias_int===1}:null}function l0(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 u0(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let n=bn({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(n){case"low_signal":case"recursive_meta":case"programmatic":return{eligible:!0};case"agent":return{eligible:!1,reason:"agent_titled"};case"manual_alias":return{eligible:!1,reason:"manual_alias"};default:return{eligible:!1,reason:"clean"}}}function d0(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(`
1559
- `)}function p0(e){let t=e.trim();if(!t)return null;let n=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")n=a.result.trim();else if(typeof a.title=="string")return Pp(a)}}catch{}let s=n.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);s&&(n=s[1].trim());let r=null;try{r=JSON.parse(n)}catch{let o=n.indexOf("{"),a=n.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(n.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:Pp(r)}function Pp(e){let t=e.title;if(typeof t!="string")return null;let n=m0(t).trim();if(!n)return null;let s=e.evidence,r=typeof s=="string"?s.trim():"";return{title:n,evidence:r}}function m0(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function g0(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=jp?t:t.slice(0,jp)}function f0(e,t){let n=e.auto_title??"";return!n&&e.has_alias&&(n=h().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:n,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function Di(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let n=c0(e);if(!n)throw new Error(`session not found: ${e}`);let s=u0(n,t.force===!0);if(!s.eligible)return f0(n,s.reason);let r=t.budget??i0,o=mr(e,{budget:r});if(l0(o))throw new Bt(e);if(t.signal?.aborted)return{session_id:e,title:n.auto_title??"",source:n.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=d0(o.bundle),c=t.model??_r,u=await a0(a,c);if(!u.success){let y=u.stderr.slice(-300);throw new Error(`claude CLI exited ${u.exitCode}: ${y||"no stderr captured"}`)}let d=p0(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 p=g0(d.title);if(!p)throw new Error("regeneration produced empty title after clamp");Ee(e,p,"agent");let S=ve(e)?.auto_title??p;return{session_id:e,title:S,source:"agent",confidence:h0(o),evidence:d.evidence||`regenerated from neighborhood (${_0(o)})`,written:!0}}function _0(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 h0(e){let t=e.parents.length,n=e.children.length,s=e.citations.length,r=e.similar.length,o=e.cousins.length,a=e.wikiLinks.length,c=.25*Math.min(1,t)+.15*Math.min(1,n)+.2*Math.min(1,s/2)+.15*Math.min(1,r/3)+.1*Math.min(1,o/3)+.15*Math.min(1,a);return Math.min(.95,c)}import{streamSSE as tt}from"hono/streaming";import{bodyLimit as $k}from"hono/body-limit";import{z as $}from"zod";q();q();bt();function jn(e){return h().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}q();function Fp(e){let t=h(),n=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1606
+ WHERE s.id = ?`).get(e);return n?{id:n.id,auto_title:n.auto_title,auto_title_source:n.auto_title_source??null,has_alias:n.has_alias_int===1}:null}function zR(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 KR(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let n=wn({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(n){case"low_signal":case"recursive_meta":case"programmatic":return{eligible:!0};case"agent":return{eligible:!1,reason:"agent_titled"};case"manual_alias":return{eligible:!1,reason:"manual_alias"};default:return{eligible:!1,reason:"clean"}}}function VR(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(`
1607
+ `)}function QR(e){let t=e.trim();if(!t)return null;let n=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")n=a.result.trim();else if(typeof a.title=="string")return gm(a)}}catch{}let s=n.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);s&&(n=s[1].trim());let r=null;try{r=JSON.parse(n)}catch{let o=n.indexOf("{"),a=n.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(n.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:gm(r)}function gm(e){let t=e.title;if(typeof t!="string")return null;let n=ZR(t).trim();if(!n)return null;let s=e.evidence,r=typeof s=="string"?s.trim():"";return{title:n,evidence:r}}function ZR(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function ek(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=pm?t:t.slice(0,pm)}function tk(e,t){let n=e.auto_title??"";return!n&&e.has_alias&&(n=_().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:n,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function zi(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let n=YR(e);if(!n)throw new Error(`session not found: ${e}`);let s=KR(n,t.force===!0);if(!s.eligible)return tk(n,s.reason);let r=t.budget??JR,o=Sr(e,{budget:r});if(zR(o))throw new Xt(e);if(t.signal?.aborted)return{session_id:e,title:n.auto_title??"",source:n.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=VR(o.bundle),c=t.model??wr,u=await GR(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=QR(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 p=ek(d.title);if(!p)throw new Error("regeneration produced empty title after clamp");he(e,p,"agent");let b=ve(e)?.auto_title??p;return{session_id:e,title:b,source:"agent",confidence:sk(o),evidence:d.evidence||`regenerated from neighborhood (${nk(o)})`,written:!0}}function nk(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 sk(e){let t=e.parents.length,n=e.children.length,s=e.citations.length,r=e.similar.length,o=e.cousins.length,a=e.wikiLinks.length,c=.25*Math.min(1,t)+.15*Math.min(1,n)+.2*Math.min(1,s/2)+.15*Math.min(1,r/3)+.1*Math.min(1,o/3)+.15*Math.min(1,a);return Math.min(.95,c)}import{streamSSE as rt}from"hono/streaming";import{bodyLimit as kA}from"hono/body-limit";import{z as P}from"zod";H();H();We();function $n(e){return _().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}H();function fm(e){let t=_(),n=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1560
1608
  (message_hash, resolved_in_session_id, fix_summary, resolved_at, unresolved_at)
1561
1609
  VALUES (?, ?, ?, ?, NULL)
1562
1610
  ON CONFLICT(message_hash) DO UPDATE SET
1563
1611
  resolved_in_session_id = excluded.resolved_in_session_id,
1564
1612
  fix_summary = excluded.fix_summary,
1565
1613
  resolved_at = excluded.resolved_at,
1566
- unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,n),E0(e.messageHash)}function $p(e){h().prepare(`UPDATE bug_signature_resolutions
1614
+ unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,n),rk(e.messageHash)}function _m(e){_().prepare(`UPDATE bug_signature_resolutions
1567
1615
  SET unresolved_at = ?
1568
- WHERE message_hash = ?`).run(new Date().toISOString(),e)}function E0(e){return h().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function Pi(e){if(e.length===0)return new Map;let t=h(),n=e.map(()=>"?").join(","),s=t.prepare(`SELECT * FROM bug_signature_resolutions
1569
- WHERE message_hash IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.message_hash,o);return r}function Fi(e){return!!e&&e.unresolved_at===null}q();function Dn(){return new Date().toISOString()}function $i(){let e=h(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1616
+ WHERE message_hash = ?`).run(new Date().toISOString(),e)}function rk(e){return _().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function Ki(e){if(e.length===0)return new Map;let t=_(),n=e.map(()=>"?").join(","),s=t.prepare(`SELECT * FROM bug_signature_resolutions
1617
+ WHERE message_hash IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.message_hash,o);return r}function Vi(e){return!!e&&e.unresolved_at===null}H();function Un(){return new Date().toISOString()}function Qi(){let e=_(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1570
1618
  FROM macro_repos
1571
1619
  ORDER BY name COLLATE NOCASE`).all();if(t.length===0)return[];let n=t.map(a=>a.id),s=n.map(()=>"?").join(","),r=e.prepare(`SELECT m.macro_repo_id, m.project_id, p.name AS project_name
1572
1620
  FROM macro_repo_members m
1573
1621
  JOIN projects p ON p.id = m.project_id
1574
1622
  WHERE m.macro_repo_id IN (${s})
1575
- ORDER BY p.name COLLATE NOCASE`).all(...n),o=new Map;for(let a of t)o.set(a.id,{...a,member_project_ids:[],member_project_names:[]});for(let a of r){let c=o.get(a.macro_repo_id);c&&(c.member_project_ids.push(a.project_id),c.member_project_names.push(a.project_name))}return Array.from(o.values())}function Wt(e){return $i().find(n=>n.id===e)??null}function Up(){return h().prepare(`SELECT p.id, p.name
1623
+ ORDER BY p.name COLLATE NOCASE`).all(...n),o=new Map;for(let a of t)o.set(a.id,{...a,member_project_ids:[],member_project_names:[]});for(let a of r){let c=o.get(a.macro_repo_id);c&&(c.member_project_ids.push(a.project_id),c.member_project_names.push(a.project_name))}return Array.from(o.values())}function Jt(e){return Qi().find(n=>n.id===e)??null}function hm(){return _().prepare(`SELECT p.id, p.name
1576
1624
  FROM projects p
1577
1625
  LEFT JOIN macro_repo_members m ON m.project_id = p.id
1578
1626
  WHERE m.project_id IS NULL
1579
- ORDER BY p.name COLLATE NOCASE`).all()}function Hp(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let n=h(),s=Dn(),r=n.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1580
- VALUES (?, ?, ?, ?)`).run(t,e.description??null,s,s),o=Number(r.lastInsertRowid);return Wt(o)}function Bp(e,t){let n=Wt(e);if(!n)throw new Error(`macro repo ${e} not found`);let s=t.name!==void 0?t.name.trim():n.name;if(!s)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:n.description;return h().prepare(`UPDATE macro_repos
1627
+ ORDER BY p.name COLLATE NOCASE`).all()}function Em(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let n=_(),s=Un(),r=n.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1628
+ VALUES (?, ?, ?, ?)`).run(t,e.description??null,s,s),o=Number(r.lastInsertRowid);return Jt(o)}function bm(e,t){let n=Jt(e);if(!n)throw new Error(`macro repo ${e} not found`);let s=t.name!==void 0?t.name.trim():n.name;if(!s)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:n.description;return _().prepare(`UPDATE macro_repos
1581
1629
  SET name = ?, description = ?, updated_at = ?
1582
- WHERE id = ?`).run(s,r,Dn(),e),Wt(e)}function Wp(e){h().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function qp(e,t){let n=h();if(!n.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!n.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);n.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1583
- VALUES (?, ?, ?)`).run(e,t,Dn()),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Dn(),e)}function Xp(e,t){let n=h();n.prepare(`DELETE FROM macro_repo_members
1584
- WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Dn(),e)}q();function Jp(e){let t=h(),n=new Date().toISOString(),s=t.prepare(`INSERT INTO bug_synthesis_results
1630
+ WHERE id = ?`).run(s,r,Un(),e),Jt(e)}function Sm(e){_().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function Tm(e,t){let n=_();if(!n.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!n.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);n.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1631
+ VALUES (?, ?, ?)`).run(e,t,Un()),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Un(),e)}function ym(e,t){let n=_();n.prepare(`DELETE FROM macro_repo_members
1632
+ WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Un(),e)}H();function wm(e){let t=_(),n=new Date().toISOString(),s=t.prepare(`INSERT INTO bug_synthesis_results
1585
1633
  (scope, target_id, mode, model, output_markdown, input_tokens, output_tokens, context_summary, created_at, job_id)
1586
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.scope,e.target_id,e.mode,e.model,e.output_markdown,e.input_tokens,e.output_tokens,JSON.stringify(e.context_summary??{}),n,e.job_id??null),r=Number(s.lastInsertRowid);return Ui(r)}function Gp(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 Ui(e){let n=h().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return n?Gp(n):null}function Yp(e={}){let t=h(),n=[],s=[];e.scope&&(n.push("scope = ?"),s.push(e.scope)),e.target_id&&(n.push("target_id = ?"),s.push(e.target_id));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1634
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.scope,e.target_id,e.mode,e.model,e.output_markdown,e.input_tokens,e.output_tokens,JSON.stringify(e.context_summary??{}),n,e.job_id??null),r=Number(s.lastInsertRowid);return Zi(r)}function Rm(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 Zi(e){let n=_().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return n?Rm(n):null}function km(e={}){let t=_(),n=[],s=[];e.scope&&(n.push("scope = ?"),s.push(e.scope)),e.target_id&&(n.push("target_id = ?"),s.push(e.target_id));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1587
1635
  ${r}
1588
1636
  ORDER BY created_at DESC
1589
- LIMIT ?`).all(...s,o).map(Gp)}function Kp(e){let n=h().prepare(`SELECT target_id, COUNT(*) AS n
1637
+ LIMIT ?`).all(...s,o).map(Rm)}function Am(e){let n=_().prepare(`SELECT target_id, COUNT(*) AS n
1590
1638
  FROM bug_synthesis_results
1591
1639
  WHERE scope = ?
1592
- GROUP BY target_id`).all(e),s=new Map;for(let r of n)s.set(r.target_id,r.n);return s}function zp(e){h().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as b0,timingSafeEqual as S0}from"node:crypto";var y0=6e4,T0=new Set(["127.0.0.1","localhost"]),Pn=new Map;function Hi(){return Date.now()}function Vp(){let e=Hi();for(let[t,n]of Pn)(n.expiresAt<=e||n.used)&&Pn.delete(t)}function De(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!T0.has(r.hostname))return e.json({error:"forbidden: cross-origin launcher request rejected"},403)}catch{return e.json({error:"forbidden: invalid Origin header"},403)}let n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.json({error:"forbidden: Sec-Fetch-Site indicates cross-origin"},403):e.req.header("x-recall-launcher")!=="1"?e.json({error:"forbidden: missing X-Recall-Launcher header (this endpoint is callable only from the Recall web UI)"},403):null}function Bi(e){Vp();let t=b0(32).toString("hex"),n=Hi()+y0;return Pn.set(t,{token:t,intent:e,expiresAt:n,used:!1}),{token:t,expiresAt:n}}function Wi(e){if(Vp(),typeof e!="string"||e.length!==64)return null;let t;try{t=Buffer.from(e,"hex")}catch{return null}if(t.length!==32)return null;for(let n of Pn.values()){if(n.used||n.expiresAt<=Hi())continue;let s;try{s=Buffer.from(n.token,"hex")}catch{continue}if(s.length===t.length&&S0(s,t))return n.used=!0,Pn.delete(n.token),n.intent}return null}import{existsSync as N0,mkdirSync as ZI,readFileSync as O0,writeFileSync as eM}from"node:fs";import{homedir as C0}from"node:os";import{join as rm}from"node:path";import{z as qi}from"zod";import{appendFileSync as w0,existsSync as Qp,mkdirSync as R0,readFileSync as k0}from"node:fs";import{homedir as A0}from"node:os";import{join as Zp}from"node:path";function em(){return process.env.RECALL_HOME??Zp(A0(),".recall")}function x0(){let e=em();Qp(e)||R0(e,{recursive:!0})}function tm(){return Zp(em(),"launcher-audit.log")}function oe(e){x0();let t={ts:new Date().toISOString(),...e};try{w0(tm(),JSON.stringify(t)+`
1593
- `,"utf8")}catch(n){console.error("[launcher-audit] failed to append:",n)}}function nm(e){let t=tm();if(!Qp(t))return{input_tokens:0,output_tokens:0,records_counted:0};let n=Date.now()-e,s=0,r=0,o=0,a;try{a=k0(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1594
- `)){if(!c.trim())continue;let u;try{u=JSON.parse(c)}catch{continue}if(u.kind!=="run-completed"&&u.kind!=="synth-completed")continue;let d=Date.parse(u.ts);!Number.isFinite(d)||d<n||(s+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:s,output_tokens:r,records_counted:o}}var L0=1440*60*1e3,v0=qi.object({dailyTokenBudget:qi.number().int().nonnegative().default(1e6),sessionCeiling:qi.number().int().positive().max(1e4).default(500)}),Xi={dailyTokenBudget:1e6,sessionCeiling:500};function I0(){return process.env.RECALL_HOME??rm(C0(),".recall")}function M0(){return rm(I0(),"config.json")}function j0(){let e=M0();if(!N0(e))return{};try{return JSON.parse(O0(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function Ji(){let e=j0().launcher;if(!e)return{...Xi};let t=v0.safeParse({...Xi,...e});return t.success?t.data:{...Xi}}function qt(){let e=Ji(),t=nm(L0),n=t.input_tokens+t.output_tokens,s=Math.max(0,e.dailyTokenBudget-n);return{daily_token_budget:e.dailyTokenBudget,session_ceiling:e.sessionCeiling,spent_input_tokens_24h:t.input_tokens,spent_output_tokens_24h:t.output_tokens,spent_total_tokens_24h:n,remaining_tokens_24h:s}}function Gi(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function Yi(e){let t=e.mode==="root_cause"?800:2e3;if(e.scope==="cluster")return{estimated_input_tokens_max:5e3+Math.min(8,Math.max(1,e.member_session_count??1))*3e3,estimated_output_tokens_max:t};let n=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*n),estimated_output_tokens_max:t}}var sm={pro:45,"max-5x":225,"max-20x":900};function D0(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var P0=4e3;function om(e){return Math.max(1,Math.ceil(e/P0))}function Ve(e,t){let n=D0(t),s=e*n,r=Object.keys(sm).map(o=>{let a=s/sm[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:n,per_plan:r,caveat:"Estimates use Anthropic\u2019s public approximate caps and assume a multiplier of ~1x for Haiku, ~5x for Sonnet, ~10x for Opus. Anthropic adjusts these limits without notice; actual consumption depends on session size. Treat as planning guidance, not a contract."}}import{randomUUID as F0}from"node:crypto";var Xt=new Map,Fn=new Map,$0=300*1e3;function hr(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function U0(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Xt.delete(e.jobId),Fn.get(e.project)===e.jobId&&Fn.delete(e.project)},$0),e.cleanupTimer.unref?.())}function Ki(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 im(e){let t=jn(e.project);if(!t)return{error:`project "${e.project}" not found`};let n=Fn.get(t.name);if(n){let p=Xt.get(n);if(p&&p.status==="running")return{jobId:n,reused:!0};Fn.delete(t.name)}let s=F0(),r=e.model??lt,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:s,project:t.name,projectId:t.id,model:r,limit:o,force:a,status:"running",startedAt:u,endedAt:null,events:[],waiters:new Set,controller:c,progress:{total:0,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0},error:null,cleanupTimer:null};return Xt.set(s,d),Fn.set(t.name,s),oe({kind:"run-launched",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await zc({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:p=>{d.progress=p,hr(d,"progress",p)},onResult:p=>{!p.ok&&!p.skipped&&hr(d,"error",{session_id:p.session_id,reason:p.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),hr(d,"done",Ki(d)),oe({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens,sessions_processed:d.progress.processed})}catch(p){let f=p instanceof Error?p.message:String(p??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=f,hr(d,"done",Ki(d)),oe({kind:"run-failed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,reason:f,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{U0(d)}})(),{jobId:s,reused:!1}}async function*am(e,t=0){let n=Xt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function cm(e){let t=Xt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function zi(e){let t=Xt.get(e);return t?Ki(t):null}q();import{randomUUID as H0}from"node:crypto";import{spawn as B0}from"node:child_process";ds();var lm="claude-haiku-4-5-20251001",Gt=new Map,Hn=new Map,W0=300*1e3;function um(e){return`${e.scope}:${e.target_id}:${e.mode}`}function $n(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function q0(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Gt.delete(e.jobId);let t=um(e.intent);Hn.get(t)===e.jobId&&Hn.delete(t)},W0),e.cleanupTimer.unref?.())}function Jt(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 dm(){return h().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1640
+ GROUP BY target_id`).all(e),s=new Map;for(let r of n)s.set(r.target_id,r.n);return s}function xm(e){_().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as ok,timingSafeEqual as ik}from"node:crypto";var ak=6e4,ck=new Set(["127.0.0.1","localhost"]),Hn=new Map;function ea(){return Date.now()}function Nm(){let e=ea();for(let[t,n]of Hn)(n.expiresAt<=e||n.used)&&Hn.delete(t)}function je(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!ck.has(r.hostname))return e.json({error:"forbidden: cross-origin launcher request rejected"},403)}catch{return e.json({error:"forbidden: invalid Origin header"},403)}let n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.json({error:"forbidden: Sec-Fetch-Site indicates cross-origin"},403):e.req.header("x-recall-launcher")!=="1"?e.json({error:"forbidden: missing X-Recall-Launcher header (this endpoint is callable only from the Recall web UI)"},403):null}function ta(e){Nm();let t=ok(32).toString("hex"),n=ea()+ak;return Hn.set(t,{token:t,intent:e,expiresAt:n,used:!1}),{token:t,expiresAt:n}}function na(e){if(Nm(),typeof e!="string"||e.length!==64)return null;let t;try{t=Buffer.from(e,"hex")}catch{return null}if(t.length!==32)return null;for(let n of Hn.values()){if(n.used||n.expiresAt<=ea())continue;let s;try{s=Buffer.from(n.token,"hex")}catch{continue}if(s.length===t.length&&ik(s,t))return n.used=!0,Hn.delete(n.token),n.intent}return null}import{existsSync as gk,mkdirSync as rD,readFileSync as fk,writeFileSync as oD}from"node:fs";import{homedir as _k}from"node:os";import{join as Dm}from"node:path";import{z as sa}from"zod";import{appendFileSync as lk,existsSync as Om,mkdirSync as uk,readFileSync as dk}from"node:fs";import{homedir as pk}from"node:os";import{join as Lm}from"node:path";function Cm(){return process.env.RECALL_HOME??Lm(pk(),".recall")}function mk(){let e=Cm();Om(e)||uk(e,{recursive:!0})}function vm(){return Lm(Cm(),"launcher-audit.log")}function oe(e){mk();let t={ts:new Date().toISOString(),...e};try{lk(vm(),JSON.stringify(t)+`
1641
+ `,"utf8")}catch(n){console.error("[launcher-audit] failed to append:",n)}}function Im(e){let t=vm();if(!Om(t))return{input_tokens:0,output_tokens:0,records_counted:0};let n=Date.now()-e,s=0,r=0,o=0,a;try{a=dk(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1642
+ `)){if(!c.trim())continue;let u;try{u=JSON.parse(c)}catch{continue}if(u.kind!=="run-completed"&&u.kind!=="synth-completed")continue;let d=Date.parse(u.ts);!Number.isFinite(d)||d<n||(s+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:s,output_tokens:r,records_counted:o}}var hk=1440*60*1e3,Ek=sa.object({dailyTokenBudget:sa.number().int().nonnegative().default(1e6),sessionCeiling:sa.number().int().positive().max(1e4).default(500)}),ra={dailyTokenBudget:1e6,sessionCeiling:500};function bk(){return process.env.RECALL_HOME??Dm(_k(),".recall")}function Sk(){return Dm(bk(),"config.json")}function Tk(){let e=Sk();if(!gk(e))return{};try{return JSON.parse(fk(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function oa(){let e=Tk().launcher;if(!e)return{...ra};let t=Ek.safeParse({...ra,...e});return t.success?t.data:{...ra}}function Gt(){let e=oa(),t=Im(hk),n=t.input_tokens+t.output_tokens,s=Math.max(0,e.dailyTokenBudget-n);return{daily_token_budget:e.dailyTokenBudget,session_ceiling:e.sessionCeiling,spent_input_tokens_24h:t.input_tokens,spent_output_tokens_24h:t.output_tokens,spent_total_tokens_24h:n,remaining_tokens_24h:s}}function ia(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function aa(e){let t=e.mode==="root_cause"?800:2e3;if(e.scope==="cluster")return{estimated_input_tokens_max:5e3+Math.min(8,Math.max(1,e.member_session_count??1))*3e3,estimated_output_tokens_max:t};let n=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*n),estimated_output_tokens_max:t}}var Mm={pro:45,"max-5x":225,"max-20x":900};function yk(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var wk=4e3;function jm(e){return Math.max(1,Math.ceil(e/wk))}function tt(e,t){let n=yk(t),s=e*n,r=Object.keys(Mm).map(o=>{let a=s/Mm[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:n,per_plan:r,caveat:"Estimates use Anthropic\u2019s public approximate caps and assume a multiplier of ~1x for Haiku, ~5x for Sonnet, ~10x for Opus. Anthropic adjusts these limits without notice; actual consumption depends on session size. Treat as planning guidance, not a contract."}}import{randomUUID as Rk}from"node:crypto";var Yt=new Map,Bn=new Map,kk=300*1e3;function Rr(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function Ak(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Yt.delete(e.jobId),Bn.get(e.project)===e.jobId&&Bn.delete(e.project)},kk),e.cleanupTimer.unref?.())}function ca(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 Pm(e){let t=$n(e.project);if(!t)return{error:`project "${e.project}" not found`};let n=Bn.get(t.name);if(n){let p=Yt.get(n);if(p&&p.status==="running")return{jobId:n,reused:!0};Bn.delete(t.name)}let s=Rk(),r=e.model??mt,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:s,project:t.name,projectId:t.id,model:r,limit:o,force:a,status:"running",startedAt:u,endedAt:null,events:[],waiters:new Set,controller:c,progress:{total:0,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0},error:null,cleanupTimer:null};return Yt.set(s,d),Bn.set(t.name,s),oe({kind:"run-launched",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await _l({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:p=>{d.progress=p,Rr(d,"progress",p)},onResult:p=>{!p.ok&&!p.skipped&&Rr(d,"error",{session_id:p.session_id,reason:p.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),Rr(d,"done",ca(d)),oe({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens,sessions_processed:d.progress.processed})}catch(p){let f=p instanceof Error?p.message:String(p??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=f,Rr(d,"done",ca(d)),oe({kind:"run-failed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,reason:f,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{Ak(d)}})(),{jobId:s,reused:!1}}async function*Fm(e,t=0){let n=Yt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function $m(e){let t=Yt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function la(e){let t=Yt.get(e);return t?ca(t):null}H();import{randomUUID as xk}from"node:crypto";import{spawn as Nk}from"node:child_process";hs();var Um="claude-haiku-4-5-20251001",Kt=new Map,Xn=new Map,Ok=300*1e3;function Hm(e){return`${e.scope}:${e.target_id}:${e.mode}`}function Wn(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function Lk(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Kt.delete(e.jobId);let t=Hm(e.intent);Xn.get(t)===e.jobId&&Xn.delete(t)},Ok),e.cleanupTimer.unref?.())}function zt(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 Bm(){return _().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1595
1643
  oi.bug_signatures
1596
1644
  FROM session_output_index oi
1597
1645
  JOIN sessions s ON s.id = oi.session_id
1598
1646
  JOIN projects p ON p.id = s.project_id
1599
1647
  WHERE oi.bug_signatures IS NOT NULL
1600
- AND oi.bug_signatures != '[]'`).all()}function pm(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function X0(e){let t=dm(),n=[];for(let a of t)for(let c of pm(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&n.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(n.length===0)return null;let s=n[0],r=Array.from(new Set(n.map(a=>a.session_id))),o=Array.from(new Set(n.map(a=>a.project))).sort();return{message_hash:s.sig.message_hash??null,error_type:s.sig.error_type??null,snippet:(s.sig.snippet??"").slice(0,400),file:s.sig.file??null,occurrence_count:n.length,projects:o,member_session_ids:r}}function J0(e){let t=dm().filter(o=>o.project===e),n=new Map;for(let o of t)for(let a of pm(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=n.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):n.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let s=new Map;try{let o=Array.from(n.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=h(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1648
+ AND oi.bug_signatures != '[]'`).all()}function Wm(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Ck(e){let t=Bm(),n=[];for(let a of t)for(let c of Wm(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&n.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(n.length===0)return null;let s=n[0],r=Array.from(new Set(n.map(a=>a.session_id))),o=Array.from(new Set(n.map(a=>a.project))).sort();return{message_hash:s.sig.message_hash??null,error_type:s.sig.error_type??null,snippet:(s.sig.snippet??"").slice(0,400),file:s.sig.file??null,occurrence_count:n.length,projects:o,member_session_ids:r}}function vk(e){let t=Bm().filter(o=>o.project===e),n=new Map;for(let o of t)for(let a of Wm(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=n.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):n.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let s=new Map;try{let o=Array.from(n.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=_(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1601
1649
  FROM bug_signature_resolutions
1602
1650
  WHERE message_hash IN (${c})
1603
- AND unresolved_at IS NULL`).all(...o);for(let d of u)s.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of n){let c=a.first.message_hash??null,u=c?s.get(c)??null:null;r.push({cluster_id:c??o,error_type:a.first.error_type??null,snippet:(a.first.snippet??"").slice(0,200),file:a.first.file??null,occurrence_count:a.sessions.size,resolved:u!==null,fix_summary:u?.fix_summary??null})}return r.sort((o,a)=>a.occurrence_count-o.occurrence_count),r}function G0(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function Y0(e,t){let n=h(),s=G0(t),r=[];for(let o of e.slice(0,8)){let a=n.prepare(`SELECT s.id, p.name AS project, s.auto_title
1651
+ AND unresolved_at IS NULL`).all(...o);for(let d of u)s.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of n){let c=a.first.message_hash??null,u=c?s.get(c)??null:null;r.push({cluster_id:c??o,error_type:a.first.error_type??null,snippet:(a.first.snippet??"").slice(0,200),file:a.first.file??null,occurrence_count:a.sessions.size,resolved:u!==null,fix_summary:u?.fix_summary??null})}return r.sort((o,a)=>a.occurrence_count-o.occurrence_count),r}function Ik(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function Mk(e,t){let n=_(),s=Ik(t),r=[];for(let o of e.slice(0,8)){let a=n.prepare(`SELECT s.id, p.name AS project, s.auto_title
1604
1652
  FROM sessions s
1605
1653
  JOIN projects p ON p.id = s.project_id
1606
1654
  WHERE s.id = ?`).get(o);if(!a)continue;let c=[];if(s.length>0){let u=n.prepare(`SELECT rowid FROM messages
@@ -1608,13 +1656,13 @@ ${n}`}function Gw(e){return ve(e)?.auto_title_source??null}async function Yw(e){
1608
1656
  AND is_sidechain = 0
1609
1657
  AND content_text LIKE ?
1610
1658
  ORDER BY rowid
1611
- LIMIT 5`).all(o,`%${s}%`),d=new Set,p=0;for(let f of u){let S=n.prepare(`SELECT rowid, role, content_text FROM messages
1659
+ LIMIT 5`).all(o,`%${s}%`),d=new Set,p=0;for(let f of u){let b=n.prepare(`SELECT rowid, role, content_text FROM messages
1612
1660
  WHERE session_id = ?
1613
1661
  AND is_sidechain = 0
1614
1662
  AND rowid BETWEEN ? AND ?
1615
- ORDER BY rowid`).all(o,f.rowid-2,f.rowid+2);for(let y of S){if(d.has(y.rowid))continue;d.add(y.rowid);let b=(y.content_text??"").slice(0,800);if(p+b.length>4e3)break;p+=b.length,c.push({role:y.role??"unknown",content:b})}if(p>=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 K0="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 z0(e,t){let n=[];n.push("[CONTEXT]"),n.push("Bug fingerprint:"),n.push(`- error_type: ${e.error_type??"unknown"}`),n.push(`- description: ${e.snippet||"(no snippet)"}`),n.push(`- file: ${e.file??"(no file recorded)"}`),n.push(`- occurrences: ${e.occurrence_count} sessions`),n.push(`- projects: ${e.projects.join(", ")}`),n.push(`- finding_id: ${e.message_hash??"(no hash)"}`),n.push(""),n.push("Member session excerpts:");for(let s of t){let r=s.auto_title??"(untitled)";if(n.push(`=== Session ${s.short_id} | ${s.project} | "${r}" ===`),s.excerpts.length===0)n.push("(no surrounding messages found for this snippet)");else for(let o of s.excerpts)n.push(`${o.role}: ${o.content}`);n.push("")}return n.join(`
1616
- `)}function V0(e,t,n){let s=[];s.push("[CONTEXT]"),s.push(`Project: ${e}`),s.push(`Total clusters: ${n}`),s.push(""),s.push("Clusters (sorted by occurrence_count desc):");for(let r of t)s.push(`- cluster_id: ${r.cluster_id}`),s.push(` error_type: ${r.error_type??"unknown"}`),s.push(` snippet: ${r.snippet||"(none)"}`),r.file&&s.push(` file: ${r.file}`),s.push(` occurrence_count: ${r.occurrence_count}`),s.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&s.push(` fix_summary: ${r.fix_summary}`),s.push("");return t.length<n&&s.push(`(Showing top ${t.length} of ${n} clusters by occurrence.)`),s.join(`
1617
- `)}var Q0=`[TASK]
1663
+ ORDER BY rowid`).all(o,f.rowid-2,f.rowid+2);for(let T of b){if(d.has(T.rowid))continue;d.add(T.rowid);let S=(T.content_text??"").slice(0,800);if(p+S.length>4e3)break;p+=S.length,c.push({role:T.role??"unknown",content:S})}if(p>=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 Dk="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 jk(e,t){let n=[];n.push("[CONTEXT]"),n.push("Bug fingerprint:"),n.push(`- error_type: ${e.error_type??"unknown"}`),n.push(`- description: ${e.snippet||"(no snippet)"}`),n.push(`- file: ${e.file??"(no file recorded)"}`),n.push(`- occurrences: ${e.occurrence_count} sessions`),n.push(`- projects: ${e.projects.join(", ")}`),n.push(`- finding_id: ${e.message_hash??"(no hash)"}`),n.push(""),n.push("Member session excerpts:");for(let s of t){let r=s.auto_title??"(untitled)";if(n.push(`=== Session ${s.short_id} | ${s.project} | "${r}" ===`),s.excerpts.length===0)n.push("(no surrounding messages found for this snippet)");else for(let o of s.excerpts)n.push(`${o.role}: ${o.content}`);n.push("")}return n.join(`
1664
+ `)}function Pk(e,t,n){let s=[];s.push("[CONTEXT]"),s.push(`Project: ${e}`),s.push(`Total clusters: ${n}`),s.push(""),s.push("Clusters (sorted by occurrence_count desc):");for(let r of t)s.push(`- cluster_id: ${r.cluster_id}`),s.push(` error_type: ${r.error_type??"unknown"}`),s.push(` snippet: ${r.snippet||"(none)"}`),r.file&&s.push(` file: ${r.file}`),s.push(` occurrence_count: ${r.occurrence_count}`),s.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&s.push(` fix_summary: ${r.fix_summary}`),s.push("");return t.length<n&&s.push(`(Showing top ${t.length} of ${n} clusters by occurrence.)`),s.join(`
1665
+ `)}var Fk=`[TASK]
1618
1666
  Output a Markdown synopsis with these sections:
1619
1667
 
1620
1668
  ## What this bug is
@@ -1636,7 +1684,7 @@ prefer "consider <pattern>" over "do <action>" unless the evidence
1636
1684
  is overwhelming.
1637
1685
 
1638
1686
  ## Confidence
1639
- One of: high / medium / low. With one sentence justifying the level.`,Z0=`[TASK]
1687
+ One of: high / medium / low. With one sentence justifying the level.`,$k=`[TASK]
1640
1688
  Output a Markdown response with these sections:
1641
1689
 
1642
1690
  ## Most likely root cause
@@ -1653,7 +1701,7 @@ list is acceptable but explicitly say "(none found)".
1653
1701
  At most 2 alternatives, each with one sentence why it is less likely.
1654
1702
 
1655
1703
  ## Confidence
1656
- high / medium / low + one-sentence justification.`,eR=`[TASK]
1704
+ high / medium / low + one-sentence justification.`,Uk=`[TASK]
1657
1705
  Output a Markdown response with these sections:
1658
1706
 
1659
1707
  ## Top 5 recurring concerns
@@ -1671,20 +1719,20 @@ Clusters that look small + high-confidence-fix. Empty list is fine.
1671
1719
  Clusters that suggest deeper issues (multiple files, repeated patterns).
1672
1720
 
1673
1721
  ## Confidence
1674
- high / medium / low.`;function tR(e,t){let n;return e.scope==="cluster"?n=e.mode==="root_cause"?Z0:Q0:n=eR,[K0,"",t,"",n].join(`
1675
- `)}var nR=null;var Un;function sR(){return Un!==void 0||(Un=kt("claude")??"claude"),Un}function rR(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
1722
+ high / medium / low.`;function Hk(e,t){let n;return e.scope==="cluster"?n=e.mode==="root_cause"?$k:Fk:n=Uk,[Dk,"",t,"",n].join(`
1723
+ `)}var Bk=null;var qn;function Wk(){return qn!==void 0||(qn=xt("claude")??"claude"),qn}function qk(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
1676
1724
  `);for(;s!==-1;){let r=t.slice(0,s);t=t.slice(s+1),r.length>0&&e(r),s=t.indexOf(`
1677
- `)}}}function oR(e){return new Promise(t=>{let n=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],s="",r=0,o=0,a=null,c=sR(),u=B0(c,n,{stdio:["ignore","pipe","pipe"],shell:us(c)||process.platform==="win32"&&Un==="claude"}),d=()=>{try{u.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",d,{once:!0});let f=rR(y=>{let b=y.trim();if(!b.startsWith("{"))return;let R;try{R=JSON.parse(b)}catch{return}if(!R||typeof R!="object")return;let k=R;if(k.type==="assistant"&&k.message?.content){for(let P of k.message.content)P?.type==="text"&&typeof P.text=="string"&&(s+=P.text,e.onPartial(P.text,s));let M=k.message?.usage;M&&(typeof M.input_tokens=="number"&&(r=Math.max(r,M.input_tokens)),typeof M.output_tokens=="number"&&(o=Math.max(o,M.output_tokens)))}});u.stdout.on("data",y=>f(y)),u.stderr.on("data",y=>{let b=y.toString("utf8");b.trim()&&(a=(a??"")+b)});let S=setTimeout(()=>{try{u.kill("SIGKILL")}catch{}},1800*1e3);u.on("close",y=>{clearTimeout(S),e.signal.removeEventListener("abort",d);let b=y===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${y}`);t({output_markdown:s,input_tokens:r,output_tokens:o,error:b})}),u.on("error",y=>{clearTimeout(S),e.signal.removeEventListener("abort",d),t({output_markdown:s,input_tokens:r,output_tokens:o,error:y instanceof Error?y.message:String(y)})})})}function Er(e){if(e.scope==="cluster"){let r=X0(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=J0(e.target_id);if(t.length===0)return null;let n=t.slice(0,30),s=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:n,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:s}}}function mm(e){let t=Er(e.intent);if(!t)return{error:e.intent.scope==="cluster"?`cluster "${e.intent.target_id}" not found in any extracted findings`:`project "${e.intent.target_id}" has no extracted findings to synthesize`};let n=um(e.intent),s=Hn.get(n);if(s){let u=Gt.get(s);if(u&&u.status==="running")return{jobId:s,reused:!0};Hn.delete(n)}let r=H0(),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 Gt.set(r,c),Hn.set(n,r),oe({kind:"synth-launched",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:`${e.intent.scope}/${e.intent.mode}/${e.intent.target_id}`}),(async()=>{await Promise.resolve();try{let u;if(e.intent.scope==="cluster"&&t.cluster){let S=Y0(t.cluster.member_session_ids,t.cluster.snippet);u=z0(t.cluster,S)}else if(e.intent.scope==="project"&&t.project_clusters)u=V0(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=tR(e.intent,u),f=await(nR??oR)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(S,y)=>{c.output_markdown=y,$n(c,"partial",Jt(c))}});if(c.output_markdown=f.output_markdown,c.input_tokens=f.input_tokens,c.output_tokens=f.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),$n(c,"done",Jt(c)),oe({kind:"synth-cancelled",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else if(f.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=f.error,$n(c,"done",Jt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:f.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),$n(c,"done",Jt(c)),oe({kind:"synth-completed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});try{Jp({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(S){console.error("[synthesize-jobs] failed to persist synthesis result:",S)}}}catch(u){let d=u instanceof Error?u.message:String(u??"unknown error");c.status="failed",c.endedAt=new Date().toISOString(),c.error=d,$n(c,"done",Jt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:d,input_tokens:c.input_tokens,output_tokens:c.output_tokens})}finally{q0(c)}})(),{jobId:r,reused:!1}}async function*gm(e,t=0){let n=Gt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function fm(e){let t=Gt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Vi(e){let t=Gt.get(e);return t?Jt(t):null}import{randomUUID as ER}from"node:crypto";q();ys();import{randomUUID as gR}from"node:crypto";q();var _m=10,hm=20;function iR(e){if(!e)return!1;let t=e.split(`
1678
- `);if(t.length<4)return!1;let n=0,s=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?n++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&s++;return n/t.length>.7||s/t.length>.5}function aR(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let n=new Float32Array(t),s=new Float32Array(e.buffer,e.byteOffset,t);return n.set(s),n}function Em(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];if(t<=0)return!1;let n=1/Math.sqrt(t);for(let s=0;s<e.length;s++)e[s]*=n;return!0}function Qi(e){if(e.length===0)return null;let t=e[0].length,n=new Float32Array(t);for(let s of e)if(s.length===t)for(let r=0;r<t;r++)n[r]+=s[r];for(let s=0;s<t;s++)n[s]/=e.length;return Em(n)?n:null}function bm(e){let t=new Map;if(e.length===0)return t;let n=h(),s=e.map(()=>"?").join(","),r=[];try{r=n.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1725
+ `)}}}function Xk(e){return new Promise(t=>{let n=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],s="",r=0,o=0,a=null,c=Wk(),u=Nk(c,n,{stdio:["ignore","pipe","pipe"],shell:_s(c)||process.platform==="win32"&&qn==="claude"}),d=()=>{try{u.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",d,{once:!0});let f=qk(T=>{let S=T.trim();if(!S.startsWith("{"))return;let w;try{w=JSON.parse(S)}catch{return}if(!w||typeof w!="object")return;let A=w;if(A.type==="assistant"&&A.message?.content){for(let j of A.message.content)j?.type==="text"&&typeof j.text=="string"&&(s+=j.text,e.onPartial(j.text,s));let I=A.message?.usage;I&&(typeof I.input_tokens=="number"&&(r=Math.max(r,I.input_tokens)),typeof I.output_tokens=="number"&&(o=Math.max(o,I.output_tokens)))}});u.stdout.on("data",T=>f(T)),u.stderr.on("data",T=>{let S=T.toString("utf8");S.trim()&&(a=(a??"")+S)});let b=setTimeout(()=>{try{u.kill("SIGKILL")}catch{}},1800*1e3);u.on("close",T=>{clearTimeout(b),e.signal.removeEventListener("abort",d);let S=T===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${T}`);t({output_markdown:s,input_tokens:r,output_tokens:o,error:S})}),u.on("error",T=>{clearTimeout(b),e.signal.removeEventListener("abort",d),t({output_markdown:s,input_tokens:r,output_tokens:o,error:T instanceof Error?T.message:String(T)})})})}function kr(e){if(e.scope==="cluster"){let r=Ck(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=vk(e.target_id);if(t.length===0)return null;let n=t.slice(0,30),s=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:n,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:s}}}function qm(e){let t=kr(e.intent);if(!t)return{error:e.intent.scope==="cluster"?`cluster "${e.intent.target_id}" not found in any extracted findings`:`project "${e.intent.target_id}" has no extracted findings to synthesize`};let n=Hm(e.intent),s=Xn.get(n);if(s){let u=Kt.get(s);if(u&&u.status==="running")return{jobId:s,reused:!0};Xn.delete(n)}let r=xk(),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 Kt.set(r,c),Xn.set(n,r),oe({kind:"synth-launched",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:`${e.intent.scope}/${e.intent.mode}/${e.intent.target_id}`}),(async()=>{await Promise.resolve();try{let u;if(e.intent.scope==="cluster"&&t.cluster){let b=Mk(t.cluster.member_session_ids,t.cluster.snippet);u=jk(t.cluster,b)}else if(e.intent.scope==="project"&&t.project_clusters)u=Pk(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=Hk(e.intent,u),f=await(Bk??Xk)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(b,T)=>{c.output_markdown=T,Wn(c,"partial",zt(c))}});if(c.output_markdown=f.output_markdown,c.input_tokens=f.input_tokens,c.output_tokens=f.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),Wn(c,"done",zt(c)),oe({kind:"synth-cancelled",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else if(f.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=f.error,Wn(c,"done",zt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:f.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),Wn(c,"done",zt(c)),oe({kind:"synth-completed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});try{wm({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,Wn(c,"done",zt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:d,input_tokens:c.input_tokens,output_tokens:c.output_tokens})}finally{Lk(c)}})(),{jobId:r,reused:!1}}async function*Xm(e,t=0){let n=Kt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Jm(e){let t=Kt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function ua(e){let t=Kt.get(e);return t?zt(t):null}import{randomUUID as r0}from"node:crypto";H();xs();import{randomUUID as e0}from"node:crypto";H();var Gm=10,Ym=20;function Jk(e){if(!e)return!1;let t=e.split(`
1726
+ `);if(t.length<4)return!1;let n=0,s=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?n++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&s++;return n/t.length>.7||s/t.length>.5}function Gk(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let n=new Float32Array(t),s=new Float32Array(e.buffer,e.byteOffset,t);return n.set(s),n}function zm(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];if(t<=0)return!1;let n=1/Math.sqrt(t);for(let s=0;s<e.length;s++)e[s]*=n;return!0}function da(e){if(e.length===0)return null;let t=e[0].length,n=new Float32Array(t);for(let s of e)if(s.length===t)for(let r=0;r<t;r++)n[r]+=s[r];for(let s=0;s<t;s++)n[s]/=e.length;return zm(n)?n:null}function Km(e){let t=new Map;if(e.length===0)return t;let n=_(),s=e.map(()=>"?").join(","),r=[];try{r=n.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1679
1727
  cm.text AS text, v.embedding AS embedding
1680
1728
  FROM chunk_meta cm
1681
1729
  JOIN vec_chunks v ON v.rowid = cm.rowid
1682
1730
  WHERE cm.stale = 0
1683
1731
  AND cm.session_id IN (${s})
1684
- 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=>!iR(D.text)),d=u.length>0?u:c,p=[];for(let D of d){let v=aR(D.embedding);v&&Em(v)&&p.push(v)}if(p.length===0)continue;let f=Math.min(_m,p.length),S=Math.max(0,p.length-_m),y=p.slice(0,f),b=p.slice(S),R=Qi(y),k=Qi(b),M=Qi(p),P=new Map;for(let D=0;D<y.length&&P.size<hm;D++)P.set(D,y[D]);for(let D=0;D<b.length&&P.size<hm;D++)P.set(S+D,b[D]);let G=Array.from(P.values());t.set(a,{session_id:a,full_mean:M,head_pool:R,tail_pool:k,sample_chunks:G})}return t}function cR(e,t){if(e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function Sm(e,t=.8){let n=new Map,s=[],r=[];for(let[d,p]of e)p.full_mean&&(s.push(d),r.push(p.full_mean));if(s.length===0)return n;let o=new Int32Array(s.length);for(let d=0;d<o.length;d++)o[d]=d;let a=d=>{let p=d;for(;o[p]!==p;)p=o[p];let f=d;for(;o[f]!==p;){let S=o[f];o[f]=p,f=S}return p},c=(d,p)=>{let f=a(d),S=a(p);f!==S&&(o[f]=S)};for(let d=0;d<s.length;d++)for(let p=d+1;p<s.length;p++)cR(r[d],r[p])>=t&&c(d,p);let u=new Map;for(let d=0;d<s.length;d++){let p=a(d),f=u.get(p);f===void 0&&(f=u.size,u.set(p,f)),n.set(s[d],f)}return n}var We={lo:.4,hi:.7},Yt="claude-haiku-4-5-20251001",Kt=50,ym=1500,Tm=50,lR={same_workflow:.15,unrelated:-.2,unsure:0};function wm(e,t,n=We){if(n.lo>n.hi)throw new Error(`borderline band invalid: lo=${n.lo} > hi=${n.hi}`);let s=[];for(let r of e){if(r.confidence<n.lo||r.confidence>n.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||s.push({parent:o,child:a,step1:r})}return s.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),s}function Rm(e,t=We){let n=0;for(let s of e)s.confidence>=t.lo&&s.confidence<=t.hi&&n++;return n}function uR(e,t){let n=e.replace(/\s+/g," ").trim();return n.length>t?n.slice(0,t-1)+"\u2026":n}function dR(e,t){if(e===null)return"unknown";let n=t-e;if(n<0)return"overlap";let s=Math.round(n/6e4);if(s<60)return`${s}m`;let r=Math.round(n/36e5);return r<24?`${r}h`:`${Math.round(n/864e5)}d`}function pR(e){let n=e.parent.recent_user_messages,s=e.child.recent_user_messages,r=n.slice(0,3),o=n.length>3?n.slice(-3):[],a=s.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(p=>` - ${uR(p,500)}`).join(`
1685
- `),u=dR(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(`
1732
+ 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=>!Jk(M.text)),d=u.length>0?u:c,p=[];for(let M of d){let F=Gk(M.embedding);F&&zm(F)&&p.push(F)}if(p.length===0)continue;let f=Math.min(Gm,p.length),b=Math.max(0,p.length-Gm),T=p.slice(0,f),S=p.slice(b),w=da(T),A=da(S),I=da(p),j=new Map;for(let M=0;M<T.length&&j.size<Ym;M++)j.set(M,T[M]);for(let M=0;M<S.length&&j.size<Ym;M++)j.set(b+M,S[M]);let J=Array.from(j.values());t.set(a,{session_id:a,full_mean:I,head_pool:w,tail_pool:A,sample_chunks:J})}return t}function Yk(e,t){if(e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function Vm(e,t=.8){let n=new Map,s=[],r=[];for(let[d,p]of e)p.full_mean&&(s.push(d),r.push(p.full_mean));if(s.length===0)return n;let o=new Int32Array(s.length);for(let d=0;d<o.length;d++)o[d]=d;let a=d=>{let p=d;for(;o[p]!==p;)p=o[p];let f=d;for(;o[f]!==p;){let b=o[f];o[f]=p,f=b}return p},c=(d,p)=>{let f=a(d),b=a(p);f!==b&&(o[f]=b)};for(let d=0;d<s.length;d++)for(let p=d+1;p<s.length;p++)Yk(r[d],r[p])>=t&&c(d,p);let u=new Map;for(let d=0;d<s.length;d++){let p=a(d),f=u.get(p);f===void 0&&(f=u.size,u.set(p,f)),n.set(s[d],f)}return n}var Xe={lo:.4,hi:.7},Vt="claude-haiku-4-5-20251001",Qt=50,Qm=1500,Zm=50,zk={same_workflow:.15,unrelated:-.2,unsure:0};function eg(e,t,n=Xe){if(n.lo>n.hi)throw new Error(`borderline band invalid: lo=${n.lo} > hi=${n.hi}`);let s=[];for(let r of e){if(r.confidence<n.lo||r.confidence>n.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||s.push({parent:o,child:a,step1:r})}return s.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),s}function tg(e,t=Xe){let n=0;for(let s of e)s.confidence>=t.lo&&s.confidence<=t.hi&&n++;return n}function Kk(e,t){let n=e.replace(/\s+/g," ").trim();return n.length>t?n.slice(0,t-1)+"\u2026":n}function Vk(e,t){if(e===null)return"unknown";let n=t-e;if(n<0)return"overlap";let s=Math.round(n/6e4);if(s<60)return`${s}m`;let r=Math.round(n/36e5);return r<24?`${r}h`:`${Math.round(n/864e5)}d`}function Qk(e){let n=e.parent.recent_user_messages,s=e.child.recent_user_messages,r=n.slice(0,3),o=n.length>3?n.slice(-3):[],a=s.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(p=>` - ${Kk(p,500)}`).join(`
1733
+ `),u=Vk(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(`
1686
1734
  `),` 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(`
1687
- `)}function mR(e){if(!e)return null;let t=e.trim();if(!t)return null;let n=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(n=u.result.trim())}catch{}let s=n.match(/\{[\s\S]*\}/);if(!s)return null;let r;try{r=JSON.parse(s[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,a=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(a!=="same_workflow"&&a!=="unrelated"&&a!=="unsure")return null;let c=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:a,reason:c,delta:lR[a]}}async function km(e,t={}){if(t.signal?.aborted)return null;let n=t.model??Yt,s=pR(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(Te(),ct));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(s,{model:n}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:mR(o.stdout)}function Am(e){let t=[];for(let n of e.proposals){let s=`${n.parent_id}::${n.child_id}`,r=e.rescored.get(s);if(!r){t.push(n);continue}let o=Math.max(0,Math.min(1,n.confidence+r.delta));if(o<e.applyThreshold)continue;let a=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...n,confidence:o,reasons:[...n.reasons,a]})}return t}function xm(e){return`${e.parent_id}::${e.child_id}`}async function fR(e){if(e.signal?.aborted)return null;let t,n;try{({spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(Te(),ct)))}catch{return null}if(!n())return null;let s=[];for(let o of e.sessionIds){let a=e.rowById.get(o);if(!a)continue;let c=a.alias||a.auto_title||(a.first_user_message?a.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");s.push(`- ${c}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1735
+ `)}function Zk(e){if(!e)return null;let t=e.trim();if(!t)return null;let n=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(n=u.result.trim())}catch{}let s=n.match(/\{[\s\S]*\}/);if(!s)return null;let r;try{r=JSON.parse(s[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,a=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(a!=="same_workflow"&&a!=="unrelated"&&a!=="unsure")return null;let c=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:a,reason:c,delta:zk[a]}}async function ng(e,t={}){if(t.signal?.aborted)return null;let n=t.model??Vt,s=Qk(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(we(),pt));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(s,{model:n}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:Zk(o.stdout)}function sg(e){let t=[];for(let n of e.proposals){let s=`${n.parent_id}::${n.child_id}`,r=e.rescored.get(s);if(!r){t.push(n);continue}let o=Math.max(0,Math.min(1,n.confidence+r.delta));if(o<e.applyThreshold)continue;let a=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...n,confidence:o,reasons:[...n.reasons,a]})}return t}function rg(e){return`${e.parent_id}::${e.child_id}`}async function t0(e){if(e.signal?.aborted)return null;let t,n;try{({spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(we(),pt)))}catch{return null}if(!n())return null;let s=[];for(let o of e.sessionIds){let a=e.rowById.get(o);if(!a)continue;let c=a.alias||a.auto_title||(a.first_user_message?a.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");s.push(`- ${c}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1688
1736
  - 4 to 8 words
1689
1737
  - Title-case, no trailing punctuation
1690
1738
  - Capture the WORKFLOW (e.g., "Update Remotion deps + lint cleanup"), not any one session
@@ -1695,12 +1743,12 @@ Sessions:
1695
1743
  `+s.join(`
1696
1744
  `)+`
1697
1745
 
1698
- Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(f=>{e.signal&&(a=()=>f(null),e.signal.addEventListener("abort",a,{once:!0}))}),u=await Promise.race([o,c]);if(a&&e.signal&&e.signal.removeEventListener("abort",a),!u||!u.success)return null;let d=u.stdout.trim();if(!d)return null;let p;try{let f=JSON.parse(d);p=typeof f.result=="string"?f.result:d}catch{p=d}return p=p.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),p?p.length>80?p.slice(0,77)+"...":p:null}catch{return null}}function Zi(e){return _R(e)}function ea(e){let t=new Map;for(let o of e){let a=t.get(o.project);a||(a=[],t.set(o.project,a)),a.push(o)}let n=e.map(o=>o.id),s=new Map;try{s=bm(n)}catch{s=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let p of a){let f=s.get(p.id);f&&c.set(p.id,f)}let u=Sm(c,.8),d=[];for(let p of a){let f=hR(p,s,u);f&&d.push(f)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function Om(e){return h().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1746
+ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(f=>{e.signal&&(a=()=>f(null),e.signal.addEventListener("abort",a,{once:!0}))}),u=await Promise.race([o,c]);if(a&&e.signal&&e.signal.removeEventListener("abort",a),!u||!u.success)return null;let d=u.stdout.trim();if(!d)return null;let p;try{let f=JSON.parse(d);p=typeof f.result=="string"?f.result:d}catch{p=d}return p=p.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),p?p.length>80?p.slice(0,77)+"...":p:null}catch{return null}}function pa(e){return n0(e)}function ma(e){let t=new Map;for(let o of e){let a=t.get(o.project);a||(a=[],t.set(o.project,a)),a.push(o)}let n=e.map(o=>o.id),s=new Map;try{s=Km(n)}catch{s=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let p of a){let f=s.get(p.id);f&&c.set(p.id,f)}let u=Vm(c,.8),d=[];for(let p of a){let f=s0(p,s,u);f&&d.push(f)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function ig(e){return _().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1699
1747
  FROM threads t
1700
1748
  JOIN thread_edges te ON te.thread_id = t.id
1701
1749
  JOIN sessions s ON s.id = te.session_id
1702
1750
  JOIN projects p ON p.id = s.project_id
1703
- WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function _R(e){let t=h(),n={},s="1=1 AND s.message_count > 2";return s+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(s+=" AND p.name = @project",n.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1751
+ WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function n0(e){let t=_(),n={},s="1=1 AND s.message_count > 2";return s+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(s+=" AND p.name = @project",n.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1704
1752
  s.first_user_message, s.auto_title,
1705
1753
  NULLIF(sa.alias, '') AS alias,
1706
1754
  s.file_path
@@ -1708,21 +1756,21 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1708
1756
  JOIN projects p ON p.id = s.project_id
1709
1757
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1710
1758
  WHERE ${s}
1711
- ORDER BY p.name ASC, s.started_at ASC`).all(n)}function hR(e,t,n){if(!e.started_at)return null;let s=Date.parse(e.started_at);if(!Number.isFinite(s))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=lr(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:s,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:a?.full_mean??null,head_pool:a?.head_pool??null,tail_pool:a?.tail_pool??null,sample_chunks:a?.sample_chunks??[],cluster_id:n?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function Nm(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 Cm(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??We,n=e.rescore.cap??Kt,s=e.rescore.model??Yt,r=new Map(e.scannables.map(S=>[S.id,S])),o=wm(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>n)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let a=new Map,c=0,u=0,d=0,p=0;for(let S=0;S<o.length&&!e.signal?.aborted;S++){let y=o[S],b=await km(y,{model:s,signal:e.signal});b?(a.set(xm(y.step1),b),b.verdict==="same_workflow"?c++:b.verdict==="unrelated"?u++:d++):p++,e.onProgress?.({phase:"rescoring",current:S+1,total:o.length,verdict:b?.verdict??"failed"})}return{proposals:Am({proposals:e.proposals,rescored:a,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:c,demoted:u,unsure:d,failed:p,capped:!1}}async function Lm(e){let t=h(),n=new Map(e.rows.map(d=>[d.id,d])),s=Zd(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of s){if(c++,e.signal?.aborted)break;let p=n.get(d.rootId),f=Nm(p);if(!e.llmNames){a.set(d.rootId,f),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:f});continue}let y=await fR({rootRow:p,sessionIds:d.sessionIds,rowById:n,signal:e.signal,model:e.model})??f;a.set(d.rootId,y),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:y})}return t.transaction(()=>{for(let d of s){if(!a.has(d.rootId))continue;let p=n.get(d.rootId),f=`auto-scan-${gR()}`,S=a.get(d.rootId)??Nm(p),y=`Auto-detected workflow chain (${d.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(f,S,y,r),t.prepare(`INSERT INTO thread_edges
1759
+ ORDER BY p.name ASC, s.started_at ASC`).all(n)}function s0(e,t,n){if(!e.started_at)return null;let s=Date.parse(e.started_at);if(!Number.isFinite(s))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=_r(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:s,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:a?.full_mean??null,head_pool:a?.head_pool??null,tail_pool:a?.tail_pool??null,sample_chunks:a?.sample_chunks??[],cluster_id:n?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function og(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 ag(e){if(!e.rescore.enabled)return{proposals:e.proposals,ran:!1,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};let t=e.rescore.band??Xe,n=e.rescore.cap??Qt,s=e.rescore.model??Vt,r=new Map(e.scannables.map(b=>[b.id,b])),o=eg(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>n)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let a=new Map,c=0,u=0,d=0,p=0;for(let b=0;b<o.length&&!e.signal?.aborted;b++){let T=o[b],S=await ng(T,{model:s,signal:e.signal});S?(a.set(rg(T.step1),S),S.verdict==="same_workflow"?c++:S.verdict==="unrelated"?u++:d++):p++,e.onProgress?.({phase:"rescoring",current:b+1,total:o.length,verdict:S?.verdict??"failed"})}return{proposals:sg({proposals:e.proposals,rescored:a,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:c,demoted:u,unsure:d,failed:p,capped:!1}}async function cg(e){let t=_(),n=new Map(e.rows.map(d=>[d.id,d])),s=Lp(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of s){if(c++,e.signal?.aborted)break;let p=n.get(d.rootId),f=og(p);if(!e.llmNames){a.set(d.rootId,f),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:f});continue}let T=await t0({rootRow:p,sessionIds:d.sessionIds,rowById:n,signal:e.signal,model:e.model})??f;a.set(d.rootId,T),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:T})}return t.transaction(()=>{for(let d of s){if(!a.has(d.rootId))continue;let p=n.get(d.rootId),f=`auto-scan-${e0()}`,b=a.get(d.rootId)??og(p),T=`Auto-detected workflow chain (${d.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(f,b,T,r),t.prepare(`INSERT INTO thread_edges
1712
1760
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1713
- VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(f,d.rootId,r);let b=new Map;for(let R of e.edges)d.sessionIds.includes(R.child_id)&&b.set(R.child_id,R);for(let R of d.sessionIds){if(R===d.rootId)continue;let k=b.get(R);k&&t.prepare(`INSERT INTO thread_edges
1761
+ VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(f,d.rootId,r);let S=new Map;for(let w of e.edges)d.sessionIds.includes(w.child_id)&&S.set(w.child_id,w);for(let w of d.sessionIds){if(w===d.rootId)continue;let A=S.get(w);A&&t.prepare(`INSERT INTO thread_edges
1714
1762
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1715
- VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(f,R,k.parent_id,k.confidence,r)}o.push({thread_id:f,name:S,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var Vt=new Map,Bn=new Map,bR=300*1e3;function zt(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function SR(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Vt.delete(e.jobId),Bn.get(e.project)===e.jobId&&Bn.delete(e.project)},bR),e.cleanupTimer.unref?.())}function br(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 yR=500,TR=30,ta="claude-haiku-4-5-20251001",vm={"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 Im(e){return vm[e]??vm[ta]}function Mm(e){let t=typeof e.threshold=="number"?e.threshold:ar;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.model??ta,s="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??We.lo,hi:e.llm_rescore?.band_hi??We.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??Yt,c=Zi({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:Ve(0,n),accuracy_caveat:s,model:n,llm_rescore:r?{enabled:!0,band:o,cap:Kt,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:Ve(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=ea(c),p=u.get(e.project)??[],f=d.get(e.project)??[],S=r?Math.min(t,o.lo):t,y=Ut(f,S),b=r?y.filter(i=>i.confidence>=t):y,R=jm(b),k=R*yR,M=R*TR,P=Im(n),G=k/1e6*P.in,D=M/1e6*P.out,v=G+D,F=Ve(R,n),U=Om(e.project),L=null;if(r){let i=Rm(y,o),l=Im(a),m=i*ym,g=i*Tm,_=m/1e6*l.in+g/1e6*l.out;L={enabled:!0,band:o,cap:Kt,estimated_borderline_pairs:i,estimated_input_tokens_max:m,estimated_output_tokens_max:g,estimated_cost_usd_max:Math.round(_*1e4)/1e4,plan_window_estimate:Ve(i,a),exceeds_cap:i>Kt,model:a}}return{eligible_sessions:p.length,proposed_edges:b.length,estimated_threads:R,estimated_llm_calls:R,estimated_input_tokens_max:k,estimated_output_tokens_max:M,estimated_cost_usd_max:Math.round(v*1e4)/1e4,existing_auto_scan_threads:U,plan_window_estimate:F,accuracy_caveat:s,model:n,llm_rescore:L}}function jm(e){let t=new Map,n=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},s=(o,a)=>{let c=n(o),u=n(a);c!==u&&t.set(c,u)};for(let o of e)t.has(o.parent_id)||t.set(o.parent_id,o.parent_id),t.has(o.child_id)||t.set(o.child_id,o.child_id),s(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(n(o));return r.size}function Dm(e){let t=typeof e.threshold=="number"?e.threshold:ar;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.llm_names??!0,s=e.model??ta,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??We.lo,hi:e.llm_rescore?.band_hi??We.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??Yt,c=Bn.get(e.project);if(c){let v=Vt.get(c);if(v&&v.status==="running")return{jobId:c,reused:!0};Bn.delete(e.project)}let u=Zi({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:p}=ea(u),f=d.get(e.project),S=p.get(e.project);if(!f||!S)return{error:`project "${e.project}" not found`};let y=r?Math.min(t,o.lo):t,b=Ut(S,y),R=r?b.filter(v=>v.confidence>=t):b;if(b.length===0||R.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let k=ER(),M=new AbortController,P=jm(R),G=new Date().toISOString(),D={jobId:k,project:e.project,threshold:t,llmNames:n,status:"running",startedAt:G,endedAt:null,events:[],waiters:new Set,controller:M,totalThreads:P,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(k,D),Bn.set(e.project,k),(async()=>{await Promise.resolve();try{zt(D,"progress",{phase:"linking",edges_proposed:b.length,estimated_threads:P});let v=b;if(r){let U=await Cm({proposals:b,scannables:S,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:M.signal,onProgress:L=>{L.phase==="rescoring"&&zt(D,"progress",{phase:"rescoring",current:L.current,total:L.total,verdict:L.verdict})}});v=U.proposals,D.rescore={enabled:!0,considered:U.considered,promoted:U.promoted,demoted:U.demoted,unsure:U.unsure,failed:U.failed,capped:U.capped}}else v=b.filter(U=>U.confidence>=t);if(v.length===0){D.status=M.signal.aborted?"cancelled":"done",D.endedAt=new Date().toISOString(),zt(D,"done",{...br(D),threads:[]});return}let F=await Lm({project:e.project,rows:f,edges:v,scannables:S,llmNames:n,model:s,signal:M.signal,onProgress:U=>{U.phase==="naming"&&(D.threadsNamed=U.current,D.currentThreadName=U.thread_name??null,zt(D,"progress",{phase:"naming",current:U.current,total:U.total,thread_name:U.thread_name}))}});D.threadsCreated=F.threads.length,D.edgesWritten=v.length+F.threads.length,D.status=M.signal.aborted?"cancelled":"done",D.endedAt=new Date().toISOString(),zt(D,"done",{...br(D),threads:F.threads})}catch(v){D.status="failed",D.endedAt=new Date().toISOString(),D.error=v instanceof Error?v.message:String(v??"unknown error"),zt(D,"done",br(D))}finally{SR(D)}})(),{jobId:k,reused:!1}}async function*Pm(e,t=0){let n=Vt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Fm(e){let t=Vt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function na(e){let t=Vt.get(e);return t?br(t):null}cs();import{randomUUID as wR}from"node:crypto";var Sr=new Map;function $m(e){let t={id:wR(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return Sr.set(t.id,t),t}function yr(e){return Sr.get(e)}function Qt(e,t){for(let n of e.listeners)n(t)}function Um(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function Hm(e){let t=Sr.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),Qt(t,{type:"status",status:"cancelled"}),!0):!1}function Bm(e){return Sr.delete(e)}wt();function Tr(e){let{session:t,knownTags:n,minTags:s,maxTags:r}=e,o=n.slice(0,50).map(c=>c.tag).join(", "),a=t.current_tags.length>0?`already applied, do not repeat: [${t.current_tags.join(", ")}]`:"currently has no tags";return[`You are tagging a software engineering session. Produce ${s}-${r} concise, lowercase, hyphen-separated tags that describe:`," - the domain or subsystem touched (auth, db, frontend, ...)"," - the kind of work (bugfix, feature, refactor, research, ...)"," - any specific tool, library, or product if prominent","","Prefer tags from the known-tags list below when applicable \u2014 consistency over creativity. Never invent marketing-sounding labels. Never tag based on speculation; only on observed work.","",`known tags (most used first, up to 50): ${o||"(none yet)"}`,"","Session:",` project: ${t.project}`,t.alias?` alias: ${JSON.stringify(t.alias)}`:" alias: (none)",t.git_branch?` git_branch: ${t.git_branch}`:"",` ${a}`," first_user_message:",Wm(t.first_user_message," ")," message_sample:",Wm(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${s}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1716
- `)}function Wm(e,t){return e.split(`
1763
+ VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(f,w,A.parent_id,A.confidence,r)}o.push({thread_id:f,name:b,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var en=new Map,Jn=new Map,o0=300*1e3;function Zt(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function i0(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{en.delete(e.jobId),Jn.get(e.project)===e.jobId&&Jn.delete(e.project)},o0),e.cleanupTimer.unref?.())}function Ar(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 a0=500,c0=30,ga="claude-haiku-4-5-20251001",lg={"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 ug(e){return lg[e]??lg[ga]}function dg(e){let t=typeof e.threshold=="number"?e.threshold:gr;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.model??ga,s="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Xe.lo,hi:e.llm_rescore?.band_hi??Xe.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??Vt,c=pa({project:e.project});if(c.length===0)return{eligible_sessions:0,proposed_edges:0,estimated_threads:0,estimated_llm_calls:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,existing_auto_scan_threads:0,plan_window_estimate:tt(0,n),accuracy_caveat:s,model:n,llm_rescore:r?{enabled:!0,band:o,cap:Qt,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:tt(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=ma(c),p=u.get(e.project)??[],f=d.get(e.project)??[],b=r?Math.min(t,o.lo):t,T=Wt(f,b),S=r?T.filter(i=>i.confidence>=t):T,w=pg(S),A=w*a0,I=w*c0,j=ug(n),J=A/1e6*j.in,M=I/1e6*j.out,F=J+M,ne=tt(w,n),U=ig(e.project),v=null;if(r){let i=tg(T,o),l=ug(a),m=i*Qm,g=i*Zm,h=m/1e6*l.in+g/1e6*l.out;v={enabled:!0,band:o,cap:Qt,estimated_borderline_pairs:i,estimated_input_tokens_max:m,estimated_output_tokens_max:g,estimated_cost_usd_max:Math.round(h*1e4)/1e4,plan_window_estimate:tt(i,a),exceeds_cap:i>Qt,model:a}}return{eligible_sessions:p.length,proposed_edges:S.length,estimated_threads:w,estimated_llm_calls:w,estimated_input_tokens_max:A,estimated_output_tokens_max:I,estimated_cost_usd_max:Math.round(F*1e4)/1e4,existing_auto_scan_threads:U,plan_window_estimate:ne,accuracy_caveat:s,model:n,llm_rescore:v}}function pg(e){let t=new Map,n=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},s=(o,a)=>{let c=n(o),u=n(a);c!==u&&t.set(c,u)};for(let o of e)t.has(o.parent_id)||t.set(o.parent_id,o.parent_id),t.has(o.child_id)||t.set(o.child_id,o.child_id),s(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(n(o));return r.size}function mg(e){let t=typeof e.threshold=="number"?e.threshold:gr;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.llm_names??!0,s=e.model??ga,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Xe.lo,hi:e.llm_rescore?.band_hi??Xe.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??Vt,c=Jn.get(e.project);if(c){let F=en.get(c);if(F&&F.status==="running")return{jobId:c,reused:!0};Jn.delete(e.project)}let u=pa({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:p}=ma(u),f=d.get(e.project),b=p.get(e.project);if(!f||!b)return{error:`project "${e.project}" not found`};let T=r?Math.min(t,o.lo):t,S=Wt(b,T),w=r?S.filter(F=>F.confidence>=t):S;if(S.length===0||w.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let A=r0(),I=new AbortController,j=pg(w),J=new Date().toISOString(),M={jobId:A,project:e.project,threshold:t,llmNames:n,status:"running",startedAt:J,endedAt:null,events:[],waiters:new Set,controller:I,totalThreads:j,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 en.set(A,M),Jn.set(e.project,A),(async()=>{await Promise.resolve();try{Zt(M,"progress",{phase:"linking",edges_proposed:S.length,estimated_threads:j});let F=S;if(r){let U=await ag({proposals:S,scannables:b,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:I.signal,onProgress:v=>{v.phase==="rescoring"&&Zt(M,"progress",{phase:"rescoring",current:v.current,total:v.total,verdict:v.verdict})}});F=U.proposals,M.rescore={enabled:!0,considered:U.considered,promoted:U.promoted,demoted:U.demoted,unsure:U.unsure,failed:U.failed,capped:U.capped}}else F=S.filter(U=>U.confidence>=t);if(F.length===0){M.status=I.signal.aborted?"cancelled":"done",M.endedAt=new Date().toISOString(),Zt(M,"done",{...Ar(M),threads:[]});return}let ne=await cg({project:e.project,rows:f,edges:F,scannables:b,llmNames:n,model:s,signal:I.signal,onProgress:U=>{U.phase==="naming"&&(M.threadsNamed=U.current,M.currentThreadName=U.thread_name??null,Zt(M,"progress",{phase:"naming",current:U.current,total:U.total,thread_name:U.thread_name}))}});M.threadsCreated=ne.threads.length,M.edgesWritten=F.length+ne.threads.length,M.status=I.signal.aborted?"cancelled":"done",M.endedAt=new Date().toISOString(),Zt(M,"done",{...Ar(M),threads:ne.threads})}catch(F){M.status="failed",M.endedAt=new Date().toISOString(),M.error=F instanceof Error?F.message:String(F??"unknown error"),Zt(M,"done",Ar(M))}finally{i0(M)}})(),{jobId:A,reused:!1}}async function*gg(e,t=0){let n=en.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function fg(e){let t=en.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function fa(e){let t=en.get(e);return t?Ar(t):null}gs();import{randomUUID as l0}from"node:crypto";var xr=new Map;function _g(e){let t={id:l0(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return xr.set(t.id,t),t}function Nr(e){return xr.get(e)}function tn(e,t){for(let n of e.listeners)n(t)}function hg(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function Eg(e){let t=xr.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),tn(t,{type:"status",status:"cancelled"}),!0):!1}function bg(e){return xr.delete(e)}kt();function Or(e){let{session:t,knownTags:n,minTags:s,maxTags:r}=e,o=n.slice(0,50).map(c=>c.tag).join(", "),a=t.current_tags.length>0?`already applied, do not repeat: [${t.current_tags.join(", ")}]`:"currently has no tags";return[`You are tagging a software engineering session. Produce ${s}-${r} concise, lowercase, hyphen-separated tags that describe:`," - the domain or subsystem touched (auth, db, frontend, ...)"," - the kind of work (bugfix, feature, refactor, research, ...)"," - any specific tool, library, or product if prominent","","Prefer tags from the known-tags list below when applicable \u2014 consistency over creativity. Never invent marketing-sounding labels. Never tag based on speculation; only on observed work.","",`known tags (most used first, up to 50): ${o||"(none yet)"}`,"","Session:",` project: ${t.project}`,t.alias?` alias: ${JSON.stringify(t.alias)}`:" alias: (none)",t.git_branch?` git_branch: ${t.git_branch}`:"",` ${a}`," first_user_message:",Sg(t.first_user_message," ")," message_sample:",Sg(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${s}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1764
+ `)}function Sg(e,t){return e.split(`
1717
1765
  `).map(n=>t+n).join(`
1718
- `)}wt();import{z as Wn}from"zod";var RR=Wn.object({tags:Wn.array(Wn.string()).min(1),confidence:Wn.number().min(0).max(1),rationale:Wn.string().min(1).max(500)});function kR(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function wr(e,t={}){let n=t.maxTags??10,s;try{s=JSON.parse(kR(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=RR.safeParse(s);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>it(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,n);return a.length===0?{ok:!1,reason:"no usable tags after normalization"}:{ok:!0,data:{tags:a,confidence:r.data.confidence,rationale:r.data.rationale}}}import AR from"@anthropic-ai/sdk";async function Rr(e){let s=(await new AR({apiKey:e.apiKey}).messages.create({model:e.model,max_tokens:512,temperature:.2,messages:[{role:"user",content:e.prompt}]},e.signal?{signal:e.signal}:void 0)).content.find(r=>r.type==="text");if(!s||s.type!=="text")throw new Error("Anthropic response contained no text block");return s.text}function xR(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 kr(e,t={}){let n=t.maxAttempts??3,s=t.baseDelayMs??500,r;for(let o=0;o<n;o++)try{return await e()}catch(a){if(r=a,!xR(a)||o===n-1)throw a;await new Promise(c=>setTimeout(c,s*Math.pow(2,o)))}throw r}async function qm(e,t){e.status="running",Qt(e,{type:"status",status:"running"});let n=Tt();for(let s of t.sessions){if(e.controller.signal.aborted)break;let r=Tr({session:s,knownTags:n,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:s.id,project:s.project,alias:s.alias,first_user_message:s.first_user_message,current_tags:s.current_tags,suggestion:null,error:null,applied:!1};try{let a=await kr(()=>Rr({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=wr(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,Qt(e,{type:"result",result:o}),Qt(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let s=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;Qt(e,{type:"done",summary:{ok:s,failed:r}})}}function Xm(e,t){let n=0,s=0;for(let r of t){let o=e.results.find(a=>a.sessionId===r.sessionId);if(o){for(let a of r.tags)try{let{added:c}=yt(r.sessionId,a);c&&(n+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),s+=1}o.applied=!0}}return{applied:n,failed:s}}wt();cs();var ee={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},sa=new Set;function Xn(){return{status:ee.status,processed:ee.processed,total:ee.total,currentSessionId:ee.currentSessionId,lastError:ee.lastError,lastRunAt:ee.lastRunAt}}function Jm(e){return sa.add(e),()=>{sa.delete(e)}}function qn(){let e=Xn();for(let t of sa)t(e)}async function Ar(){let e=Be();if(!(!e.enabled||!e.autopilot)&&!(e.backend!=="api"||!e.apiKey)&&!ee.running){ee.running=!0,ee.status="scanning",ee.processed=0,ee.total=0,ee.currentSessionId=null,ee.lastError=null,ee.controller=new AbortController,qn();try{let t=Rt({untaggedOnly:!0,limit:200});if(ee.total=t.length,qn(),t.length===0){ee.status="idle",ee.lastRunAt=new Date().toISOString();return}let n=Tt();for(let s of t){if(ee.controller.signal.aborted)break;let r=Be();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;ee.currentSessionId=s.id,qn();let o=Tr({session:s,knownTags:n,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await kr(()=>Rr({apiKey:r.apiKey,model:r.model,prompt:o,signal:ee.controller.signal})),c=wr(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{yt(s.id,u)}catch(d){console.error("[autopilot] addTag failed:",s.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",s.id,a)}ee.processed+=1,qn(),await new Promise(a=>setTimeout(a,1500))}ee.status="idle",ee.lastRunAt=new Date().toISOString()}catch(t){ee.status="error",ee.lastError=t instanceof Error?t.message:String(t),console.error("[autopilot] fatal:",t)}finally{ee.running=!1,ee.currentSessionId=null,ee.controller=null,qn()}}}ds();import{copyFileSync as NR,existsSync as xr,readFileSync as Ym,writeFileSync as Km}from"node:fs";import{homedir as OR}from"node:os";import{dirname as CR,join as LR,resolve as vR}from"node:path";import{fileURLToPath as IR}from"node:url";var Pe=LR(OR(),".claude.json"),MR=CR(IR(import.meta.url)),jR=vR(MR,"..","mcp","server.js");function ra(){if(!xr(Pe))return{};try{let e=Ym(Pe,"utf8"),t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch(e){return console.error("[mcp-installer] failed to parse ~/.claude.json:",e),{}}}var Gm=new Map;function DR(e){let t=Gm.get(e);if(t!==void 0)return t;let n=kt(e)!==null;return Gm.set(e,n),n}function PR(){return DR("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[jR]}}function qe(){let t=ra().mcpServers?.recall;return{configPath:Pe,configExists:xr(Pe),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function oa(){let e=ra(),t=e.mcpServers??{},n=PR(),s=t.recall;if(s?.command===n.command&&JSON.stringify(s?.args??[])===JSON.stringify(n.args))return qe();let o={command:n.command};n.args.length>0&&(o.args=n.args);let a={...e,mcpServers:{...t,recall:o}};return Km(Pe,JSON.stringify(a,null,2)),qe()}function zm(){if(!xr(Pe))return{repointed:!1,reason:"config_missing"};let e;try{e=Ym(Pe,"utf8")}catch(f){return console.error("[mcp-installer] validate: failed to read ~/.claude.json:",f),{repointed:!1,reason:"read_error"}}let t;try{t=JSON.parse(e)}catch(f){return console.error("[mcp-installer] validate: ~/.claude.json is not valid JSON \u2014 skipping (operator must fix):",f),{repointed:!1,reason:"parse_error"}}if(!t||typeof t!="object"||Array.isArray(t))return{repointed:!1,reason:"not_an_object"};let s=t.mcpServers;if(!s||typeof s!="object")return{repointed:!1,reason:"no_mcp_servers"};let r=s.recall;if(!r)return{repointed:!1,reason:"no_recall_entry"};let o=r.args;if(!Array.isArray(o)||o.length===0||typeof o[0]!="string")return{repointed:!1,reason:"no_args_path"};let a=o[0];if(xr(a))return{repointed:!1,reason:"path_live"};let c=new Date().toISOString().replace(/[:.]/g,"-"),u=`${Pe}.bak.${c}`;try{NR(Pe,u)}catch(f){return console.error(`[mcp-installer] validate: backup write failed (${u}) \u2014 refusing to repoint to avoid risking original:`,f),{repointed:!1,reason:"backup_failed"}}try{oa()}catch(f){return console.error(`[mcp-installer] validate: installMcp() failed during repoint \u2014 backup preserved at ${u}:`,f),{repointed:!1,reason:"install_failed"}}let d=qe(),p=d.args&&d.args.length>0?d.args[0]:d.command;return console.log(`[mcp-installer] repointed stale ~/.claude.json from ${a} to ${p??"(command-only)"} (backup: ${u})`),{repointed:!0,reason:"stale_args[0]"}}function Vm(){let e=ra(),t=e.mcpServers??{};if(!t.recall)return qe();let{recall:n,...s}=t,r={...e,mcpServers:s};return Km(Pe,JSON.stringify(r,null,2)),qe()}import{existsSync as Qm,mkdirSync as FR,readFileSync as $R,writeFileSync as Zm}from"node:fs";import{homedir as UR}from"node:os";import{join as eg}from"node:path";import{z as Qe}from"zod";function tg(){return process.env.RECALL_HOME??eg(UR(),".recall")}function ng(){let e=tg();Qm(e)||FR(e,{recursive:!0})}function aa(){return eg(tg(),"onboarding.json")}var Nr=Qe.object({version:Qe.literal(1).default(1),completed:Qe.boolean().default(!1),skipped:Qe.boolean().default(!1),finishedAt:Qe.string().nullable().default(null),completedSteps:Qe.array(Qe.string().max(100)).max(50).default([]),threadsIntroSeen:Qe.boolean().default(!1)}),ia={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function Or(){let e=aa();if(!Qm(e))return{...ia};try{let t=JSON.parse($R(e,"utf8")),n=Nr.safeParse(t);return n.success?n.data:{...ia}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...ia}}}function sg(e){ng();let t=Or(),n=Nr.parse({...t,...e,completedSteps:HR([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return Zm(aa(),JSON.stringify(n,null,2)),n}function rg(){ng();let e=Or(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return Zm(aa(),JSON.stringify(t,null,2)),t}function HR(e){let t=new Set,n=[];for(let s of e)t.has(s)||(t.add(s),n.push(s));return n}Te();co();ao();q();q();var BR=500,Cr=new Set,og=null,Lr=!1;function ig(){return Cr.size}function WR(){return`
1766
+ `)}kt();import{z as Gn}from"zod";var u0=Gn.object({tags:Gn.array(Gn.string()).min(1),confidence:Gn.number().min(0).max(1),rationale:Gn.string().min(1).max(500)});function d0(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function Lr(e,t={}){let n=t.maxTags??10,s;try{s=JSON.parse(d0(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=u0.safeParse(s);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>ut(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,n);return a.length===0?{ok:!1,reason:"no usable tags after normalization"}:{ok:!0,data:{tags:a,confidence:r.data.confidence,rationale:r.data.rationale}}}import p0 from"@anthropic-ai/sdk";async function Cr(e){let s=(await new p0({apiKey:e.apiKey}).messages.create({model:e.model,max_tokens:512,temperature:.2,messages:[{role:"user",content:e.prompt}]},e.signal?{signal:e.signal}:void 0)).content.find(r=>r.type==="text");if(!s||s.type!=="text")throw new Error("Anthropic response contained no text block");return s.text}function m0(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 vr(e,t={}){let n=t.maxAttempts??3,s=t.baseDelayMs??500,r;for(let o=0;o<n;o++)try{return await e()}catch(a){if(r=a,!m0(a)||o===n-1)throw a;await new Promise(c=>setTimeout(c,s*Math.pow(2,o)))}throw r}async function Tg(e,t){e.status="running",tn(e,{type:"status",status:"running"});let n=Rt();for(let s of t.sessions){if(e.controller.signal.aborted)break;let r=Or({session:s,knownTags:n,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:s.id,project:s.project,alias:s.alias,first_user_message:s.first_user_message,current_tags:s.current_tags,suggestion:null,error:null,applied:!1};try{let a=await vr(()=>Cr({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=Lr(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,tn(e,{type:"result",result:o}),tn(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let s=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;tn(e,{type:"done",summary:{ok:s,failed:r}})}}function yg(e,t){let n=0,s=0;for(let r of t){let o=e.results.find(a=>a.sessionId===r.sessionId);if(o){for(let a of r.tags)try{let{added:c}=wt(r.sessionId,a);c&&(n+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),s+=1}o.applied=!0}}return{applied:n,failed:s}}kt();gs();var ee={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},_a=new Set;function zn(){return{status:ee.status,processed:ee.processed,total:ee.total,currentSessionId:ee.currentSessionId,lastError:ee.lastError,lastRunAt:ee.lastRunAt}}function wg(e){return _a.add(e),()=>{_a.delete(e)}}function Yn(){let e=zn();for(let t of _a)t(e)}async function Ir(){let e=qe();if(!(!e.enabled||!e.autopilot)&&!(e.backend!=="api"||!e.apiKey)&&!ee.running){ee.running=!0,ee.status="scanning",ee.processed=0,ee.total=0,ee.currentSessionId=null,ee.lastError=null,ee.controller=new AbortController,Yn();try{let t=At({untaggedOnly:!0,limit:200});if(ee.total=t.length,Yn(),t.length===0){ee.status="idle",ee.lastRunAt=new Date().toISOString();return}let n=Rt();for(let s of t){if(ee.controller.signal.aborted)break;let r=qe();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;ee.currentSessionId=s.id,Yn();let o=Or({session:s,knownTags:n,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await vr(()=>Cr({apiKey:r.apiKey,model:r.model,prompt:o,signal:ee.controller.signal})),c=Lr(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{wt(s.id,u)}catch(d){console.error("[autopilot] addTag failed:",s.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",s.id,a)}ee.processed+=1,Yn(),await new Promise(a=>setTimeout(a,1500))}ee.status="idle",ee.lastRunAt=new Date().toISOString()}catch(t){ee.status="error",ee.lastError=t instanceof Error?t.message:String(t),console.error("[autopilot] fatal:",t)}finally{ee.running=!1,ee.currentSessionId=null,ee.controller=null,Yn()}}}hs();import{copyFileSync as g0,existsSync as Mr,readFileSync as kg,writeFileSync as Ag}from"node:fs";import{homedir as f0}from"node:os";import{dirname as _0,join as h0,resolve as E0}from"node:path";import{fileURLToPath as b0}from"node:url";var Pe=h0(f0(),".claude.json");function S0(){let e=_0(b0(import.meta.url));return E0(e,"..","mcp-server.js")}var T0=S0();function ha(){if(!Mr(Pe))return{};try{let e=kg(Pe,"utf8"),t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch(e){return console.error("[mcp-installer] failed to parse ~/.claude.json:",e),{}}}var Rg=new Map;function y0(e){let t=Rg.get(e);if(t!==void 0)return t;let n=xt(e)!==null;return Rg.set(e,n),n}function w0(){return y0("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[T0]}}function Je(){let t=ha().mcpServers?.recall;return{configPath:Pe,configExists:Mr(Pe),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function Ea(){let e=ha(),t=e.mcpServers??{},n=w0(),s=t.recall;if(s?.command===n.command&&JSON.stringify(s?.args??[])===JSON.stringify(n.args))return Je();let o={command:n.command};n.args.length>0&&(o.args=n.args);let a={...e,mcpServers:{...t,recall:o}};return Ag(Pe,JSON.stringify(a,null,2)),Je()}function xg(){if(!Mr(Pe))return{repointed:!1,reason:"config_missing"};let e;try{e=kg(Pe,"utf8")}catch(f){return console.error("[mcp-installer] validate: failed to read ~/.claude.json:",f),{repointed:!1,reason:"read_error"}}let t;try{t=JSON.parse(e)}catch(f){return console.error("[mcp-installer] validate: ~/.claude.json is not valid JSON \u2014 skipping (operator must fix):",f),{repointed:!1,reason:"parse_error"}}if(!t||typeof t!="object"||Array.isArray(t))return{repointed:!1,reason:"not_an_object"};let s=t.mcpServers;if(!s||typeof s!="object")return{repointed:!1,reason:"no_mcp_servers"};let r=s.recall;if(!r)return{repointed:!1,reason:"no_recall_entry"};let o=r.args;if(!Array.isArray(o)||o.length===0||typeof o[0]!="string")return{repointed:!1,reason:"no_args_path"};let a=o[0];if(Mr(a))return{repointed:!1,reason:"path_live"};let c=new Date().toISOString().replace(/[:.]/g,"-"),u=`${Pe}.bak.${c}`;try{g0(Pe,u)}catch(f){return console.error(`[mcp-installer] validate: backup write failed (${u}) \u2014 refusing to repoint to avoid risking original:`,f),{repointed:!1,reason:"backup_failed"}}try{Ea()}catch(f){return console.error(`[mcp-installer] validate: installMcp() failed during repoint \u2014 backup preserved at ${u}:`,f),{repointed:!1,reason:"install_failed"}}let d=Je(),p=d.args&&d.args.length>0?d.args[0]:d.command;return console.log(`[mcp-installer] repointed stale ~/.claude.json from ${a} to ${p??"(command-only)"} (backup: ${u})`),{repointed:!0,reason:"stale_args[0]"}}function Ng(){let e=ha(),t=e.mcpServers??{};if(!t.recall)return Je();let{recall:n,...s}=t,r={...e,mcpServers:s};return Ag(Pe,JSON.stringify(r,null,2)),Je()}import{existsSync as Og,mkdirSync as R0,readFileSync as k0,writeFileSync as Lg}from"node:fs";import{homedir as A0}from"node:os";import{join as Cg}from"node:path";import{z as nt}from"zod";function vg(){return process.env.RECALL_HOME??Cg(A0(),".recall")}function Ig(){let e=vg();Og(e)||R0(e,{recursive:!0})}function Sa(){return Cg(vg(),"onboarding.json")}var Dr=nt.object({version:nt.literal(1).default(1),completed:nt.boolean().default(!1),skipped:nt.boolean().default(!1),finishedAt:nt.string().nullable().default(null),completedSteps:nt.array(nt.string().max(100)).max(50).default([]),threadsIntroSeen:nt.boolean().default(!1)}),ba={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function jr(){let e=Sa();if(!Og(e))return{...ba};try{let t=JSON.parse(k0(e,"utf8")),n=Dr.safeParse(t);return n.success?n.data:{...ba}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...ba}}}function Mg(e){Ig();let t=jr(),n=Dr.parse({...t,...e,completedSteps:x0([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return Lg(Sa(),JSON.stringify(n,null,2)),n}function Dg(){Ig();let e=jr(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return Lg(Sa(),JSON.stringify(t,null,2)),t}function x0(e){let t=new Set,n=[];for(let s of e)t.has(s)||(t.add(s),n.push(s));return n}we();ho();_o();H();H();var N0=500,Pr=new Set,jg=null,Fr=!1;function Pg(){return Pr.size}function O0(){return`
1719
1767
  SELECT m.uuid, m.session_id, m.timestamp, m.raw_json
1720
1768
  FROM messages m
1721
1769
  LEFT JOIN message_usage mu ON mu.message_uuid = m.uuid
1722
1770
  WHERE m.role = 'assistant' AND mu.message_uuid IS NULL
1723
1771
  AND m.uuid NOT IN (SELECT value FROM json_each(?))
1724
1772
  LIMIT ?
1725
- `}function ag(e,t){let n=t.limit??Number.MAX_SAFE_INTEGER,s=Math.max(1,t.chunkSize??BR),r=e.prepare(WR()),o=e.prepare(`
1773
+ `}function Fg(e,t){let n=t.limit??Number.MAX_SAFE_INTEGER,s=Math.max(1,t.chunkSize??N0),r=e.prepare(O0()),o=e.prepare(`
1726
1774
  INSERT INTO message_usage (
1727
1775
  message_uuid, session_id, model,
1728
1776
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1732,7 +1780,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1732
1780
  @input, @output, @cc, @cr, @ts
1733
1781
  )
1734
1782
  ON CONFLICT(message_uuid) DO NOTHING
1735
- `),a=0,c=0,u=new Set;for(;a<n;){let d=Math.min(s,n-a),p=JSON.stringify([...Cr]),f=r.all(p,d);if(f.length===0)break;let S=new Set;if(e.transaction(()=>{for(let b of f){let R;try{R=JSON.parse(b.raw_json)}catch{Cr.add(b.uuid);continue}let k=Io(R.message);if(!k){Cr.add(b.uuid);continue}o.run({uuid:b.uuid,session_id:b.session_id,model:R.message?.model??null,input:k.inputTokens,output:k.outputTokens,cc:k.cacheCreateTokens,cr:k.cacheReadTokens,ts:b.timestamp}),c+=1,S.add(b.session_id)}for(let b of S)Cs(e,b),u.add(b)})(),a+=f.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:f.length<d}),f.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function cg(e={}){return ag(h(),e)}function lg(e={}){return Lr?!1:(Lr=!0,queueMicrotask(()=>{try{let t=ag(h(),e);og={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{Lr=!1}}),!0)}function ug(){return Lr}function ca(){return og}var qR=[[/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}]],dg={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function Ze(e){if(!e)return dg;for(let[t,n]of qR)if(t.test(e))return n;return dg}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,p]of Object.entries(e.byModel)){let f=Ze(d);a.input+=p.inputTokens/1e6*f.inputCentsPerMtok,a.output+=p.outputTokens/1e6*f.outputCentsPerMtok,a.cacheCreate+=p.cacheCreateTokens/1e6*f.cacheCreateCentsPerMtok,a.cacheRead+=p.cacheReadTokens/1e6*f.cacheReadCentsPerMtok,c+=p.inputTokens+p.outputTokens+p.cacheCreateTokens+p.cacheReadTokens}let u=a.input+a.output+a.cacheCreate+a.cacheRead;return{cents:u,dollars:u/100,totalTokens:c,parts:a}}let n=Ze(t),s={input:e.inputTokens/1e6*n.inputCentsPerMtok,output:e.outputTokens/1e6*n.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*n.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*n.cacheReadCentsPerMtok},r=s.input+s.output+s.cacheCreate+s.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:s}}function Zt(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 en(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 la(e){let t=new Map;for(let s of e){let r=s.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=s.input_tokens,o.outputTokens+=s.output_tokens,o.cacheCreateTokens+=s.cache_create_tokens,o.cacheReadTokens+=s.cache_read_tokens,o.messageCount+=s.n,t.set(r,o)}let n=[];for(let[s,r]of t.entries()){let o=Ae({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},s);n.push({model:s,modelLabel:Ze(s).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return n.sort((s,r)=>r.cost.cents-s.cost.cents)}function ua(e){let t={};for(let n of e)t[n.model??"__unknown__"]={inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheCreateTokens:n.cacheCreateTokens,cacheReadTokens:n.cacheReadTokens};return{byModel:t}}function pg(e){let t=h(),n=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1783
+ `),a=0,c=0,u=new Set;for(;a<n;){let d=Math.min(s,n-a),p=JSON.stringify([...Pr]),f=r.all(p,d);if(f.length===0)break;let b=new Set;if(e.transaction(()=>{for(let S of f){let w;try{w=JSON.parse(S.raw_json)}catch{Pr.add(S.uuid);continue}let A=Ho(w.message);if(!A){Pr.add(S.uuid);continue}o.run({uuid:S.uuid,session_id:S.session_id,model:w.message?.model??null,input:A.inputTokens,output:A.outputTokens,cc:A.cacheCreateTokens,cr:A.cacheReadTokens,ts:S.timestamp}),c+=1,b.add(S.session_id)}for(let S of b)yn(e,S),u.add(S)})(),a+=f.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:f.length<d}),f.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function $g(e={}){return Fg(_(),e)}function Ug(e={}){return Fr?!1:(Fr=!0,queueMicrotask(()=>{try{let t=Fg(_(),e);jg={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{Fr=!1}}),!0)}function Hg(){return Fr}function Ta(){return jg}var L0=[[/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}]],Bg={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function st(e){if(!e)return Bg;for(let[t,n]of L0)if(t.test(e))return n;return Bg}function xe(e,t){if(e.byModel&&Object.keys(e.byModel).length>0){let a={input:0,output:0,cacheCreate:0,cacheRead:0},c=0;for(let[d,p]of Object.entries(e.byModel)){let f=st(d);a.input+=p.inputTokens/1e6*f.inputCentsPerMtok,a.output+=p.outputTokens/1e6*f.outputCentsPerMtok,a.cacheCreate+=p.cacheCreateTokens/1e6*f.cacheCreateCentsPerMtok,a.cacheRead+=p.cacheReadTokens/1e6*f.cacheReadCentsPerMtok,c+=p.inputTokens+p.outputTokens+p.cacheCreateTokens+p.cacheReadTokens}let u=a.input+a.output+a.cacheCreate+a.cacheRead;return{cents:u,dollars:u/100,totalTokens:c,parts:a}}let n=st(t),s={input:e.inputTokens/1e6*n.inputCentsPerMtok,output:e.outputTokens/1e6*n.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*n.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*n.cacheReadCentsPerMtok},r=s.input+s.output+s.cacheCreate+s.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:s}}function nn(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 sn(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 ya(e){let t=new Map;for(let s of e){let r=s.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=s.input_tokens,o.outputTokens+=s.output_tokens,o.cacheCreateTokens+=s.cache_create_tokens,o.cacheReadTokens+=s.cache_read_tokens,o.messageCount+=s.n,t.set(r,o)}let n=[];for(let[s,r]of t.entries()){let o=xe({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},s);n.push({model:s,modelLabel:st(s).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return n.sort((s,r)=>r.cost.cents-s.cost.cents)}function wa(e){let t={};for(let n of e)t[n.model??"__unknown__"]={inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheCreateTokens:n.cacheCreateTokens,cacheReadTokens:n.cacheReadTokens};return{byModel:t}}function Wg(e){let t=_(),n=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1736
1784
  s.message_count,
1737
1785
  s.total_input_tokens, s.total_output_tokens,
1738
1786
  s.total_cache_create_tokens, s.total_cache_read_tokens,
@@ -1747,7 +1795,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1747
1795
  COUNT(*) AS n
1748
1796
  FROM message_usage
1749
1797
  WHERE session_id = ?
1750
- GROUP BY model`).all(e),r=la(s),o=n.total_input_tokens??0,a=n.total_output_tokens??0,c=n.total_cache_create_tokens??0,u=n.total_cache_read_tokens??0,d=Ae({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...ua(r)},n.primary_model);return{sessionId:n.id,project:n.project,startedAt:n.started_at,endedAt:n.ended_at,messageCount:n.message_count,primaryModel:n.primary_model,primaryModelLabel:Ze(n.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:Zt(d.cents),tokens:en(d.totalTokens),model:Ze(n.primary_model).label}}}function mg(e){let t=h(),n=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT mu.model,
1798
+ GROUP BY model`).all(e),r=ya(s),o=n.total_input_tokens??0,a=n.total_output_tokens??0,c=n.total_cache_create_tokens??0,u=n.total_cache_read_tokens??0,d=xe({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...wa(r)},n.primary_model);return{sessionId:n.id,project:n.project,startedAt:n.started_at,endedAt:n.ended_at,messageCount:n.message_count,primaryModel:n.primary_model,primaryModelLabel:st(n.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:nn(d.cents),tokens:sn(d.totalTokens),model:st(n.primary_model).label}}}function qg(e){let t=_(),n=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT mu.model,
1751
1799
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1752
1800
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1753
1801
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1756,12 +1804,12 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1756
1804
  FROM message_usage mu
1757
1805
  JOIN sessions s ON s.id = mu.session_id
1758
1806
  WHERE s.project_id = ?
1759
- GROUP BY mu.model`).all(n.id),r=la(s),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1807
+ GROUP BY mu.model`).all(n.id),r=ya(s),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1760
1808
  COALESCE(SUM(total_output_tokens), 0) AS output_tokens,
1761
1809
  COALESCE(SUM(total_cache_create_tokens), 0) AS cache_create_tokens,
1762
1810
  COALESCE(SUM(total_cache_read_tokens), 0) AS cache_read_tokens,
1763
1811
  COUNT(*) AS session_count
1764
- FROM sessions WHERE project_id = ?`).get(n.id),a=Ae({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...ua(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1812
+ FROM sessions WHERE project_id = ?`).get(n.id),a=xe({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...wa(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1765
1813
  s.total_input_tokens, s.total_output_tokens,
1766
1814
  s.total_cache_create_tokens, s.total_cache_read_tokens,
1767
1815
  s.primary_model
@@ -1772,7 +1820,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1772
1820
  + COALESCE(s.total_output_tokens,0)
1773
1821
  + COALESCE(s.total_cache_create_tokens,0)
1774
1822
  + COALESCE(s.total_cache_read_tokens,0)) DESC
1775
- LIMIT 10`).all(n.id).map(d=>{let p=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:p.totalTokens,cost:p}});return{project:n.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:a.totalTokens,cost:a,byModel:r,topSessions:u,display:{dollars:Zt(a.cents),tokens:en(a.totalTokens)}}}function gg(e="all"){let t=h(),n=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,s=n?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=n?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=n?{since:n}:{},a=l=>n?t.prepare(l).get(o):t.prepare(l).get(),c=l=>n?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1823
+ LIMIT 10`).all(n.id).map(d=>{let p=xe({inputTokens:d.total_input_tokens??0,outputTokens:d.total_output_tokens??0,cacheCreateTokens:d.total_cache_create_tokens??0,cacheReadTokens:d.total_cache_read_tokens??0},d.primary_model);return{sessionId:d.id,alias:d.alias,startedAt:d.started_at,totalTokens:p.totalTokens,cost:p}});return{project:n.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:a.totalTokens,cost:a,byModel:r,topSessions:u,display:{dollars:nn(a.cents),tokens:sn(a.totalTokens)}}}function Xg(e="all"){let t=_(),n=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,s=n?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=n?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=n?{since:n}:{},a=l=>n?t.prepare(l).get(o):t.prepare(l).get(),c=l=>n?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1776
1824
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1777
1825
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1778
1826
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1781,7 +1829,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1781
1829
  FROM message_usage mu
1782
1830
  JOIN sessions s ON s.id = mu.session_id
1783
1831
  ${s}
1784
- GROUP BY mu.model`),d=la(u),p=0,f=0,S=0,y=0;for(let l of d)p+=l.inputTokens,f+=l.outputTokens,S+=l.cacheCreateTokens,y+=l.cacheReadTokens;let b=Ae({inputTokens:p,outputTokens:f,cacheCreateTokens:S,cacheReadTokens:y,...ua(d)},null),R=n?a(`SELECT
1832
+ GROUP BY mu.model`),d=ya(u),p=0,f=0,b=0,T=0;for(let l of d)p+=l.inputTokens,f+=l.outputTokens,b+=l.cacheCreateTokens,T+=l.cacheReadTokens;let S=xe({inputTokens:p,outputTokens:f,cacheCreateTokens:b,cacheReadTokens:T,...wa(d)},null),w=n?a(`SELECT
1785
1833
  (SELECT COUNT(DISTINCT m.session_id)
1786
1834
  FROM messages m
1787
1835
  JOIN sessions s2 ON s2.id = m.session_id
@@ -1795,7 +1843,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1795
1843
  WHERE (COALESCE(total_input_tokens,0)
1796
1844
  +COALESCE(total_output_tokens,0)
1797
1845
  +COALESCE(total_cache_create_tokens,0)
1798
- +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`).get(),k=c(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
1846
+ +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`).get(),A=c(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
1799
1847
  mu.model,
1800
1848
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1801
1849
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
@@ -1805,7 +1853,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1805
1853
  JOIN sessions s ON s.id = mu.session_id
1806
1854
  ${s}
1807
1855
  GROUP BY day, mu.model
1808
- ORDER BY day ASC`),M=new Map;for(let l of k){if(!l.day)continue;let m=Ae({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.model),g=M.get(l.day)??{tokens:0,cents:0};g.tokens+=m.totalTokens,g.cents+=m.cents,M.set(l.day,g)}let P=[...M.entries()].map(([l,m])=>({day:l,tokens:m.tokens,cents:m.cents})).sort((l,m)=>l.day.localeCompare(m.day)),D=c(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1856
+ ORDER BY day ASC`),I=new Map;for(let l of A){if(!l.day)continue;let m=xe({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.model),g=I.get(l.day)??{tokens:0,cents:0};g.tokens+=m.totalTokens,g.cents+=m.cents,I.set(l.day,g)}let j=[...I.entries()].map(([l,m])=>({day:l,tokens:m.tokens,cents:m.cents})).sort((l,m)=>l.day.localeCompare(m.day)),M=c(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1809
1857
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1810
1858
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1811
1859
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1821,7 +1869,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1821
1869
  + COALESCE(SUM(mu.output_tokens),0)
1822
1870
  + COALESCE(SUM(mu.cache_create_tokens),0)
1823
1871
  + COALESCE(SUM(mu.cache_read_tokens),0)) DESC
1824
- LIMIT 10`).map(l=>{let m=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:m.totalTokens,cost:m}}),v=c(`SELECT p.id AS project_id,
1872
+ LIMIT 10`).map(l=>{let m=xe({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:m.totalTokens,cost:m}}),F=c(`SELECT p.id AS project_id,
1825
1873
  p.name AS project,
1826
1874
  mu.model,
1827
1875
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
@@ -1833,29 +1881,29 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1833
1881
  JOIN sessions s ON s.id = mu.session_id
1834
1882
  LEFT JOIN projects p ON p.id = s.project_id
1835
1883
  ${s}
1836
- GROUP BY p.id, mu.model`),F=new Map;for(let l of v){let m=l.project_id??"__none__",g=F.get(m);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},F.set(m,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 U=[...F.values()].map(l=>{let m=0,g=0,_=0,E=0;for(let w of Object.values(l.byModel))m+=w.inputTokens,g+=w.outputTokens,_+=w.cacheCreateTokens,E+=w.cacheReadTokens;let T=Ae({inputTokens:m,outputTokens:g,cacheCreateTokens:_,cacheReadTokens:E,byModel:l.byModel},null);return{project:l.project,sessions:l.sessionsApprox,totalTokens:T.totalTokens,cost:T}});U.sort((l,m)=>m.totalTokens-l.totalTokens);let L=U.slice(0,20),i=t.prepare(`SELECT
1884
+ GROUP BY p.id, mu.model`),ne=new Map;for(let l of F){let m=l.project_id??"__none__",g=ne.get(m);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},ne.set(m,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 U=[...ne.values()].map(l=>{let m=0,g=0,h=0,E=0;for(let R of Object.values(l.byModel))m+=R.inputTokens,g+=R.outputTokens,h+=R.cacheCreateTokens,E+=R.cacheReadTokens;let y=xe({inputTokens:m,outputTokens:g,cacheCreateTokens:h,cacheReadTokens:E,byModel:l.byModel},null);return{project:l.project,sessions:l.sessionsApprox,totalTokens:y.totalTokens,cost:y}});U.sort((l,m)=>m.totalTokens-l.totalTokens);let v=U.slice(0,20),i=t.prepare(`SELECT
1837
1885
  (SELECT COUNT(*) FROM messages WHERE role='assistant') AS assistant_messages,
1838
- (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:R.total_sessions,sessionsWithUsage:R.sessions_with_usage,inputTokens:p,outputTokens:f,cacheCreateTokens:S,cacheReadTokens:y,totalTokens:b.totalTokens,cost:b,daily:P,byModel:d,topSessions:D,topRepos:L,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min(ig(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:Zt(b.cents),tokens:en(b.totalTokens)}}}q();function Jn(e){return Math.max(0,Math.min(1,e))}function da(e){let t=h(),n=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT COUNT(*) AS cnt,
1886
+ (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:w.total_sessions,sessionsWithUsage:w.sessions_with_usage,inputTokens:p,outputTokens:f,cacheCreateTokens:b,cacheReadTokens:T,totalTokens:S.totalTokens,cost:S,daily:j,byModel:d,topSessions:M,topRepos:v,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min(Pg(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:nn(S.cents),tokens:sn(S.totalTokens)}}}H();function Kn(e){return Math.max(0,Math.min(1,e))}function Ra(e){let t=_(),n=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT COUNT(*) AS cnt,
1839
1887
  MAX(started_at) AS latest
1840
- FROM sessions WHERE project_id = ?`).get(e),r=s.cnt;if(r===0)return{projectId:e,projectName:n.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=Jn(r/10),a=s.latest?(Date.now()-new Date(s.latest).getTime())/(1e3*60*60*24):90,c=Jn(1-a/90),d=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1841
- FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,p=Jn((d-2)/3),f=t.prepare(`SELECT COUNT(*) AS total,
1888
+ FROM sessions WHERE project_id = ?`).get(e),r=s.cnt;if(r===0)return{projectId:e,projectName:n.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=Kn(r/10),a=s.latest?(Date.now()-new Date(s.latest).getTime())/(1e3*60*60*24):90,c=Kn(1-a/90),d=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1889
+ FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,p=Kn((d-2)/3),f=t.prepare(`SELECT COUNT(*) AS total,
1842
1890
  SUM(CASE WHEN m.content_text IS NOT NULL AND m.content_text != '' THEN 1 ELSE 0 END) AS covered
1843
1891
  FROM messages m
1844
1892
  JOIN sessions s ON s.id = m.session_id
1845
- WHERE s.project_id = ?`).get(e),S=f.total>0?f.covered/f.total:.5,y=Jn(S),b=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
1893
+ WHERE s.project_id = ?`).get(e),b=f.total>0?f.covered/f.total:.5,T=Kn(b),S=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
1846
1894
  COUNT(DISTINCT st.session_id) AS tagged
1847
1895
  FROM sessions s
1848
1896
  LEFT JOIN session_tags st ON st.session_id = s.id
1849
- WHERE s.project_id = ?`).get(e),R=b.total>0?b.tagged/b.total:0,k=Jn(R),M=Math.round((o*.2+c*.25+p*.15+y*.2+k*.2)*100);return{projectId:e,projectName:n.name,score:M,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:p,weight:.15},searchCoverage:{ratio:Math.round(S*100)/100,score:y,weight:.2},tagCoverage:{ratio:Math.round(R*100)/100,score:k,weight:.2}}}}function fg(){let t=h().prepare("SELECT id FROM projects ORDER BY name").all(),n=[];for(let s of t){let r=da(s.id);r&&n.push(r)}return n}q();import{execFile as XR}from"node:child_process";import{promisify as JR}from"node:util";import{stat as GR}from"node:fs/promises";var YR=JR(XR),KR=60,zR=7,VR=7,QR=5e3;function ZR(){let e=h(),t=n=>{try{return!!e.prepare(n).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1897
+ WHERE s.project_id = ?`).get(e),w=S.total>0?S.tagged/S.total:0,A=Kn(w),I=Math.round((o*.2+c*.25+p*.15+T*.2+A*.2)*100);return{projectId:e,projectName:n.name,score:I,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:p,weight:.15},searchCoverage:{ratio:Math.round(b*100)/100,score:T,weight:.2},tagCoverage:{ratio:Math.round(w*100)/100,score:A,weight:.2}}}}function Jg(){let t=_().prepare("SELECT id FROM projects ORDER BY name").all(),n=[];for(let s of t){let r=Ra(s.id);r&&n.push(r)}return n}H();import{execFile as C0}from"node:child_process";import{promisify as v0}from"node:util";import{stat as I0}from"node:fs/promises";var M0=v0(C0),D0=60,j0=7,P0=7,F0=5e3;function $0(){let e=_(),t=n=>{try{return!!e.prepare(n).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1850
1898
  WHERE (COALESCE(total_input_tokens,0)
1851
1899
  + COALESCE(total_output_tokens,0)
1852
1900
  + COALESCE(total_cache_create_tokens,0)
1853
1901
  + COALESCE(total_cache_read_tokens,0)) > 0
1854
- LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function pa(e){if(!e)return[];let t=new Set,n=[];for(let s of e.split(",")){let r=s.trim().toLowerCase();!r||t.has(r)||(t.add(r),n.push(r))}return n}function _g(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 ek(){let e=h(),t=e.prepare(`SELECT ss.keywords
1902
+ LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function ka(e){if(!e)return[];let t=new Set,n=[];for(let s of e.split(",")){let r=s.trim().toLowerCase();!r||t.has(r)||(t.add(r),n.push(r))}return n}function Gg(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 U0(){let e=_(),t=e.prepare(`SELECT ss.keywords
1855
1903
  FROM session_semantic ss
1856
1904
  JOIN sessions s ON s.id = ss.session_id
1857
1905
  WHERE s.started_at IS NOT NULL
1858
- AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:zR});if(t.length===0)return null;let n=new Set;for(let o of t)for(let a of pa(o.keywords))n.add(a);if(n.size===0)return null;let s=e.prepare(`SELECT ss.session_id AS session_id,
1906
+ AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:j0});if(t.length===0)return null;let n=new Set;for(let o of t)for(let a of ka(o.keywords))n.add(a);if(n.size===0)return null;let s=e.prepare(`SELECT ss.session_id AS session_id,
1859
1907
  ss.summary AS summary,
1860
1908
  ss.keywords AS keywords,
1861
1909
  p.name AS project,
@@ -1871,7 +1919,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1871
1919
  WHERE s.started_at IS NOT NULL
1872
1920
  AND s.message_count > 2
1873
1921
  AND julianday('now') - julianday(s.started_at) >= @ageDays
1874
- ORDER BY s.started_at ASC`).all({ageDays:KR});if(s.length===0)return null;let r=null;for(let o of s){let c=pa(o.keywords).filter(u=>n.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{..._g(r.row),summary:r.row.summary,keywords:pa(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function tk(){let t=h().prepare(`SELECT s.id AS session_id,
1922
+ ORDER BY s.started_at ASC`).all({ageDays:D0});if(s.length===0)return null;let r=null;for(let o of s){let c=ka(o.keywords).filter(u=>n.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{...Gg(r.row),summary:r.row.summary,keywords:ka(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function H0(){let t=_().prepare(`SELECT s.id AS session_id,
1875
1923
  p.name AS project,
1876
1924
  NULLIF(sa.alias, '') AS alias,
1877
1925
  s.started_at AS started_at,
@@ -1890,51 +1938,51 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1890
1938
  AND (COALESCE(s.total_input_tokens, 0)
1891
1939
  + COALESCE(s.total_output_tokens, 0)
1892
1940
  + COALESCE(s.total_cache_create_tokens, 0)
1893
- + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:VR});if(t.length===0)return null;let n=null;for(let s of t){let r=Ae({inputTokens:s.input_tokens,outputTokens:s.output_tokens,cacheCreateTokens:s.cache_create_tokens,cacheReadTokens:s.cache_read_tokens},s.primary_model);r.cents<=0||(!n||r.cents>n.cents)&&(n={row:s,cents:r.cents,totalTokens:r.totalTokens})}return n?{..._g(n.row),totalTokens:n.totalTokens,costCents:n.cents,costDisplay:Zt(n.cents),tokensDisplay:en(n.totalTokens),primaryModel:n.row.primary_model,primaryModelLabel:Ze(n.row.primary_model).label}:null}async function nk(e){try{if(!(await GR(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await YR("git",["rev-parse","HEAD"],{cwd:e,timeout:QR}),n=t.trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}async function sk(){let e=h(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1941
+ + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:P0});if(t.length===0)return null;let n=null;for(let s of t){let r=xe({inputTokens:s.input_tokens,outputTokens:s.output_tokens,cacheCreateTokens:s.cache_create_tokens,cacheReadTokens:s.cache_read_tokens},s.primary_model);r.cents<=0||(!n||r.cents>n.cents)&&(n={row:s,cents:r.cents,totalTokens:r.totalTokens})}return n?{...Gg(n.row),totalTokens:n.totalTokens,costCents:n.cents,costDisplay:nn(n.cents),tokensDisplay:sn(n.totalTokens),primaryModel:n.row.primary_model,primaryModelLabel:st(n.row.primary_model).label}:null}async function B0(e){try{if(!(await I0(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await M0("git",["rev-parse","HEAD"],{cwd:e,timeout:F0}),n=t.trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}async function W0(){let e=_(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1894
1942
  FROM sessions s
1895
1943
  WHERE s.cwd IS NOT NULL AND s.started_at IS NOT NULL
1896
1944
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1897
- LIMIT 1`).get();if(!t?.cwd)return null;let n=await nk(t.cwd);if(!n)return null;let s=Ls(n);if(s.length===0)return null;let r=s[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1945
+ LIMIT 1`).get();if(!t?.cwd)return null;let n=await B0(t.cwd);if(!n)return null;let s=Ps(n);if(s.length===0)return null;let r=s[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1898
1946
  FROM sessions s
1899
- 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 hg(){let e=ZR(),t=e.semantic?Promise.resolve().then(()=>{try{return ek()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),n=e.cost?Promise.resolve().then(()=>{try{return tk()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),s=e.git?sk().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,n,s]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}bt();q();var ma=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,n){super(`semantic search refused: vec_chunks has ${t} rows (cap ${n}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=n}};function ga(e,t={}){let n=t.limit??rk()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new ma(r,n)}function rk(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}Vn();import{Worker as ok}from"node:worker_threads";import{join as ik}from"node:path";var fa=class extends Error{constructor(n){super(`semantic kNN exceeded ${n}ms wall-clock budget -- query aborted`);this.timeoutMs=n}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},ak=1e4;function ck(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function _a(e){let t=e.timeoutMs??ck()??ak,n=(e.workerFactory??lk)();return new Promise((s,r)=>{let o=setTimeout(()=>{n.terminate().catch(()=>{}),r(new fa(t))},t);n.once("message",a=>{clearTimeout(o);let c=a;c&&c.ok===!0&&Array.isArray(c.hits)?s(c.hits):r(new Error(c?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",a=>{clearTimeout(o),n.terminate().catch(()=>{}),r(a)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function lk(){let e=ik(ht(),"dist","daemon","query-worker.js");return new ok(e)}async function Eg(e,t=50){return ga(h()),_a({query:e,limit:t})}async function bg(e,t=10,n=.65){let s=h();ga(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=await _a({precomputedVector:o.embedding,limit:t*5}),c=new Map;for(let d of a){if(d.sessionId===e)continue;let p=c.get(d.sessionId);(p===void 0||d.distance<p)&&c.set(d.sessionId,d.distance)}let u=[];for(let[d,p]of c){let f=1-p;f>=n&&u.push({sessionId:d,similarity:f})}return u.sort((d,p)=>p.similarity-d.similarity),u.slice(0,t)}function uk(){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 Sg(e){let t=uk(),n=new Map;for(let r of e)for(let o=0;o<r.length;o++){let a=r[o],c=1/(t+o+1),u=n.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):n.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let s=[];for(let[r,o]of n)s.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return s.sort((r,o)=>o.score-r.score),s}ne();import{existsSync as wg,mkdirSync as yg,rmSync as Tg,createWriteStream as dk,statSync as pk}from"node:fs";import{join as vr}from"node:path";import{createHash as mk}from"node:crypto";import{readFile as gk}from"node:fs/promises";var fk="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",Rg=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function kg(){return vr(W,"models","BAAI","bge-base-en-v1.5")}function et(){let e=kg();return Rg.every(t=>wg(vr(e,t.path)))}async function Ag(e){let t=kg();yg(t,{recursive:!0}),yg(vr(t,"onnx"),{recursive:!0});for(let n of Rg){let s=vr(t,n.path),r=fk+n.path,o=0;wg(s)&&(o=pk(s).size);let a={};o>0&&(a.Range=`bytes=${o}-`);let c=await fetch(r,{headers:a});if(!c.ok&&c.status!==206)throw new Error(`Failed to download ${n.path}: HTTP ${c.status}`);let u=c.headers.get("content-length"),d=u?o+Number(u):0,p=c.body;if(!p)throw new Error(`No response body for ${n.path}`);let f=dk(s,{flags:o>0?"a":"w"}),S=p.getReader(),y=o;for(;;){let{done:k,value:M}=await S.read();if(k)break;f.write(Buffer.from(M)),y+=M.byteLength,e?.(n.path,y,d)}if(f.end(),await new Promise((k,M)=>{f.on("finish",k),f.on("error",M)}),n.sha256==="TODO_PLACEHOLDER")throw Tg(s),new Error(`Refusing to install: SHA-256 not pinned for ${n.path}. Update model-download.ts.`);let b=await gk(s);if(mk("sha256").update(b).digest("hex")!==n.sha256)throw Tg(s),new Error(`SHA-256 mismatch for ${n.path}`)}}q();var _k=[/\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],hk=1440*60*1e3;function Ek(e){let t=h(),n=t.prepare(`SELECT content_text, tool_names FROM messages
1947
+ 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 Yg(){let e=$0(),t=e.semantic?Promise.resolve().then(()=>{try{return U0()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),n=e.cost?Promise.resolve().then(()=>{try{return H0()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),s=e.git?W0().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,n,s]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}We();H();es();var Aa=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,n){super(`semantic search refused: vec_chunks has ${t} rows (cap ${n}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=n}};function xa(e,t={}){let n=t.limit??q0()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new Aa(r,n)}function q0(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}ns();import{Worker as X0}from"node:worker_threads";import{join as J0}from"node:path";var Na=class extends Error{constructor(n){super(`semantic kNN exceeded ${n}ms wall-clock budget -- query aborted`);this.timeoutMs=n}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},G0=1e4;function Y0(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function Oa(e){let t=e.timeoutMs??Y0()??G0,n=(e.workerFactory??z0)();return new Promise((s,r)=>{let o=setTimeout(()=>{n.terminate().catch(()=>{}),r(new Na(t))},t);n.once("message",a=>{clearTimeout(o);let c=a;c&&c.ok===!0&&Array.isArray(c.hits)?s(c.hits):r(new Error(c?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",a=>{clearTimeout(o),n.terminate().catch(()=>{}),r(a)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function z0(){let e=J0(Tt(),"dist","daemon","query-worker.js"),t={...process.env,RECALL_DB_PROFILE:"worker"};return new X0(e,{env:t})}async function zg(e,t=50){let n=_();return at(n),xa(n),Oa({query:e,limit:t})}async function Kg(e,t=10,n=.65){let s=_();at(s),xa(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=await Oa({precomputedVector:o.embedding,limit:t*5}),c=new Map;for(let d of a){if(d.sessionId===e)continue;let p=c.get(d.sessionId);(p===void 0||d.distance<p)&&c.set(d.sessionId,d.distance)}let u=[];for(let[d,p]of c){let f=1-p;f>=n&&u.push({sessionId:d,similarity:f})}return u.sort((d,p)=>p.similarity-d.similarity),u.slice(0,t)}function K0(){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 Vg(e){let t=K0(),n=new Map;for(let r of e)for(let o=0;o<r.length;o++){let a=r[o],c=1/(t+o+1),u=n.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):n.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let s=[];for(let[r,o]of n)s.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return s.sort((r,o)=>o.score-r.score),s}Z();import{existsSync as ef,mkdirSync as Qg,rmSync as Zg,createWriteStream as V0,statSync as Q0}from"node:fs";import{join as $r}from"node:path";import{createHash as Z0}from"node:crypto";import{readFile as eA}from"node:fs/promises";var tA="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",tf=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function nf(){return $r(W,"models","BAAI","bge-base-en-v1.5")}function Ge(){let e=nf();return tf.every(t=>ef($r(e,t.path)))}async function sf(e){let t=nf();Qg(t,{recursive:!0}),Qg($r(t,"onnx"),{recursive:!0});for(let n of tf){let s=$r(t,n.path),r=tA+n.path,o=0;ef(s)&&(o=Q0(s).size);let a={};o>0&&(a.Range=`bytes=${o}-`);let c=await fetch(r,{headers:a});if(!c.ok&&c.status!==206)throw new Error(`Failed to download ${n.path}: HTTP ${c.status}`);let u=c.headers.get("content-length"),d=u?o+Number(u):0,p=c.body;if(!p)throw new Error(`No response body for ${n.path}`);let f=V0(s,{flags:o>0?"a":"w"}),b=p.getReader(),T=o;for(;;){let{done:A,value:I}=await b.read();if(A)break;f.write(Buffer.from(I)),T+=I.byteLength,e?.(n.path,T,d)}if(f.end(),await new Promise((A,I)=>{f.on("finish",A),f.on("error",I)}),n.sha256==="TODO_PLACEHOLDER")throw Zg(s),new Error(`Refusing to install: SHA-256 not pinned for ${n.path}. Update model-download.ts.`);let S=await eA(s);if(Z0("sha256").update(S).digest("hex")!==n.sha256)throw Zg(s),new Error(`SHA-256 mismatch for ${n.path}`)}}H();var nA=[/\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],sA=1440*60*1e3;function rA(e){let t=_(),n=t.prepare(`SELECT content_text, tool_names FROM messages
1900
1948
  WHERE session_id = ? AND role = 'assistant'
1901
- ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let u of n)if(u.content_text&&_k.some(d=>d.test(u.content_text))){s=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1902
- WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let u of r){let d=u.tool_names??"",p=u.content_text??"";/\bWrite\b|\bEdit\b/.test(d)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(p)&&/pass|ok|✓/i.test(p)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(p)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(p)||/tsc.*(?:0 errors|no errors)/i.test(p))&&(o.buildSuccess=!0)}return s?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:s}:{status:"neutral",evidence:o,claimFound:s}}function bk(e){let t=Ek(e);return h().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function xg(e){let n=h().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(n?.verification_status&&n.verification_computed_at&&Date.now()-n.verification_computed_at<hk){let s={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:n.verification_status,evidence:s,claimFound:n.verification_status!=="neutral"}}return bk(e)}import{readFileSync as Sk,writeFileSync as yk,mkdirSync as Tk,chmodSync as wk}from"node:fs";import{join as Ng}from"node:path";import{homedir as Og}from"node:os";function Cg(){return Ng(Og(),".recall","config.json")}function Lg(){try{return JSON.parse(Sk(Cg(),"utf-8"))}catch{return{}}}function Rk(e){let t=Cg();Tk(Ng(Og(),".recall"),{recursive:!0}),yk(t,JSON.stringify(e,null,2)+`
1903
- `,"utf-8"),wk(t,384)}function ha(){let t=Lg().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function vg(e){let t=Lg();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},Rk(t)}var Ik=5e3,ka={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},Ea=0,ba=ka,Sa=null;async function Mk(){return Date.now()-Ea>=Ik&&!Sa&&(Sa=Os().then(n=>(ba=n,Ea=Date.now(),n)).catch(()=>(Ea=Date.now(),ba=ka,ka)).finally(()=>{Sa=null})),ba}var jk=2e3,Dk=6,jr=new Map;function Gn(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function Pk(e,t){let n=Date.now(),s=(jr.get(e)??[]).filter(a=>n-a.ts<jk);return jr.set(e,s),s.length<2||s[s.length-1].name===t?!1:s.slice(0,-1).some(a=>a.name===t)}function Fk(e,t){let n=jr.get(e)??[];for(n.push({name:t,ts:Date.now()});n.length>Dk;)n.shift();jr.set(e,n)}function Mg(e,t){let n=t.trim();if(!n)return 0;if(le(n)){let o=Ct(n);if(!o)return 0;n=o}if(de(n))return 0;if(Pk(e,n))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${n}", flap signature (competing editor sync sources)`),0;Fk(e,n);let s=j.sessionsFor(e),r=0;for(let o of s)try{if(ye(o)===n)continue;he(o,n),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${n}" propagated to ${r} session(s)`),r}var jg=(()=>{try{let e=Dr(xa(Na(import.meta.url)),"..","..","package.json");return JSON.parse(Ra(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),ya=!1,Ta=!1,wa=!1,Uk=xa(Na(import.meta.url)),Aa=Dr(Uk,"..","web"),Fg=Dr(Aa,"index.html"),Hk=xk(Fg);function Dg(){return h().prepare(`SELECT
1949
+ ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let u of n)if(u.content_text&&nA.some(d=>d.test(u.content_text))){s=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1950
+ WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let u of r){let d=u.tool_names??"",p=u.content_text??"";/\bWrite\b|\bEdit\b/.test(d)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(p)&&/pass|ok|✓/i.test(p)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(p)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(p)||/tsc.*(?:0 errors|no errors)/i.test(p))&&(o.buildSuccess=!0)}return s?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:s}:{status:"neutral",evidence:o,claimFound:s}}function oA(e){let t=rA(e);return _().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function rf(e){let n=_().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(n?.verification_status&&n.verification_computed_at&&Date.now()-n.verification_computed_at<sA){let s={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:n.verification_status,evidence:s,claimFound:n.verification_status!=="neutral"}}return oA(e)}import{readFileSync as iA,writeFileSync as aA,mkdirSync as cA,chmodSync as lA}from"node:fs";import{join as of}from"node:path";import{homedir as af}from"node:os";function cf(){return of(af(),".recall","config.json")}function lf(){try{return JSON.parse(iA(cf(),"utf-8"))}catch{return{}}}function uA(e){let t=cf();cA(of(af(),".recall"),{recursive:!0}),aA(t,JSON.stringify(e,null,2)+`
1951
+ `,"utf-8"),lA(t,384)}function La(){let t=lf().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function uf(e){let t=lf();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},uA(t)}var bA=5e3,Fa={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},Ca=0,va=Fa,Ia=null;async function SA(){return Date.now()-Ca>=bA&&!Ia&&(Ia=js().then(n=>(va=n,Ca=Date.now(),n)).catch(()=>(Ca=Date.now(),va=Fa,Fa)).finally(()=>{Ia=null})),va}var TA=2e3,yA=6,Br=new Map;function Vn(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function wA(e,t){let n=Date.now(),s=(Br.get(e)??[]).filter(a=>n-a.ts<TA);return Br.set(e,s),s.length<2||s[s.length-1].name===t?!1:s.slice(0,-1).some(a=>a.name===t)}function RA(e,t){let n=Br.get(e)??[];for(n.push({name:t,ts:Date.now()});n.length>yA;)n.shift();Br.set(e,n)}function pf(e,t){let n=t.trim();if(!n)return 0;if(ue(n)){let o=vt(n);if(!o)return 0;n=o}if(pe(n))return 0;if(wA(e,n))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${n}", flap signature (competing editor sync sources)`),0;RA(e,n);let s=D.sessionsFor(e),r=0;for(let o of s)try{if(Se(o)===n)continue;_e(o,n),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${n}" propagated to ${r} session(s)`),r}var mf=(()=>{try{let e=Wr(Ua(Ha(import.meta.url)),"..","..","package.json");return JSON.parse(Pa(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),Ma=!1,Da=!1,ja=!1,AA=Ua(Ha(import.meta.url)),$a=Wr(AA,"..","web"),_f=Wr($a,"index.html"),xA=mA(_f);function gf(){return _().prepare(`SELECT
1904
1952
  (SELECT COUNT(*) FROM projects) AS projects,
1905
1953
  (SELECT COUNT(DISTINCT COALESCE(repo_root, decoded_path)) FROM projects) AS repos,
1906
1954
  (SELECT COUNT(*) FROM sessions) AS sessions,
1907
1955
  (SELECT COUNT(*) FROM messages) AS messages,
1908
1956
  (SELECT MIN(started_at) FROM sessions WHERE started_at IS NOT NULL) AS earliest,
1909
- (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var Bk=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,Wk=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function Fe(e,t){if((await rt()).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 Ir=new Map,$g=0,Pg=0,Ug=null,qk=6e4;function Xk(){$g+=1;let e=Date.now();e-Pg<qk||(Pg=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 Jk(){Ug=new Date().toISOString()}var Gk=6e4,Yk=5,Xe=[];function Kk(e){let t=Date.now(),n=Xe[Xe.length-1];if(!n||t-n.ts>=Gk)for(Xe.push({ts:t,remainingChunks:e});Xe.length>Yk;)Xe.shift();if(Xe.length<2)return{deltaPerSec:0};let s=Xe[0],r=Xe[Xe.length-1],o=Math.max(1,(r.ts-s.ts)/1e3),a=r.remainingChunks-s.remainingChunks;return{deltaPerSec:Math.max(0,a/o)}}function zk(){let e=_l();return{silentTerminalRejections:$g,lastTerminalSyncAt:Ug,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns},watcherReindexHotFiles:Ks(10)}}function Mr(e,t){if(t)return"";let n=e;return` AND COALESCE(${n}.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[skill]%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are summarizing a Claude Code session%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are extracting a structured Output Index%' AND COALESCE(${n}.title_quality, '') != 'programmatic'`}function Vk(e){let t=new kk;if(t.use("*",$k({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let m=i.req.raw.headers.get("host")??"";if(!Bk.test(m))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!Wk.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,m)=>{if(l.req.method==="GET"&&l.req.path==="/api/health")return m();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{_=Lk(Buffer.from(g,"utf8"),i)}catch{_=!1}return _?m():(l.req.path.startsWith("/api/terminal/")&&Xk(),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:jg,uptimeSeconds:Math.round(process.uptime()),pipeline:zk()})),t.get("/api/stats",i=>i.json(Dg())),t.get("/api/stats/session/:id",i=>{let l=pg(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=mg(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"),m=l==="7d"?"7d":l==="30d"?"30d":"all";return i.json(gg(m))}),t.post("/api/stats/backfill",async i=>{let l=await i.req.json().catch(()=>({})),m=l.limit?Math.max(1,Math.min(1e5,Number(l.limit))):5e3;if(m>5e3){let _=lg({limit:m});return i.json({mode:"background",started:_,alreadyRunning:!_&&ug(),limit:m,lastRun:ca()})}let g=cg({limit:m});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:m,result:g,lastRun:ca()})}),t.get("/api/stats/health",i=>i.json(fg())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),m=da(l);return m?i.json(m):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:ha()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&vg(l.enabled),i.json({enabled:ha()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),m=xg(l);return i.json(m)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=h().prepare(`SELECT tool_names, raw_json FROM messages
1910
- WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''`).all(l),_=g.length,E=new Set;for(let T of g){if(!/Read|Write|Edit/.test(T.tool_names))continue;let w=T.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(w)for(let x of w){let A=x.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"),m=$o(l);if(m.length>0||i.req.query("refresh")!=="1")return i.json({commits:m});let g=await Fo(l);return i.json({commits:$o(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:Ls(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await rt();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 m=process.env.RECALL_FEEDBACK_API??"https://clauderecall.com/api/feedback",g=m==="https://clauderecall.com/api/feedback",_=await rt(),E=sn(),T=g&&_.tier==="pro"&&E?E.license_jwt:null,w=(()=>{try{let N=Dr(xa(Na(import.meta.url)),"..","..","package.json");return JSON.parse(Ra(N,"utf8")).version}catch{return"unknown"}})(),x=l,A={score:x.score,comment:x.comment??null,surface:"web",version:typeof x.version=="string"?x.version:w,os:typeof x.os=="string"?x.os:process.platform,trigger_kind:typeof x.trigger_kind=="string"?x.trigger_kind:"manual",license_jwt:T};try{let N=await fetch(m,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(A)}),C=await N.json().catch(()=>({}));return i.json(C,N.status)}catch(N){let C=N instanceof Error?N.message:"network error";return i.json({error:"upstream_unreachable",detail:C},502)}}),t.get("/api/discover/today",Fe,async i=>{try{return i.json(await hg())}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:$i(),orphan_projects:Up()})),t.get("/api/bug-synthesis",i=>{let l=i.req.query("scope"),m=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,T=Yp({scope:_,target_id:m??void 0,limit:E});return i.json({results:T})}),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 m=Kp(l);return i.json({counts:Array.from(m.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 m=Ui(l);return m?i.json({result:m}):i.json({error:"not found"},404)}),t.delete("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(zp(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let n=$.object({name:$.string().min(1).max(100),description:$.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),m=n.safeParse(l);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);try{let g=Hp({name:m.data.name,description:m.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 "${m.data.name}" already exists`},409):i.json({error:_},400)}});let s=$.object({name:$.string().min(1).max(100).optional(),description:$.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 m=await i.req.json().catch(()=>null),g=s.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);try{let _=Bp(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)?(Wp(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=$.object({project_id:$.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 m=await i.req.json().catch(()=>null),g=r.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);try{return qp(l,g.data.project_id),i.json({macro_repo:Wt(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")),m=Number(i.req.param("projectId"));return!Number.isFinite(l)||!Number.isFinite(m)?i.json({error:"invalid id"},400):(Xp(l,m),i.json({macro_repo:Wt(l)}))}),t.get("/api/projects",i=>{let l=h(),m=i.req.query("system")==="1"||i.req.query("system")==="true",g=Mr("s",m),_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1957
+ (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var NA=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,OA=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function Fe(e,t){if((await it()).tier!=="pro")return e.json({error:"pro_required",message:"This feature requires a Claude Recall Pro license.",upgrade_url:"https://clauderecall.com/pricing",activate_command:"recall activate <license-key>"},402);await t()}var Ur=new Map,hf=0,ff=0,Ef=null,LA=6e4;function CA(){hf+=1;let e=Date.now();e-ff<LA||(ff=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 vA(){Ef=new Date().toISOString()}var IA=6e4,MA=5,Ye=[];function DA(e){let t=Date.now(),n=Ye[Ye.length-1];if(!n||t-n.ts>=IA)for(Ye.push({ts:t,remainingChunks:e});Ye.length>MA;)Ye.shift();if(Ye.length<2)return{deltaPerSec:0};let s=Ye[0],r=Ye[Ye.length-1],o=Math.max(1,(r.ts-s.ts)/1e3),a=r.remainingChunks-s.remainingChunks;return{deltaPerSec:Math.max(0,a/o)}}function jA(){let e=Ul();return{silentTerminalRejections:hf,lastTerminalSyncAt:Ef,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns},watcherReindexHotFiles:tr(10)}}function Hr(e,t){if(t)return"";let n=e;return` AND COALESCE(${n}.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[skill]%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are summarizing a Claude Code session%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are extracting a structured Output Index%' AND COALESCE(${n}.title_quality, '') != 'programmatic'`}function PA(e){let t=new dA;if(t.use("*",kA({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let m=i.req.raw.headers.get("host")??"";if(!NA.test(m))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!OA.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,m)=>{if(l.req.method==="GET"&&l.req.path==="/api/health")return m();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 h=!1;if(g.length===e.length)try{h=hA(Buffer.from(g,"utf8"),i)}catch{h=!1}return h?m():(l.req.path.startsWith("/api/terminal/")&&CA(),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:mf,uptimeSeconds:Math.round(process.uptime()),pipeline:jA()})),t.get("/api/stats",i=>i.json(gf())),t.get("/api/stats/session/:id",i=>{let l=Wg(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=qg(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"),m=l==="7d"?"7d":l==="30d"?"30d":"all";return i.json(Xg(m))}),t.post("/api/stats/backfill",async i=>{let l=await i.req.json().catch(()=>({})),m=l.limit?Math.max(1,Math.min(1e5,Number(l.limit))):5e3;if(m>5e3){let h=Ug({limit:m});return i.json({mode:"background",started:h,alreadyRunning:!h&&Hg(),limit:m,lastRun:Ta()})}let g=$g({limit:m});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:m,result:g,lastRun:Ta()})}),t.get("/api/stats/health",i=>i.json(Jg())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),m=Ra(l);return m?i.json(m):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:La()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&uf(l.enabled),i.json({enabled:La()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),m=rf(l);return i.json(m)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=_().prepare(`SELECT tool_names, raw_json FROM messages
1958
+ WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''`).all(l),h=g.length,E=new Set;for(let y of g){if(!/Read|Write|Edit/.test(y.tool_names))continue;let R=y.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(R)for(let O of R){let N=O.match(/":\s*"([^"]+)"/);N&&E.add(N[1])}}return i.json({filesReferenced:E.size,toolCallCount:h})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),m=zo(l);if(m.length>0||i.req.query("refresh")!=="1")return i.json({commits:m});let g=await Yo(l);return i.json({commits:zo(l),status:g.status})}),t.get("/api/commits/:sha/session",i=>{let l=i.req.param("sha");return/^[0-9a-fA-F]{4,40}$/.test(l)?i.json({sessions:Ps(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await it();return i.json(l)}),t.post("/api/feedback",async i=>{let l;try{l=await i.req.json()}catch{return i.json({error:"invalid json"},400)}if(!l||typeof l!="object")return i.json({error:"invalid body"},400);let m=process.env.RECALL_FEEDBACK_API??"https://clauderecall.com/api/feedback",g=m==="https://clauderecall.com/api/feedback",h=await it(),E=an(),y=g&&h.tier==="pro"&&E?E.license_jwt:null,R=(()=>{try{let C=Wr(Ua(Ha(import.meta.url)),"..","..","package.json");return JSON.parse(Pa(C,"utf8")).version}catch{return"unknown"}})(),O=l,N={score:O.score,comment:O.comment??null,surface:"web",version:typeof O.version=="string"?O.version:R,os:typeof O.os=="string"?O.os:process.platform,trigger_kind:typeof O.trigger_kind=="string"?O.trigger_kind:"manual",license_jwt:y};try{let C=await fetch(m,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(N)}),x=await C.json().catch(()=>({}));return i.json(x,C.status)}catch(C){let x=C instanceof Error?C.message:"network error";return i.json({error:"upstream_unreachable",detail:x},502)}}),t.get("/api/discover/today",Fe,async i=>{try{return i.json(await Yg())}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:Qi(),orphan_projects:hm()})),t.get("/api/bug-synthesis",i=>{let l=i.req.query("scope"),m=i.req.query("target_id"),g=i.req.query("limit"),h=l==="cluster"||l==="project"?l:void 0,E=g?Math.max(1,Number(g)):50,y=km({scope:h,target_id:m??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 m=Am(l);return i.json({counts:Array.from(m.entries()).map(([g,h])=>({target_id:g,count:h}))})}),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 m=Zi(l);return m?i.json({result:m}):i.json({error:"not found"},404)}),t.delete("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(xm(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let n=P.object({name:P.string().min(1).max(100),description:P.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),m=n.safeParse(l);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);try{let g=Em({name:m.data.name,description:m.data.description??null});return i.json({macro_repo:g},201)}catch(g){let h=g instanceof Error?g.message:String(g);return h.includes("UNIQUE constraint")?i.json({error:`a macro repo named "${m.data.name}" already exists`},409):i.json({error:h},400)}});let s=P.object({name:P.string().min(1).max(100).optional(),description:P.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 m=await i.req.json().catch(()=>null),g=s.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);try{let h=bm(l,g.data);return i.json({macro_repo:h})}catch(h){let E=h instanceof Error?h.message:String(h);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)?(Sm(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=P.object({project_id:P.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 m=await i.req.json().catch(()=>null),g=r.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);try{return Tm(l,g.data.project_id),i.json({macro_repo:Jt(l)})}catch(h){let E=h instanceof Error?h.message:String(h);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")),m=Number(i.req.param("projectId"));return!Number.isFinite(l)||!Number.isFinite(m)?i.json({error:"invalid id"},400):(ym(l,m),i.json({macro_repo:Jt(l)}))}),t.get("/api/projects",i=>{let l=_(),m=i.req.query("system")==="1"||i.req.query("system")==="true",g=Hr("s",m),h=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1911
1959
  COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count,
1912
1960
  COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count,
1913
1961
  MAX(COALESCE(s.ended_at, s.started_at)) AS latest
1914
1962
  FROM projects p
1915
1963
  LEFT JOIN sessions s ON s.project_id = p.id
1916
1964
  GROUP BY p.id
1917
- ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),E=i.req.query("groupBy"),T=E==="repo"||E==="repo-flat"?E:"folder";if(T==="folder")return i.json(_);let w=T==="repo-flat"?"COALESCE(p.main_repo, p.repo_root, p.decoded_path)":"COALESCE(p.repo_root, p.decoded_path)",x=l.prepare(`SELECT ${w} AS repo_root,
1965
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),E=i.req.query("groupBy"),y=E==="repo"||E==="repo-flat"?E:"folder";if(y==="folder")return i.json(h);let R=y==="repo-flat"?"COALESCE(p.main_repo, p.repo_root, p.decoded_path)":"COALESCE(p.repo_root, p.decoded_path)",O=l.prepare(`SELECT ${R} AS repo_root,
1918
1966
  COUNT(DISTINCT p.id) AS folder_count,
1919
1967
  COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count_total,
1920
1968
  COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count_total,
1921
1969
  MAX(COALESCE(s.ended_at, s.started_at)) AS latest,
1922
1970
  MAX(p.is_repo) AS is_repo,
1923
- ${T==="repo-flat"?"0":"MAX(p.is_worktree)"} AS is_worktree,
1924
- ${T==="repo-flat"?"NULL":"MAX(p.main_repo)"} AS main_repo
1971
+ ${y==="repo-flat"?"0":"MAX(p.is_worktree)"} AS is_worktree,
1972
+ ${y==="repo-flat"?"NULL":"MAX(p.main_repo)"} AS main_repo
1925
1973
  FROM projects p
1926
1974
  LEFT JOIN sessions s ON s.project_id = p.id
1927
- GROUP BY ${w}
1928
- ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),A=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1975
+ GROUP BY ${R}
1976
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),N=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1929
1977
  COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count,
1930
1978
  COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count,
1931
1979
  MAX(COALESCE(s.ended_at, s.started_at)) AS latest
1932
1980
  FROM projects p
1933
1981
  LEFT JOIN sessions s ON s.project_id = p.id
1934
- WHERE ${w} = ?
1982
+ WHERE ${R} = ?
1935
1983
  GROUP BY p.id
1936
- ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`),N=x.map(C=>({repo_root:C.repo_root,name:C.repo_root?C.repo_root.split("/").filter(Boolean).pop()??C.repo_root:"(unknown)",is_repo:C.is_repo===1,is_worktree:C.is_worktree===1,main_repo:C.main_repo,session_count_total:C.session_count_total,message_count_total:C.message_count_total,latest:C.latest,folders:A.all(C.repo_root)}));return i.json({groupBy:T,groups:N})}),t.get("/api/graph/:project",i=>{let l=h(),m=i.req.param("project"),g=l.prepare(`SELECT id, name, decoded_path FROM projects
1937
- WHERE name = ? LIMIT 1`).get(m);if(!g)return i.json({error:`project "${m}" not found`},404);let _=i.req.query("system")==="1"||i.req.query("system")==="true",E=l.prepare(`SELECT s.id,
1984
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`),C=O.map(x=>({repo_root:x.repo_root,name:x.repo_root?x.repo_root.split("/").filter(Boolean).pop()??x.repo_root:"(unknown)",is_repo:x.is_repo===1,is_worktree:x.is_worktree===1,main_repo:x.main_repo,session_count_total:x.session_count_total,message_count_total:x.message_count_total,latest:x.latest,folders:N.all(x.repo_root)}));return i.json({groupBy:y,groups:C})}),t.get("/api/graph/:project",i=>{let l=_(),m=i.req.param("project"),g=l.prepare(`SELECT id, name, decoded_path FROM projects
1985
+ WHERE name = ? LIMIT 1`).get(m);if(!g)return i.json({error:`project "${m}" not found`},404);let h=i.req.query("system")==="1"||i.req.query("system")==="true",E=l.prepare(`SELECT s.id,
1938
1986
  s.auto_title,
1939
1987
  s.auto_title_source,
1940
1988
  s.first_user_message,
@@ -1944,12 +1992,12 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1944
1992
  sa.alias AS alias
1945
1993
  FROM sessions s
1946
1994
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1947
- WHERE s.project_id = ?${Mr("s",_)}
1948
- ORDER BY s.started_at`).all(g.id),T=E.map(A=>A.id),w=[];if(T.length>0){let A=T.map(()=>"?").join(",");w=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1995
+ WHERE s.project_id = ?${Hr("s",h)}
1996
+ ORDER BY s.started_at`).all(g.id),y=E.map(N=>N.id),R=[];if(y.length>0){let N=y.map(()=>"?").join(",");R=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1949
1997
  FROM thread_edges
1950
- WHERE session_id IN (${A})
1998
+ WHERE session_id IN (${N})
1951
1999
  AND (parent_session_id IS NULL
1952
- OR parent_session_id IN (${A}))`).all(...T,...T)}let x=E.map(A=>{let N=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:N,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:x,thread_edges:w})});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,m=i.req.query("target_id")??void 0,g=i.req.query("type"),_=i.req.query("approved"),E=i.req.query("limit"),T;if(g){if(!o.has(g))return i.json({error:`invalid type: ${g}`},400);T=g}let w=_==="1"||_==="true",x=E?Number(E):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<1))return i.json({error:"invalid limit"},400);try{let A=is({sourceSessionId:l,targetSessionId:m,linkType:T,approvedOnly:w,limit:x});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"),m=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"),T;if(l){if(!a.has(l))return i.json({error:`invalid status: ${l}`},400);T=l}let w;if(_){if(!c.has(_))return i.json({error:`invalid inferred_by: ${_}`},400);w=_}let x=E?Number(E):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<1))return i.json({error:"invalid limit"},400);try{let A=St({status:T,sourceSessionId:m,targetSessionId:g,inferredBy:w,limit:x}),N=new Set;for(let O of A)N.add(O.source_session_id),N.add(O.target_session_id);let C=new Map;if(N.size>0){let O=Array.from(N),H=O.map(()=>"?").join(","),Q=h().prepare(`SELECT s.id,
2000
+ OR parent_session_id IN (${N}))`).all(...y,...y)}let O=E.map(N=>{let C=N.alias??N.auto_title??N.first_user_message??N.id.slice(0,8),x=null,k=null;if(N.auto_title?.startsWith("/")){let L=N.auto_title.split(" \xB7 ");x=L[0],k=L.length>1?L.slice(1).join(" \xB7 "):null}return{id:N.id.slice(0,8),full_id:N.id,title:C,alias:N.alias,auto_title:N.auto_title,auto_title_source:N.auto_title_source,title_quality:N.title_quality,started_at:N.started_at,msgs:N.message_count,skill:x,brand:k}});return i.json({project:g,sessions:O,thread_edges:R})});let o=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),a=new Set(["pending","approved","rejected"]),c=new Set(["L1","L2","L3","L4","user"]);t.get("/api/links",i=>{let l=i.req.query("source_id")??void 0,m=i.req.query("target_id")??void 0,g=i.req.query("type"),h=i.req.query("approved"),E=i.req.query("limit"),y;if(g){if(!o.has(g))return i.json({error:`invalid type: ${g}`},400);y=g}let R=h==="1"||h==="true",O=E?Number(E):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);try{let N=ps({sourceSessionId:l,targetSessionId:m,linkType:y,approvedOnly:R,limit:O});return i.json({links:N})}catch(N){return i.json({error:N.message},400)}}),t.get("/api/links/suggestions",i=>{let l=i.req.query("status"),m=i.req.query("source_id")??void 0,g=i.req.query("target_id")??void 0,h=i.req.query("inferred_by"),E=i.req.query("limit"),y;if(l){if(!a.has(l))return i.json({error:`invalid status: ${l}`},400);y=l}let R;if(h){if(!c.has(h))return i.json({error:`invalid inferred_by: ${h}`},400);R=h}let O=E?Number(E):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);try{let N=yt({status:y,sourceSessionId:m,targetSessionId:g,inferredBy:R,limit:O}),C=new Set;for(let L of N)C.add(L.source_session_id),C.add(L.target_session_id);let x=new Map;if(C.size>0){let L=Array.from(C),$=L.map(()=>"?").join(","),V=_().prepare(`SELECT s.id,
1953
2001
  NULLIF(sa.alias, '') AS alias,
1954
2002
  s.auto_title,
1955
2003
  s.first_user_message,
@@ -1957,7 +2005,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1957
2005
  FROM sessions s
1958
2006
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1959
2007
  LEFT JOIN projects p ON p.id = s.project_id
1960
- WHERE s.id IN (${H})`).all(...O);for(let Y of Q){let B=Y.first_user_message?Y.first_user_message.slice(0,80):null,X=Y.alias??Y.auto_title??B??Y.id.slice(0,8);C.set(Y.id,{title:X,project:Y.project})}}let I=A.map(O=>{let H=C.get(O.source_session_id),J=C.get(O.target_session_id);return{...O,source_title:H?.title??O.source_session_id.slice(0,8),source_project:H?.project??null,target_title:J?.title??O.target_session_id.slice(0,8),target_project:J?.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 m=ot(l);return m?i.json(m):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 m=i.req.query("budget"),g=m!==void 0?Number(m):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=_,T=i.req.query("max_depth"),w=T!==void 0?Number(T):2;if(!Number.isFinite(w)||w<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let x,A=i.req.query("edge_types");if(A){let H=A.split(",").map(J=>J.trim()).filter(Boolean);for(let J of H)if(!o.has(J))return i.json({error:`invalid edge_type: ${J}`},400);x=H}let N=i.req.query("include_wiki_links"),C=N===void 0?!0:!(N==="0"||N==="false"),I=i.req.query("include_suggestions"),O=I==="1"||I==="true";try{let H=mr(l,{budget:g,scoring:E,maxDepth:w,edgeTypes:x,includeWikiLinks:C,includeSuggestions:O});return i.json(H)}catch(H){let J=H instanceof Error?H.message:"unknown error",Q=/not found/.test(J)?404:500;return i.json({error:J},Q)}}),t.get("/api/bug-patterns",i=>{let l=i.req.query("min_count"),m=i.req.query("status"),g=i.req.query("project")??void 0,_=i.req.query("limit"),E=i.req.query("offset"),T=l?Number(l):void 0;if(T!==void 0&&(!Number.isFinite(T)||T<1))return i.json({error:"min_count must be a positive integer"},400);let w;if(m==="open")w=!1;else if(m==="resolved")w=!0;else if(m&&m!=="all")return i.json({error:`invalid status: ${m}; valid: open, resolved, all`},400);let x=_?Number(_):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<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 N=Za({minOccurrenceCount:T,hasResolved:w,project:g,limit:x,offset:A});return i.json(N)}catch(N){return i.json({error:N.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=h(),g=l.prepare(`SELECT p.name AS project,
2008
+ WHERE s.id IN (${$})`).all(...L);for(let G of V){let B=G.first_user_message?G.first_user_message.slice(0,80):null,X=G.alias??G.auto_title??B??G.id.slice(0,8);x.set(G.id,{title:X,project:G.project})}}let k=N.map(L=>{let $=x.get(L.source_session_id),q=x.get(L.target_session_id);return{...L,source_title:$?.title??L.source_session_id.slice(0,8),source_project:$?.project??null,target_title:q?.title??L.target_session_id.slice(0,8),target_project:q?.project??null}});return i.json({suggestions:k})}catch(N){return i.json({error:N.message},400)}}),t.get("/api/output-index/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let m=lt(l);return m?i.json(m):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 m=i.req.query("budget"),g=m!==void 0?Number(m):4e3;if(!Number.isFinite(g)||g<100)return i.json({error:"budget must be a number \u2265 100"},400);let h=i.req.query("scoring")??"hybrid";if(!u.has(h))return i.json({error:`invalid scoring: ${h}; valid: pagerank, embedding-rerank, hybrid`},400);let E=h,y=i.req.query("max_depth"),R=y!==void 0?Number(y):2;if(!Number.isFinite(R)||R<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let O,N=i.req.query("edge_types");if(N){let $=N.split(",").map(q=>q.trim()).filter(Boolean);for(let q of $)if(!o.has(q))return i.json({error:`invalid edge_type: ${q}`},400);O=$}let C=i.req.query("include_wiki_links"),x=C===void 0?!0:!(C==="0"||C==="false"),k=i.req.query("include_suggestions"),L=k==="1"||k==="true";try{let $=Sr(l,{budget:g,scoring:E,maxDepth:R,edgeTypes:O,includeWikiLinks:x,includeSuggestions:L});return i.json($)}catch($){let q=$ instanceof Error?$.message:"unknown error",V=/not found/.test(q)?404:500;return i.json({error:q},V)}}),t.get("/api/bug-patterns",i=>{let l=i.req.query("min_count"),m=i.req.query("status"),g=i.req.query("project")??void 0,h=i.req.query("limit"),E=i.req.query("offset"),y=l?Number(l):void 0;if(y!==void 0&&(!Number.isFinite(y)||y<1))return i.json({error:"min_count must be a positive integer"},400);let R;if(m==="open")R=!1;else if(m==="resolved")R=!0;else if(m&&m!=="all")return i.json({error:`invalid status: ${m}; valid: open, resolved, all`},400);let O=h?Number(h):void 0;if(O!==void 0&&(!Number.isFinite(O)||O<1))return i.json({error:"invalid limit"},400);let N=E?Number(E):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<0))return i.json({error:"invalid offset"},400);try{let C=Ec({minOccurrenceCount:y,hasResolved:R,project:g,limit:O,offset:N});return i.json(C)}catch(C){return i.json({error:C.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=_(),g=l.prepare(`SELECT p.name AS project,
1961
2009
  COUNT(s.id) AS total_sessions,
1962
2010
  SUM(CASE WHEN oi.session_id IS NOT NULL THEN 1 ELSE 0 END) AS extracted_sessions,
1963
2011
  MAX(oi.extracted_at) AS last_extracted_at
@@ -1965,20 +2013,20 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1965
2013
  LEFT JOIN sessions s ON s.project_id = p.id
1966
2014
  LEFT JOIN session_output_index oi ON oi.session_id = s.id
1967
2015
  GROUP BY p.id
1968
- ORDER BY total_sessions DESC`).all().map(T=>({project:T.project,total_sessions:T.total_sessions??0,extracted_sessions:T.extracted_sessions??0,remaining_sessions:(T.total_sessions??0)-(T.extracted_sessions??0),last_extracted_at:T.last_extracted_at})),_=g.reduce((T,w)=>(T.total_sessions+=w.total_sessions,T.extracted_sessions+=w.extracted_sessions,T.remaining_sessions+=w.remaining_sessions,T),{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=$.object({project:$.string().min(1),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:$.number().int().positive().optional(),force:$.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=De(i);if(l)return l;let m=Ri();if(m>0&&m<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(m/1024**3).toFixed(2)} GB free \u2014 extract-outputs needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:m},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??lt,limit:_.data.limit??200,force:_.data.force??!1},T=Ji();if(E.limit>T.sessionCeiling)return oe({kind:"run-rejected",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,reason:`limit ${E.limit} exceeds session ceiling ${T.sessionCeiling}`}),i.json({error:`requested limit ${E.limit} exceeds session ceiling ${T.sessionCeiling}. Lower the limit or edit launcher.sessionCeiling in ~/.recall/config.json.`},400);let w=jn(E.project);if(!w)return i.json({error:`project "${E.project}" not found`},404);let A=xt({projectId:w.id,limit:E.limit,force:E.force}).eligible.length,N=Gi(A),C=Ve(A,E.model),I=qt(),O=N.estimated_input_tokens_max+N.estimated_output_tokens_max>I.remaining_tokens_24h;if(oe({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:A}),A===0)return i.json({eligible_session_count:0,...N,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:H,expiresAt:J}=Bi(E);return i.json({preflight_token:H,expires_at:new Date(J).toISOString(),eligible_session_count:A,...N,plan_window_estimate:C,budget:I,would_exceed_budget:O})});let p=$.object({preflight_token:$.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=p.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);let _=Wi(g.data.preflight_token);if(!_)return oe({kind:"run-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let E=qt(),w=(()=>{let C=jn(_.project);return C?xt({projectId:C.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,x=Gi(w),A=x.estimated_input_tokens_max+x.estimated_output_tokens_max;if(A>E.remaining_tokens_24h)return oe({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${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 N=im({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(!zi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return tt(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of am(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=zi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/extract-outputs/jobs/:jobId",i=>{let l=De(i);return l||(cm(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(qt()));let f=$.object({scope:$.enum(["cluster","project"]),target_id:$.string().min(1),mode:$.enum(["synopsis","priorities","root_cause"]),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let J=Ri();if(J>0&&J<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(J/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:J},507)}let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=f.safeParse(m);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??lm},E=Er(_);if(!E)return oe({kind:"synth-rejected",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:"target not found"}),i.json({error:_.scope==="cluster"?`cluster "${_.target_id}" not found in any extracted findings`:`project "${_.target_id}" has no extracted findings to synthesize`},404);let T=Yi({scope:_.scope,mode:_.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),w=T.estimated_input_tokens_max+T.estimated_output_tokens_max,x=om(w),A=Ve(x,_.model),N=qt(),I=T.estimated_input_tokens_max+T.estimated_output_tokens_max>N.remaining_tokens_24h;oe({kind:"synth-preflight",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:`${_.scope}/${_.mode}/${_.target_id}`});let{token:O,expiresAt:H}=Bi({project:_.target_id,model:_.model,limit:1,force:!1});return Ir.set(O,_),setTimeout(()=>Ir.delete(O),9e4).unref?.(),i.json({preflight_token:O,expires_at:new Date(H).toISOString(),estimated_input_tokens_max:T.estimated_input_tokens_max,estimated_output_tokens_max:T.estimated_output_tokens_max,plan_window_estimate:A,budget:N,would_exceed_budget:I,context_summary:E.context_summary})});let S=$.object({preflight_token:$.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=S.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);let _=Wi(g.data.preflight_token),E=Ir.get(g.data.preflight_token)??null;if(Ir.delete(g.data.preflight_token),!E||!_)return oe({kind:"synth-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let T=qt(),w=Er(E);if(!w)return i.json({error:E.scope==="cluster"?`cluster "${E.target_id}" no longer exists`:`project "${E.target_id}" has no findings`},404);let x=Yi({scope:E.scope,mode:E.mode,member_session_count:w.context_summary.session_count,cluster_count:w.context_summary.cluster_count}),A=x.estimated_input_tokens_max+x.estimated_output_tokens_max;if(A>T.remaining_tokens_24h)return oe({kind:"synth-rejected",job_id:null,project:E.scope==="project"?E.target_id:null,model:E.model,limit:null,origin:i.req.header("origin")??null,reason:`projected spend ${A} exceeds remaining 24h budget ${T.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:T,projected_spend:A},429);let N=mm({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(!Vi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return tt(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of gm(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=Vi(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=De(i);return l||(fm(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,m=Math.min(Math.max(1,Number(i.req.query("limit")??100)),500),g=h(),_=["oi.bug_signatures IS NOT NULL"],E=[];l&&(_.push("p.name = ?"),E.push(l));let w=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
2016
+ 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})),h=g.reduce((y,R)=>(y.total_sessions+=R.total_sessions,y.extracted_sessions+=R.extracted_sessions,y.remaining_sessions+=R.remaining_sessions,y),{total_sessions:0,extracted_sessions:0,remaining_sessions:0}),E=l.prepare("SELECT COUNT(*) AS n FROM bug_pattern_clusters").get();return i.json({projects:g,totals:{...h,cluster_count:E.n}})});let d=P.object({project:P.string().min(1),model:P.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:P.number().int().positive().optional(),force:P.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=je(i);if(l)return l;let m=Pi();if(m>0&&m<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(m/1024**3).toFixed(2)} GB free \u2014 extract-outputs needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:m},507);let g=await i.req.json().catch(()=>null),h=d.safeParse(g);if(!h.success)return i.json({error:"invalid request body",details:h.error.format()},400);let E={project:h.data.project,model:h.data.model??mt,limit:h.data.limit??200,force:h.data.force??!1},y=oa();if(E.limit>y.sessionCeiling)return oe({kind:"run-rejected",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,reason:`limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}`}),i.json({error:`requested limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}. Lower the limit or edit launcher.sessionCeiling in ~/.recall/config.json.`},400);let R=$n(E.project);if(!R)return i.json({error:`project "${E.project}" not found`},404);let N=Ot({projectId:R.id,limit:E.limit,force:E.force}).eligible.length,C=ia(N),x=tt(N,E.model),k=Gt(),L=C.estimated_input_tokens_max+C.estimated_output_tokens_max>k.remaining_tokens_24h;if(oe({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:N}),N===0)return i.json({eligible_session_count:0,...C,plan_window_estimate:x,budget:k,would_exceed_budget:!1,preflight_token:null,expires_at:null,message:"No eligible sessions to extract. Pass force=true to re-extract sessions already at the current extractor version."});let{token:$,expiresAt:q}=ta(E);return i.json({preflight_token:$,expires_at:new Date(q).toISOString(),eligible_session_count:N,...C,plan_window_estimate:x,budget:k,would_exceed_budget:L})});let p=P.object({preflight_token:P.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=je(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=p.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);let h=na(g.data.preflight_token);if(!h)return oe({kind:"run-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let E=Gt(),R=(()=>{let x=$n(h.project);return x?Ot({projectId:x.id,limit:h.limit,force:h.force}):null})()?.eligible.length??0,O=ia(R),N=O.estimated_input_tokens_max+O.estimated_output_tokens_max;if(N>E.remaining_tokens_24h)return oe({kind:"run-rejected",job_id:null,project:h.project,model:h.model,limit:h.limit,origin:i.req.header("origin")??null,reason:`projected spend ${N} exceeds remaining 24h budget ${E.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:E,projected_spend:N},429);let C=Pm({project:h.project,model:h.model,limit:h.limit,force:h.force,origin:i.req.header("origin")??null});return"error"in C?i.json({error:C.error},400):i.json({jobId:C.jobId,reused:C.reused},C.reused?409:200)}),t.get("/api/extract-outputs/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!la(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async h=>{let E=!1,y=setInterval(()=>{E||h.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of Fm(l,g))if(E||(await h.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=la(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/extract-outputs/jobs/:jobId",i=>{let l=je(i);return l||($m(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(Gt()));let f=P.object({scope:P.enum(["cluster","project"]),target_id:P.string().min(1),mode:P.enum(["synopsis","priorities","root_cause"]),model:P.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let q=Pi();if(q>0&&q<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(q/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:q},507)}let l=je(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=f.safeParse(m);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let h={scope:g.data.scope,target_id:g.data.target_id,mode:g.data.mode,model:g.data.model??Um},E=kr(h);if(!E)return oe({kind:"synth-rejected",job_id:null,project:h.scope==="project"?h.target_id:null,model:h.model,limit:null,origin:i.req.header("origin")??null,reason:"target not found"}),i.json({error:h.scope==="cluster"?`cluster "${h.target_id}" not found in any extracted findings`:`project "${h.target_id}" has no extracted findings to synthesize`},404);let y=aa({scope:h.scope,mode:h.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),R=y.estimated_input_tokens_max+y.estimated_output_tokens_max,O=jm(R),N=tt(O,h.model),C=Gt(),k=y.estimated_input_tokens_max+y.estimated_output_tokens_max>C.remaining_tokens_24h;oe({kind:"synth-preflight",job_id:null,project:h.scope==="project"?h.target_id:null,model:h.model,limit:null,origin:i.req.header("origin")??null,reason:`${h.scope}/${h.mode}/${h.target_id}`});let{token:L,expiresAt:$}=ta({project:h.target_id,model:h.model,limit:1,force:!1});return Ur.set(L,h),setTimeout(()=>Ur.delete(L),9e4).unref?.(),i.json({preflight_token:L,expires_at:new Date($).toISOString(),estimated_input_tokens_max:y.estimated_input_tokens_max,estimated_output_tokens_max:y.estimated_output_tokens_max,plan_window_estimate:N,budget:C,would_exceed_budget:k,context_summary:E.context_summary})});let b=P.object({preflight_token:P.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=je(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=b.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);let h=na(g.data.preflight_token),E=Ur.get(g.data.preflight_token)??null;if(Ur.delete(g.data.preflight_token),!E||!h)return oe({kind:"synth-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let y=Gt(),R=kr(E);if(!R)return i.json({error:E.scope==="cluster"?`cluster "${E.target_id}" no longer exists`:`project "${E.target_id}" has no findings`},404);let O=aa({scope:E.scope,mode:E.mode,member_session_count:R.context_summary.session_count,cluster_count:R.context_summary.cluster_count}),N=O.estimated_input_tokens_max+O.estimated_output_tokens_max;if(N>y.remaining_tokens_24h)return oe({kind:"synth-rejected",job_id:null,project:E.scope==="project"?E.target_id:null,model:E.model,limit:null,origin:i.req.header("origin")??null,reason:`projected spend ${N} exceeds remaining 24h budget ${y.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:y,projected_spend:N},429);let C=qm({intent:E,origin:i.req.header("origin")??null});return"error"in C?i.json({error:C.error},400):i.json({jobId:C.jobId,reused:C.reused},C.reused?409:200)}),t.get("/api/bug-patterns/synthesize/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!ua(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async h=>{let E=!1,y=setInterval(()=>{E||h.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of Xm(l,g))if(E||(await h.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=ua(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=je(i);return l||(Jm(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,m=Math.min(Math.max(1,Number(i.req.query("limit")??100)),500),g=_(),h=["oi.bug_signatures IS NOT NULL"],E=[];l&&(h.push("p.name = ?"),E.push(l));let R=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1969
2017
  s.started_at, oi.extracted_at, oi.bug_signatures
1970
2018
  FROM session_output_index oi
1971
2019
  JOIN sessions s ON s.id = oi.session_id
1972
2020
  JOIN projects p ON p.id = s.project_id
1973
- WHERE ${_.join(" AND ")}
2021
+ WHERE ${h.join(" AND ")}
1974
2022
  ORDER BY oi.extracted_at DESC
1975
- LIMIT ?`).all(...E,m).map(I=>{let O=[];try{let H=JSON.parse(I.bug_signatures);Array.isArray(H)&&(O=H)}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}}),x=w.flatMap(I=>I.rawSignatures.map(O=>O.message_hash).filter(O=>typeof O=="string")),A=Pi(x),N=w.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 H=O.message_hash?A.get(O.message_hash)??null:null;return{...O,resolved:Fi(H),resolution:H}}),signature_count:I.rawSignatures.length})),C=N.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:N,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 m=await i.req.json().catch(()=>({})),g=Fp({messageHash:l,resolvedInSessionId:m.resolved_in_session_id??null,fixSummary:m.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):($p(l),i.json({ok:!0}))}),t.get("/api/bug-patterns/graph",i=>{let l=i.req.query("project")??null,m=i.req.query("include_resolved")!=="0",g=h(),_=["oi.bug_signatures IS NOT NULL","oi.bug_signatures != '[]'"],E=[];l&&(_.push("p.name = ?"),E.push(l));let T=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
2023
+ LIMIT ?`).all(...E,m).map(k=>{let L=[];try{let $=JSON.parse(k.bug_signatures);Array.isArray($)&&(L=$)}catch{L=[]}return{session_id:k.session_id,project:k.project,auto_title:k.auto_title,started_at:k.started_at,extracted_at:k.extracted_at,rawSignatures:L}}),O=R.flatMap(k=>k.rawSignatures.map(L=>L.message_hash).filter(L=>typeof L=="string")),N=Ki(O),C=R.map(k=>({session_id:k.session_id,project:k.project,auto_title:k.auto_title,started_at:k.started_at,extracted_at:k.extracted_at,signatures:k.rawSignatures.map(L=>{let $=L.message_hash?N.get(L.message_hash)??null:null;return{...L,resolved:Vi($),resolution:$}}),signature_count:k.rawSignatures.length})),x=C.reduce((k,L)=>(k.sessions_total+=1,L.signature_count>0?(k.sessions_with_findings+=1,k.total_findings+=L.signature_count):k.sessions_empty+=1,k),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:C,totals:x})}),t.post("/api/bug-signatures/:hash/resolve",async i=>{let l=i.req.param("hash");if(!l||l.length<4)return i.json({error:"invalid message hash"},400);let m=await i.req.json().catch(()=>({})),g=fm({messageHash:l,resolvedInSessionId:m.resolved_in_session_id??null,fixSummary:m.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):(_m(l),i.json({ok:!0}))}),t.get("/api/bug-patterns/graph",i=>{let l=i.req.query("project")??null,m=i.req.query("include_resolved")!=="0",g=_(),h=["oi.bug_signatures IS NOT NULL","oi.bug_signatures != '[]'"],E=[];l&&(h.push("p.name = ?"),E.push(l));let y=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1976
2024
  s.started_at, oi.extracted_at, oi.bug_signatures
1977
2025
  FROM session_output_index oi
1978
2026
  JOIN sessions s ON s.id = oi.session_id
1979
2027
  JOIN projects p ON p.id = s.project_id
1980
- WHERE ${_.join(" AND ")}
1981
- ORDER BY oi.extracted_at DESC`).all(...E),w=[];for(let B of T){let X=[];try{let z=JSON.parse(B.bug_signatures);Array.isArray(z)&&(X=z)}catch{continue}for(let z of X)w.push({sig:z,session_id:B.session_id,project:B.project,auto_title:B.auto_title})}let x=new Map;for(let B of w){let X=B.sig.message_hash??`nohash:${(B.sig.snippet??"").slice(0,64)}`,z=x.get(X);z?z.push(B):x.set(X,[B])}let A=Array.from(x.keys()).filter(B=>!B.startsWith("nohash:")),N=Pi(A),C=[],I=new Map,O=[];for(let[B,X]of x){let z=X[0],te=z.sig.message_hash??null,Z=te?N.get(te)??null:null,K=Fi(Z);if(!m&&K)continue;let ie=Array.from(new Set(X.map(ge=>ge.project))).sort(),nt=Array.from(new Set(X.map(ge=>ge.session_id))),$e={id:te??B,message_hash:te,error_type:z.sig.error_type??null,snippet:(z.sig.snippet??"").slice(0,200),file:z.sig.file??null,occurrence_count:X.length,projects:ie,resolved:K,fix_summary:Z?.fix_summary??null,member_session_ids:nt};C.push($e);for(let ge of X)I.has(ge.session_id)||I.set(ge.session_id,{session_id:ge.session_id,project:ge.project,auto_title:ge.auto_title}),O.push({cluster_id:$e.id,session_id:ge.session_id})}let H=[],J=4,Q=new Map;function Y(B){let X=Q.get(B)??0;return X>=J?!1:(Q.set(B,X+1),!0)}for(let B=0;B<C.length;B+=1)for(let X=B+1;X<C.length;X+=1){let z=C[B],te=C[X],Z=null;z.error_type&&z.error_type!=="unknown"&&z.error_type===te.error_type?Z="same_error_type":z.file&&te.file&&z.file===te.file&&(Z="same_file"),Z&&(!Y(z.id)||!Y(te.id)||H.push({a:z.id,b:te.id,reason:Z}))}return i.json({clusters:C,sessions:Array.from(I.values()),member_edges:O,related_edges:H,totals:{cluster_count:C.length,singleton_count:C.filter(B=>B.occurrence_count===1).length,recurring_count:C.filter(B=>B.occurrence_count>1).length,session_count:I.size,resolved_count:C.filter(B=>B.resolved).length}})}),t.get("/api/bug-patterns/:clusterId",i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let m=ec(l);return m?i.json(m):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 m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);let g=m.resolved_in_session_id,_=m.fix_summary;if(typeof g!="string"||g.length===0)return i.json({error:"resolved_in_session_id required"},400);if(typeof _!="string"||_.trim().length===0)return i.json({error:"fix_summary required"},400);try{let E=tc(l,g,_);return i.json(E)}catch(E){let T=E instanceof Error?E.message:"unknown error",w=/not found/.test(T)?404:(/not a member/.test(T),400);return i.json({error:T},w)}}),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 m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);let g=m.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=nc(l,_);return i.json(E)}catch(E){let T=E instanceof Error?E.message:"unknown error",w=/not found/.test(T)?404:(/cannot split|none of the supplied/.test(T),400);return i.json({error:T},w)}}),t.post("/api/links",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let m=l.source_session_id,g=l.target_session_id,_=l.link_type;if(typeof m!="string"||m.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(m===g)return i.json({error:"a session cannot link to itself"},400);let E=h();if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(m))return i.json({error:`source session not found: ${m}`},404);if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(g))return i.json({error:`target session not found: ${g}`},404);let x=is({sourceSessionId:g,targetSessionId:m,linkType:"wiki_link"});if(x.length>0)return i.json({link:x[0]});try{let A=Tc({source_session_id:m,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"),m=Number(l);if(!Number.isInteger(m)||m<=0)return i.json({error:"id must be a positive integer"},400);let g=wc(m);return g.removed===0?i.json({error:`link ${m} 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 m=h();if(!m.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=_,T=un(l).filter(I=>I.link_type===E),w=gp(l,T);if(w.length===0)return i.json({links:[]});let x=w.map(I=>I.otherSessionId),A=x.map(()=>"?").join(","),N=m.prepare(`SELECT s.id,
2028
+ WHERE ${h.join(" AND ")}
2029
+ ORDER BY oi.extracted_at DESC`).all(...E),R=[];for(let B of y){let X=[];try{let z=JSON.parse(B.bug_signatures);Array.isArray(z)&&(X=z)}catch{continue}for(let z of X)R.push({sig:z,session_id:B.session_id,project:B.project,auto_title:B.auto_title})}let O=new Map;for(let B of R){let X=B.sig.message_hash??`nohash:${(B.sig.snippet??"").slice(0,64)}`,z=O.get(X);z?z.push(B):O.set(X,[B])}let N=Array.from(O.keys()).filter(B=>!B.startsWith("nohash:")),C=Ki(N),x=[],k=new Map,L=[];for(let[B,X]of O){let z=X[0],te=z.sig.message_hash??null,Q=te?C.get(te)??null:null,Y=Vi(Q);if(!m&&Y)continue;let ie=Array.from(new Set(X.map(ge=>ge.project))).sort(),ot=Array.from(new Set(X.map(ge=>ge.session_id))),$e={id:te??B,message_hash:te,error_type:z.sig.error_type??null,snippet:(z.sig.snippet??"").slice(0,200),file:z.sig.file??null,occurrence_count:X.length,projects:ie,resolved:Y,fix_summary:Q?.fix_summary??null,member_session_ids:ot};x.push($e);for(let ge of X)k.has(ge.session_id)||k.set(ge.session_id,{session_id:ge.session_id,project:ge.project,auto_title:ge.auto_title}),L.push({cluster_id:$e.id,session_id:ge.session_id})}let $=[],q=4,V=new Map;function G(B){let X=V.get(B)??0;return X>=q?!1:(V.set(B,X+1),!0)}for(let B=0;B<x.length;B+=1)for(let X=B+1;X<x.length;X+=1){let z=x[B],te=x[X],Q=null;z.error_type&&z.error_type!=="unknown"&&z.error_type===te.error_type?Q="same_error_type":z.file&&te.file&&z.file===te.file&&(Q="same_file"),Q&&(!G(z.id)||!G(te.id)||$.push({a:z.id,b:te.id,reason:Q}))}return i.json({clusters:x,sessions:Array.from(k.values()),member_edges:L,related_edges:$,totals:{cluster_count:x.length,singleton_count:x.filter(B=>B.occurrence_count===1).length,recurring_count:x.filter(B=>B.occurrence_count>1).length,session_count:k.size,resolved_count:x.filter(B=>B.resolved).length}})}),t.get("/api/bug-patterns/:clusterId",i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let m=bc(l);return m?i.json(m):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 m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);let g=m.resolved_in_session_id,h=m.fix_summary;if(typeof g!="string"||g.length===0)return i.json({error:"resolved_in_session_id required"},400);if(typeof h!="string"||h.trim().length===0)return i.json({error:"fix_summary required"},400);try{let E=Sc(l,g,h);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",R=/not found/.test(y)?404:(/not a member/.test(y),400);return i.json({error:y},R)}}),t.post("/api/bug-patterns/:clusterId/split",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);let g=m.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 h=[];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);h.push(E)}try{let E=Tc(l,h);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",R=/not found/.test(y)?404:(/cannot split|none of the supplied/.test(y),400);return i.json({error:y},R)}}),t.post("/api/links",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let m=l.source_session_id,g=l.target_session_id,h=l.link_type;if(typeof m!="string"||m.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 h!="string")return i.json({error:"link_type required"},400);if(!o.has(h))return i.json({error:`invalid link_type: ${h}`},400);if(h!=="wiki_link")return i.json({error:`link_type '${h}' is not user-writable; only wiki_link is exposed via this endpoint. Other types must go through the suggestions-queue review flow.`},403);if(m===g)return i.json({error:"a session cannot link to itself"},400);let E=_();if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(m))return i.json({error:`source session not found: ${m}`},404);if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(g))return i.json({error:`target session not found: ${g}`},404);let O=ps({sourceSessionId:g,targetSessionId:m,linkType:"wiki_link"});if(O.length>0)return i.json({link:O[0]});try{let N=Hc({source_session_id:m,target_session_id:g,link_type:"wiki_link",confidence:1,source:"manual",evidence:l.evidence??{created_via:"context_menu"},approved:!0});return i.json({link:N})}catch(N){return i.json({error:N.message},400)}}),t.delete("/api/links/:id",i=>{let l=i.req.param("id"),m=Number(l);if(!Number.isInteger(m)||m<=0)return i.json({error:"id must be a positive integer"},400);let g=Bc(m);return g.removed===0?i.json({error:`link ${m} 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 m=_();if(!m.prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:`session not found: ${l}`},404);let h=i.req.query("type")??"wiki_link";if(!o.has(h))return i.json({error:`invalid type: ${h}`},400);let E=h,y=mn(l).filter(k=>k.link_type===E),R=Xp(l,y);if(R.length===0)return i.json({links:[]});let O=R.map(k=>k.otherSessionId),N=O.map(()=>"?").join(","),C=m.prepare(`SELECT s.id,
1982
2030
  NULLIF(sa.alias, '') AS alias,
1983
2031
  s.auto_title,
1984
2032
  s.first_user_message,
@@ -1986,7 +2034,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1986
2034
  FROM sessions s
1987
2035
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1988
2036
  LEFT JOIN projects p ON p.id = s.project_id
1989
- WHERE s.id IN (${A})`).all(...x),C=new Map(N.map(I=>[I.id,I]));return i.json({links:w.map(I=>{let O=C.get(I.otherSessionId),H=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:H,project:O?.project??null}})})}),t.patch("/api/links/suggestions/:id",async i=>{let l=i.req.param("id"),m=Number(l);if(!Number.isInteger(m)||m<=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 _=eo(m,g.status);return i.json(_)}catch(_){let E=_.message,T=/already decided/.test(E)?409:/not found/.test(E)?404:400;return i.json({error:E},T)}}),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 m=Array.isArray(l.ids)?l.ids:null;if(!m||m.length===0)return i.json({error:"ids must be a non-empty array"},400);if(m.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,T=[];for(let w of m){let x=Number(w);if(!Number.isInteger(x)||x<=0){T.push({id:Number.isFinite(Number(w))?Number(w):-1,error:"invalid id"});continue}try{eo(x,g),_+=1}catch(A){let N=A.message;/already decided/.test(N)?E+=1:T.push({id:x,error:N})}}return i.json({decided:_,skipped:E,errors:T})}),t.get("/api/sessions",i=>{let l=h(),m=i.req.query("project"),g=i.req.query("since"),_=i.req.query("until"),E=i.req.queries("tag")??[],T=i.req.query("collection"),w=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),x=i.req.query("cursor"),A=null;if(x)try{let Q=Buffer.from(x,"base64url").toString("utf8"),Y=JSON.parse(Q);typeof Y.ts=="string"&&typeof Y.id=="string"&&(A={ts:Y.ts,id:Y.id})}catch{}let N=i.req.query("system")==="1"||i.req.query("system")==="true",C={limit:w},I="s.message_count > 2"+Mr("s",N);if(m&&(I+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",C.proj=`%${Gn(m)}%`),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(Y=>it(Y)).filter(Boolean).forEach((Y,B)=>{I+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${B})`,C[`tag_${B}`]=Y}),T){let Q=Go(T);if(Q.length===0)return i.json({items:[],nextCursor:null});let Y=Q.map((B,X)=>`@col_${X}`).join(",");I+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${Y}))`,Q.forEach((B,X)=>{C[`col_${X}`]=B})}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,
2037
+ WHERE s.id IN (${N})`).all(...O),x=new Map(C.map(k=>[k.id,k]));return i.json({links:R.map(k=>{let L=x.get(k.otherSessionId),$=L?.alias?.trim()||L?.auto_title?.trim()||(L?.first_user_message?L.first_user_message.slice(0,80):"")||k.otherSessionId.slice(0,8);return{linkId:k.linkId,otherSessionId:k.otherSessionId,direction:k.direction,updatedAt:k.updatedAt,title:$,project:L?.project??null}})})}),t.patch("/api/links/suggestions/:id",async i=>{let l=i.req.param("id"),m=Number(l);if(!Number.isInteger(m)||m<=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 h=co(m,g.status);return i.json(h)}catch(h){let E=h.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 m=Array.isArray(l.ids)?l.ids:null;if(!m||m.length===0)return i.json({error:"ids must be a non-empty array"},400);if(m.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,h=0,E=0,y=[];for(let R of m){let O=Number(R);if(!Number.isInteger(O)||O<=0){y.push({id:Number.isFinite(Number(R))?Number(R):-1,error:"invalid id"});continue}try{co(O,g),h+=1}catch(N){let C=N.message;/already decided/.test(C)?E+=1:y.push({id:O,error:C})}}return i.json({decided:h,skipped:E,errors:y})}),t.get("/api/sessions",i=>{let l=_(),m=i.req.query("project"),g=i.req.query("since"),h=i.req.query("until"),E=i.req.queries("tag")??[],y=i.req.query("collection"),R=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),O=i.req.query("cursor"),N=null;if(O)try{let V=Buffer.from(O,"base64url").toString("utf8"),G=JSON.parse(V);typeof G.ts=="string"&&typeof G.id=="string"&&(N={ts:G.ts,id:G.id})}catch{}let C=i.req.query("system")==="1"||i.req.query("system")==="true",x={limit:R},k="s.message_count > 2"+Hr("s",C);if(m&&(k+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",x.proj=`%${Vn(m)}%`),g&&(k+=" AND s.started_at >= @since",x.since=g),h&&(k+=" AND s.started_at <= @until",/^\d{4}-\d{2}-\d{2}$/.test(h)?x.until=`${h}T23:59:59.999Z`:x.until=h),E.length>0&&E.map(G=>ut(G)).filter(Boolean).forEach((G,B)=>{k+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${B})`,x[`tag_${B}`]=G}),y){let V=si(y);if(V.length===0)return i.json({items:[],nextCursor:null});let G=V.map((B,X)=>`@col_${X}`).join(",");k+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${G}))`,V.forEach((B,X)=>{x[`col_${X}`]=B})}N&&(k+=" AND (COALESCE(s.ended_at, s.started_at, '') < @cursor_ts OR (COALESCE(s.ended_at, s.started_at, '') = @cursor_ts AND s.id < @cursor_id))",x.cursor_ts=N.ts,x.cursor_id=N.id);let L=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1990
2038
  s.message_count, s.first_user_message, s.git_branch,
1991
2039
  s.auto_title, s.auto_title_source, s.verification_status,
1992
2040
  COALESCE(s.ended_at, s.started_at, '') AS _cursor_ts,
@@ -2005,17 +2053,17 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2005
2053
  JOIN projects p ON p.id = s.project_id
2006
2054
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2007
2055
  LEFT JOIN session_notes sn ON sn.session_id = s.id
2008
- WHERE ${I}
2056
+ WHERE ${k}
2009
2057
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC, s.id DESC
2010
- LIMIT @limit`).all(C),H=O.map(({tags_csv:Q,_cursor_ts:Y,...B})=>{let X=B.id,z=j.getOrigin(X),te=B.alias,Z=te==null?null:j.isSessionAutoLinked(X)?"auto":"manual",K=bn({auto_title:B.auto_title,auto_title_source:B.auto_title_source??null,has_alias:te!=null&&Z==="manual"});return{...B,tags:Q?Q.split(","):[],origin:z?{editor:z.editor,label:z.label}:null,alias_source:Z,title_quality:K}}),J=null;if(O.length===w&&O.length>0){let Q=O[O.length-1],Y=JSON.stringify({ts:Q._cursor_ts??"",id:Q.id});J=Buffer.from(Y,"utf8").toString("base64url")}return i.json({items:H,nextCursor:J})}),t.get("/api/sessions/:id",i=>{let l=h(),m=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
2058
+ LIMIT @limit`).all(x),$=L.map(({tags_csv:V,_cursor_ts:G,...B})=>{let X=B.id,z=D.getOrigin(X),te=B.alias,Q=te==null?null:D.isSessionAutoLinked(X)?"auto":"manual",Y=wn({auto_title:B.auto_title,auto_title_source:B.auto_title_source??null,has_alias:te!=null&&Q==="manual"});return{...B,tags:V?V.split(","):[],origin:z?{editor:z.editor,label:z.label}:null,alias_source:Q,title_quality:Y}}),q=null;if(L.length===R&&L.length>0){let V=L[L.length-1],G=JSON.stringify({ts:V._cursor_ts??"",id:V.id});q=Buffer.from(G,"utf8").toString("base64url")}return i.json({items:$,nextCursor:q})}),t.get("/api/sessions/:id",i=>{let l=_(),m=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
2011
2059
  NULLIF(sa.alias, '') AS alias
2012
2060
  FROM sessions s
2013
2061
  JOIN projects p ON p.id = s.project_id
2014
2062
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2015
- WHERE s.id = ?`).get(m);if(!g)return i.json({error:"not found"},404);let _=mn(m),E=j.getOrigin(m),T=E?{editor:E.editor,label:E.label}:null,w=g.alias==null?null:j.isSessionAutoLinked(m)?"auto":"manual",x=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2063
+ WHERE s.id = ?`).get(m);if(!g)return i.json({error:"not found"},404);let h=_n(m),E=D.getOrigin(m),y=E?{editor:E.editor,label:E.label}:null,R=g.alias==null?null:D.isSessionAutoLinked(m)?"auto":"manual",O=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2016
2064
  FROM messages
2017
2065
  WHERE session_id = ?
2018
- ORDER BY COALESCE(timestamp, ''), rowid`).all(m);return i.json({session:{...g,tags:_,origin:T,alias_source:w},messages:x})}),t.get("/api/tags",i=>i.json(Tt())),t.get("/api/sessions/:id/tags",i=>i.json({tags:mn(i.req.param("id"))})),t.post("/api/sessions/:id/tags",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.tag!="string")return i.json({error:"tag required"},400);try{let g=yt(l,m.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"),m=i.req.param("tag");return i.json(Ic(l,m))}),t.get("/api/config/auto-tag",i=>i.json(ji(Be()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),m=fr.partial().safeParse(l);if(!m.success)return i.json({error:"invalid config",issues:m.error.issues},400);let g=m.data;g.apiKey===void 0&&delete g.apiKey;let _=Mp(g);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&Ar(),i.json(ji(_))}),t.get("/api/onboarding",i=>{let m=h().prepare(`SELECT s.id,
2066
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(m);return i.json({session:{...g,tags:h,origin:y,alias_source:R},messages:O})}),t.get("/api/tags",i=>i.json(Rt())),t.get("/api/sessions/:id/tags",i=>i.json({tags:_n(i.req.param("id"))})),t.post("/api/sessions/:id/tags",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.tag!="string")return i.json({error:"tag required"},400);try{let g=wt(l,m.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"),m=i.req.param("tag");return i.json(Vc(l,m))}),t.get("/api/config/auto-tag",i=>i.json(Yi(qe()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),m=yr.partial().safeParse(l);if(!m.success)return i.json({error:"invalid config",issues:m.error.issues},400);let g=m.data;g.apiKey===void 0&&delete g.apiKey;let h=dm(g);return h.autopilot&&h.enabled&&h.backend==="api"&&h.apiKey&&Ir(),i.json(Yi(h))}),t.get("/api/onboarding",i=>{let m=_().prepare(`SELECT s.id,
2019
2067
  p.name AS project,
2020
2068
  s.started_at,
2021
2069
  s.ended_at,
@@ -2027,7 +2075,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2027
2075
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2028
2076
  WHERE s.message_count > 2
2029
2077
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
2030
- LIMIT 1`).get();return i.json({state:Or(),mostRecentSession:m??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),m=Nr.partial().safeParse(l);return m.success?i.json(sg(m.data)):i.json({error:"invalid onboarding state",issues:m.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(rg())),t.get("/api/config/mcp-install",i=>i.json({...qe(),claudeCliAvailable:me()})),t.post("/api/config/mcp-install",i=>i.json({...oa(),claudeCliAvailable:me()})),t.delete("/api/config/mcp-install",i=>i.json({...Vm(),claudeCliAvailable:me()}));let y=$.object({scope:$.object({untaggedOnly:$.boolean().optional(),project:$.string().optional(),collectionId:$.string().optional(),sessionIds:$.array($.string()).optional(),limit:$.number().int().min(1).max(500).optional()}).default({}),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:$.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(ya)return i.json({error:"a scan is already running"},409);if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!qe().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let m=await i.req.json().catch(()=>({})),g=y.safeParse(m);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=g.data.scope,E=Be(),T=g.data.model??E.model,w=h(),x=()=>w.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,A=x();ya=!0;let N;try{let C=g.data.scanId;N=await lo(_,{model:T,scanId:C});let I=x(),O=Math.max(0,I-A);return C&&ls(C,{type:"done",result:{success:N.success,exitCode:N.exitCode,tagsAdded:O}}),i.json({success:N.success,exitCode:N.exitCode,tagsAdded:O,model:T,stdout:Se(N.stdout.slice(0,2e3)).redacted,stderrTail:Se(N.stderr.slice(-2e3)).redacted})}finally{ya=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return tt(i,async m=>{let g=[],_={resolve:()=>{}},E=new Promise(A=>{_.resolve=A}),T=Dc(l,A=>{g.push(A);let N=_.resolve;E=new Promise(C=>{_.resolve=C}),N()}),w=!1,x=setInterval(()=>{w||m.writeSSE({event:"heartbeat",data:""}).catch(()=>{w=!0})},15e3);try{for(;!w;){g.length===0&&await E;let A=g.shift();if(A&&(await m.writeSSE({event:A.type,data:JSON.stringify(A)}),A.type==="done"))break}}finally{w=!0,clearInterval(x),T()}})}),t.get("/api/prompts",i=>i.json({prompts:io.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:me()})),t.post("/api/prompts/run",async i=>{if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!qe().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let m=await i.req.json().catch(()=>({})),_=$.object({name:$.string(),args:$.record($.string(),$.unknown()).optional(),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).safeParse(m);if(!_.success)return i.json({error:"invalid request",issues:_.error.issues},400);let E=jc(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let T=E.build(_.data.args??{}),w=Be(),x=_.data.model??w.model,A=await at(T,E.allowedTools,{model:x});return i.json({success:A.success,exitCode:A.exitCode,promptName:E.name,model:x,stdout:A.stdout,stderrTail:A.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(Xn())),t.get("/api/autopilot/events",i=>tt(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(Xn())});let m=[],g=()=>{},_=new Promise(T=>g=T),E=Jm(T=>{m.push(T);let w=g;_=new Promise(x=>g=x),w()});try{for(;;){if(m.length===0){let w=new Promise(A=>setTimeout(()=>A("tick"),3e4));if(await Promise.race([_.then(()=>"event"),w])==="tick"){await l.writeSSE({event:"heartbeat",data:"1"});continue}}let T=m.shift();T&&await l.writeSSE({event:"state",data:JSON.stringify(T)})}}finally{E()}})),t.post("/api/autopilot/kick",i=>(Ar(),i.json({ok:!0,snapshot:Xn()})));let b=$.object({scope:$.object({untaggedOnly:$.boolean().optional(),project:$.string().optional(),collectionId:$.string().optional(),sessionIds:$.array($.string()).optional(),limit:$.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=Be();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 m=await i.req.json().catch(()=>({})),g=b.safeParse(m);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=Rt(g.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=$m(_.length);return qm(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=yr(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let{controller:m,listeners:g,..._}=l;return i.json(_)}),t.get("/api/tags/scan/:id/events",i=>{let l=yr(i.req.param("id"));return l?tt(i,async m=>{await m.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let w of l.results)await m.writeSSE({event:"result",data:JSON.stringify(w)});let g=[],_={resolve:()=>{}},E=new Promise(w=>{_.resolve=w}),T=Um(l,w=>{g.push(w);let x=_.resolve;E=new Promise(A=>{_.resolve=A}),x()});try{for(;l.status==="running"||l.status==="pending";){g.length===0&&await E;let w=g.shift();if(w&&(await m.writeSSE({event:w.type,data:JSON.stringify(w)}),w.type==="done"||w.type==="status"&&(w.status==="cancelled"||w.status==="failed")))break}}finally{T()}}):i.json({error:"scan not found"},404)});let R=$.object({selection:$.array($.object({sessionId:$.string(),tags:$.array($.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=yr(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let m=await i.req.json().catch(()=>({})),g=R.safeParse(m);if(!g.success)return i.json({error:"invalid selection"},400);let _=Xm(l,g.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return Hm(l),Bm(l),i.json({ok:!0})}),t.put("/api/sessions/:id/alias",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.alias!="string")return i.json({error:"alias required"},400);try{let g=he(l,m.alias);if(m.pin===!0)j.unlinkSession(l);else{let _=h().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=_?.cwd?_.cwd.replace(/\/+$/,""):null,T=!1;if(E&&_?.started_at){let w=Date.parse(_.started_at),x=_.started_at,A=j.all().filter(N=>N.cwd&&N.cwd.replace(/\/+$/,"")===E&&Cn({sessionStartedAt:x,terminalOpenedAt:N.opened_at??null}).allowed);if(Number.isFinite(w)&&A.length>0){let C=A.map(I=>({t:I,gap:w-Date.parse(I.opened_at??"")})).filter(I=>Number.isFinite(I.gap)).sort((I,O)=>I.gap-O.gap)[0];C&&(j.linkSession(l,C.t.shell_pid),T=!0)}}T||j.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 ks(l),j.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:ye(l)})}),t.get("/api/config/auto-title",i=>i.json(dt())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),m=Ds.partial().safeParse(l);return m.success?i.json(ru(m.data)):i.json({error:"invalid config",issues:m.error.issues},400)}),t.get("/api/sessions/:id/auto-title",i=>{let l=i.req.param("id"),m=ve(l);return m?i.json(m):i.json({error:"session not found"},404)}),t.post("/api/sessions/:id/auto-title",async i=>{let l=i.req.param("id");if(!dt().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!h().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await Yl(l);return Ee(l,E,"agent"),i.json(ve(l))}catch(E){return i.json({error:E.message,code:"agent-title-failed"},500)}}),t.post("/api/sessions/:id/auto-title/revert",i=>{let l=i.req.param("id"),m=ve(l);if(!m)return i.json({error:"session not found"},404);let g=m.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(ve(l))}),t.post("/api/sessions/:id/regenerate-title",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({})),g=m.model??_r;try{let _=await Di(l,{model:g,force:m.force===!0,budget:typeof m.budget=="number"?m.budget:void 0,signal:i.req.raw.signal}),E=ve(l),T=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:T})}catch(_){if(_ instanceof Bt)return i.json({error:_.message,code:"no-context-available",session_id:_.sessionId},422);let E=_ instanceof Error?_.message:"unknown error",T=/not found|unknown/i.test(E)?404:500;return i.json({error:E,code:"regenerate-failed"},T)}}),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 m=l.project;if(typeof m!="string"||m.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 T=typeof l.model=="string"&&l.model.length>0?l.model:_r,w=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,x=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,N=h().prepare(`SELECT s.id,
2078
+ LIMIT 1`).get();return i.json({state:jr(),mostRecentSession:m??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),m=Dr.partial().safeParse(l);return m.success?i.json(Mg(m.data)):i.json({error:"invalid onboarding state",issues:m.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(Dg())),t.get("/api/config/mcp-install",i=>i.json({...Je(),claudeCliAvailable:me()})),t.post("/api/config/mcp-install",i=>i.json({...Ea(),claudeCliAvailable:me()})),t.delete("/api/config/mcp-install",i=>i.json({...Ng(),claudeCliAvailable:me()}));let T=P.object({scope:P.object({untaggedOnly:P.boolean().optional(),project:P.string().optional(),collectionId:P.string().optional(),sessionIds:P.array(P.string()).optional(),limit:P.number().int().min(1).max(500).optional()}).default({}),model:P.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:P.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(Ma)return i.json({error:"a scan is already running"},409);if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!Je().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let m=await i.req.json().catch(()=>({})),g=T.safeParse(m);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let h=g.data.scope,E=qe(),y=g.data.model??E.model,R=_(),O=()=>R.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,N=O();Ma=!0;let C;try{let x=g.data.scanId;C=await Eo(h,{model:y,scanId:x});let k=O(),L=Math.max(0,k-N);return x&&fs(x,{type:"done",result:{success:C.success,exitCode:C.exitCode,tagsAdded:L}}),i.json({success:C.success,exitCode:C.exitCode,tagsAdded:L,model:y,stdout:be(C.stdout.slice(0,2e3)).redacted,stderrTail:be(C.stderr.slice(-2e3)).redacted})}finally{Ma=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return rt(i,async m=>{let g=[],h={resolve:()=>{}},E=new Promise(N=>{h.resolve=N}),y=el(l,N=>{g.push(N);let C=h.resolve;E=new Promise(x=>{h.resolve=x}),C()}),R=!1,O=setInterval(()=>{R||m.writeSSE({event:"heartbeat",data:""}).catch(()=>{R=!0})},15e3);try{for(;!R;){g.length===0&&await E;let N=g.shift();if(N&&(await m.writeSSE({event:N.type,data:JSON.stringify(N)}),N.type==="done"))break}}finally{R=!0,clearInterval(O),y()}})}),t.get("/api/prompts",i=>i.json({prompts:fo.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:me()})),t.post("/api/prompts/run",async i=>{if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!Je().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let m=await i.req.json().catch(()=>({})),h=P.object({name:P.string(),args:P.record(P.string(),P.unknown()).optional(),model:P.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).safeParse(m);if(!h.success)return i.json({error:"invalid request",issues:h.error.issues},400);let E=Zc(h.data.name);if(!E)return i.json({error:`unknown prompt: ${h.data.name}`},404);let y=E.build(h.data.args??{}),R=qe(),O=h.data.model??R.model,N=await dt(y,E.allowedTools,{model:O});return i.json({success:N.success,exitCode:N.exitCode,promptName:E.name,model:O,stdout:N.stdout,stderrTail:N.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(zn())),t.get("/api/autopilot/events",i=>rt(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(zn())});let m=[],g=()=>{},h=new Promise(y=>g=y),E=wg(y=>{m.push(y);let R=g;h=new Promise(O=>g=O),R()});try{for(;;){if(m.length===0){let R=new Promise(N=>setTimeout(()=>N("tick"),3e4));if(await Promise.race([h.then(()=>"event"),R])==="tick"){await l.writeSSE({event:"heartbeat",data:"1"});continue}}let y=m.shift();y&&await l.writeSSE({event:"state",data:JSON.stringify(y)})}}finally{E()}})),t.post("/api/autopilot/kick",i=>(Ir(),i.json({ok:!0,snapshot:zn()})));let S=P.object({scope:P.object({untaggedOnly:P.boolean().optional(),project:P.string().optional(),collectionId:P.string().optional(),sessionIds:P.array(P.string()).optional(),limit:P.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=qe();if(!l.enabled)return i.json({error:"auto-tagging is disabled"},403);if(l.backend!=="api")return i.json({error:"api-backend scan requires backend=api in config"},400);if(!l.apiKey)return i.json({error:"no api key configured"},400);let m=await i.req.json().catch(()=>({})),g=S.safeParse(m);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let h=At(g.data.scope);if(h.length===0)return i.json({error:"no sessions match scope"},400);let E=_g(h.length);return Tg(E,{apiKey:l.apiKey,model:l.model,minTags:l.minTagsPerSession,maxTags:l.maxTagsPerSession,sessions:h}),i.json({scanId:E.id,total:E.total})}),t.get("/api/tags/scan/:id",i=>{let l=Nr(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let{controller:m,listeners:g,...h}=l;return i.json(h)}),t.get("/api/tags/scan/:id/events",i=>{let l=Nr(i.req.param("id"));return l?rt(i,async m=>{await m.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let R of l.results)await m.writeSSE({event:"result",data:JSON.stringify(R)});let g=[],h={resolve:()=>{}},E=new Promise(R=>{h.resolve=R}),y=hg(l,R=>{g.push(R);let O=h.resolve;E=new Promise(N=>{h.resolve=N}),O()});try{for(;l.status==="running"||l.status==="pending";){g.length===0&&await E;let R=g.shift();if(R&&(await m.writeSSE({event:R.type,data:JSON.stringify(R)}),R.type==="done"||R.type==="status"&&(R.status==="cancelled"||R.status==="failed")))break}}finally{y()}}):i.json({error:"scan not found"},404)});let w=P.object({selection:P.array(P.object({sessionId:P.string(),tags:P.array(P.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=Nr(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let m=await i.req.json().catch(()=>({})),g=w.safeParse(m);if(!g.success)return i.json({error:"invalid selection"},400);let h=yg(l,g.data.selection);return i.json(h)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return Eg(l),bg(l),i.json({ok:!0})}),t.put("/api/sessions/:id/alias",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.alias!="string")return i.json({error:"alias required"},400);try{let g=_e(l,m.alias);if(m.pin===!0)D.unlinkSession(l);else{let h=_().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=h?.cwd?h.cwd.replace(/\/+$/,""):null,y=!1;if(E&&h?.started_at){let R=Date.parse(h.started_at),O=h.started_at,N=D.all().filter(C=>C.cwd&&C.cwd.replace(/\/+$/,"")===E&&Mn({sessionStartedAt:O,terminalOpenedAt:C.opened_at??null}).allowed);if(Number.isFinite(R)&&N.length>0){let x=N.map(k=>({t:k,gap:R-Date.parse(k.opened_at??"")})).filter(k=>Number.isFinite(k.gap)).sort((k,L)=>k.gap-L.gap)[0];x&&(D.linkSession(l,x.t.shell_pid),y=!0)}}y||D.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 vs(l),D.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:Se(l)})}),t.get("/api/config/auto-title",i=>i.json(ft())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),m=Bs.partial().safeParse(l);return m.success?i.json(Cu(m.data)):i.json({error:"invalid config",issues:m.error.issues},400)}),t.get("/api/sessions/:id/auto-title",i=>{let l=i.req.param("id"),m=ve(l);return m?i.json(m):i.json({error:"session not found"},404)}),t.post("/api/sessions/:id/auto-title",async i=>{let l=i.req.param("id");if(!ft().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!_().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await Tu(l);return he(l,E,"agent"),i.json(ve(l))}catch(E){return i.json({error:E.message,code:"agent-title-failed"},500)}}),t.post("/api/sessions/:id/auto-title/revert",i=>{let l=i.req.param("id"),m=ve(l);if(!m)return i.json({error:"session not found"},404);let g=m.auto_title_history;if(!g||g.length===0)return i.json({error:"no prior title to revert to",code:"no-history"},422);let h=g[g.length-1];return he(l,h.title,"agent"),i.json(ve(l))}),t.post("/api/sessions/:id/regenerate-title",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({})),g=m.model??wr;try{let h=await zi(l,{model:g,force:m.force===!0,budget:typeof m.budget=="number"?m.budget:void 0,signal:i.req.raw.signal}),E=ve(l),y=E?.auto_title_history&&E.auto_title_history.length>0?E.auto_title_history[E.auto_title_history.length-1].title:null;return i.json({...h,previous_title:y})}catch(h){if(h instanceof Xt)return i.json({error:h.message,code:"no-context-available",session_id:h.sessionId},422);let E=h instanceof Error?h.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 m=l.project;if(typeof m!="string"||m.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 h=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(!h.has(L))return i.json({error:`quality_filter must be a subset of ${[...h].join(",")}; got ${L}`},400);E.push(L)}let y=typeof l.model=="string"&&l.model.length>0?l.model:wr,R=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,O=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,C=_().prepare(`SELECT s.id,
2031
2079
  s.auto_title,
2032
2080
  s.auto_title_source,
2033
2081
  NULLIF(sa.alias, '') AS alias
@@ -2036,16 +2084,16 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2036
2084
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2037
2085
  WHERE p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\'
2038
2086
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
2039
- LIMIT @limit`).all({proj:`%${Gn(m)}%`,limit:w}),C=new Set(E),I=N.filter(O=>{let H=O.alias==null?null:j.isSessionAutoLinked(O.id)?"auto":"manual",J=bn({auto_title:O.auto_title,auto_title_source:O.auto_title_source??null,has_alias:O.alias!=null&&H==="manual"});return C.has(J)});return tt(i,async O=>{let H=I.length,J=[],Q=[],Y=[],B=0,X=async(te,Z)=>{B+=1;try{await O.writeSSE({id:String(B),event:te,data:JSON.stringify(Z)})}catch{}};await X("start",{total:H,model:T});let z=0;for(let te of I){if(i.req.raw.signal.aborted)break;z+=1;try{let Z=await Di(te.id,{model:T,budget:x,signal:i.req.raw.signal});Z.written?(J.push(te.id),await X("progress",{sessionId:te.id,title:Z.title,evidence:Z.evidence,confidence:Z.confidence,current:z,total:H})):(Q.push({sessionId:te.id,reason:Z.skipped??"unknown"}),await X("skipped",{sessionId:te.id,reason:Z.skipped??"unknown",current:z,total:H}))}catch(Z){let K=Z instanceof Error?Z.message:String(Z),ie=Z instanceof Bt?"no-context-available":"failed";Y.push({sessionId:te.id,error:K}),await X("error",{sessionId:te.id,error:K,code:ie,current:z,total:H})}}await X("done",{generated:J,skipped:Q,failed:Y,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),m=nr(l);return m?i.json(m):i.body(null,204)}),t.put("/api/sessions/:id/notes",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.content!="string")return i.json({error:"content required (string)"},400);try{let g=vd(l,m.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(!dt().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await Id(l),_=Md(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(mo())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),m=ms.partial().safeParse(l);return m.success?(gs(m.data,"api"),i.json(mo())):i.json({error:"invalid semantic config",issues:m.error.issues},400)}),t.get("/api/semantic/config",i=>i.json(ae())),t.post("/api/semantic/backfill",Fe,async i=>{if(wa)return i.json({error:"a scan is already running"},409);if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let m=await i.req.json().catch(()=>({})),g=Math.max(1,Math.min(5e3,Number(m.limit??200)));return wa=!0,hs({limit:g,force:!!m.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{wa=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",Fe,async i=>{if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let m=i.req.param("id");if(!m)return i.json({error:"session id required"},400);let g=await _s(m);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=pe(),m=Me(),g=et(),_=(i.req.query("project")??"").trim(),E=_.length>512?"":_,T=null,w=0,x=h();if(E){let X=x.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(E,E);X?T=x.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(X.id).n:T=0}let A=x.prepare(`SELECT COALESCE(SUM(s.message_count), 0) AS n
2087
+ LIMIT @limit`).all({proj:`%${Vn(m)}%`,limit:R}),x=new Set(E),k=C.filter(L=>{let $=L.alias==null?null:D.isSessionAutoLinked(L.id)?"auto":"manual",q=wn({auto_title:L.auto_title,auto_title_source:L.auto_title_source??null,has_alias:L.alias!=null&&$==="manual"});return x.has(q)});return rt(i,async L=>{let $=k.length,q=[],V=[],G=[],B=0,X=async(te,Q)=>{B+=1;try{await L.writeSSE({id:String(B),event:te,data:JSON.stringify(Q)})}catch{}};await X("start",{total:$,model:y});let z=0;for(let te of k){if(i.req.raw.signal.aborted)break;z+=1;try{let Q=await zi(te.id,{model:y,budget:O,signal:i.req.raw.signal});Q.written?(q.push(te.id),await X("progress",{sessionId:te.id,title:Q.title,evidence:Q.evidence,confidence:Q.confidence,current:z,total:$})):(V.push({sessionId:te.id,reason:Q.skipped??"unknown"}),await X("skipped",{sessionId:te.id,reason:Q.skipped??"unknown",current:z,total:$}))}catch(Q){let Y=Q instanceof Error?Q.message:String(Q),ie=Q instanceof Xt?"no-context-available":"failed";G.push({sessionId:te.id,error:Y}),await X("error",{sessionId:te.id,error:Y,code:ie,current:z,total:$})}}await X("done",{generated:q,skipped:V,failed:G,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),m=lr(l);return m?i.json(m):i.body(null,204)}),t.put("/api/sessions/:id/notes",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.content!="string")return i.json({error:"content required (string)"},400);try{let g=lp(l,m.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(!ft().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await up(l),h=dp(l,g);return i.json(h)}catch(g){let h=g.message,E=/no messages available/i.test(h)?404:500;return i.json({error:h},E)}}),t.get("/api/semantic/status",i=>i.json(To())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),m=bs.partial().safeParse(l);return m.success?(Ss(m.data,"api"),i.json(To())):i.json({error:"invalid semantic config",issues:m.error.issues},400)}),t.get("/api/semantic/config",i=>i.json(ae())),t.post("/api/semantic/backfill",Fe,async i=>{if(ja)return i.json({error:"a scan is already running"},409);if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let m=await i.req.json().catch(()=>({})),g=Math.max(1,Math.min(5e3,Number(m.limit??200)));return ja=!0,ws({limit:g,force:!!m.force}).catch(h=>console.error("[semantic.backfill] error:",h)).finally(()=>{ja=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",Fe,async i=>{if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let m=i.req.param("id");if(!m)return i.json({error:"session id required"},400);let g=await ys(m);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=le(),m=Me(),g=Ge(),h=(i.req.query("project")??"").trim(),E=h.length>512?"":h,y=null,R=0,O=_();if(E){let X=O.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(E,E);X?y=O.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(X.id).n:y=0}let N=O.prepare(`SELECT COALESCE(SUM(s.message_count), 0) AS n
2040
2088
  FROM chunk_queue cq
2041
- JOIN sessions s ON s.id = cq.session_id`).get(),N=Number(A.n)||0,C=ii(),I=C?.chunksPerSec??0,O=C?.samples??0,H=Kk(N),Q=I>0?Math.ceil(N/I):0,Y=null;if(O>=5&&I>0){let X=I-H.deltaPerSec;X>0&&H.deltaPerSec>0?Y=Math.ceil(N/X):(H.deltaPerSec<=0,Y=null)}let B="no-samples";return O>=5&&I>0&&(B=H.deltaPerSec>0?"live":"stable"),I>0&&T!==null&&(w=Math.ceil(T/I)),i.json({embedder:l,worker:m,modelInstalled:g,project:E||null,queueDepthForProject:T,etaForProject:w,remainingChunks:N,throughput:{chunksPerSec:I,samples:O,source:C?"local-measured":"no-samples-yet"},etaSeconds:Q,etaSecondsDetail:{currentQueue:Q,withGrowth:Y,source:B},queueGrowthChunksPerSec:H.deltaPerSec})}),t.post("/api/semantic/install",Fe,async i=>{if(et())return i.json({ok:!0,status:"already_installed"});if(Ta)return i.json({error:"a scan is already running"},409);Ta=!0;try{return await Ag(),await Ge(),Nn(),i.json({ok:!0,status:"installed"})}catch(l){let m=l instanceof Error?l.message:"unknown error";return i.json({ok:!1,error:m},500)}finally{Ta=!1}}),t.get("/api/semantic/reindex-preview",Fe,async i=>{let l=(i.req.query("project")??"").trim();if(!l)return i.json({error:"project name required"},400);let m=h(),g=m.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 _=m.prepare(`SELECT
2089
+ JOIN sessions s ON s.id = cq.session_id`).get(),C=Number(N.n)||0,x=_i(),k=x?.chunksPerSec??0,L=x?.samples??0,$=DA(C),V=k>0?Math.ceil(C/k):0,G=null;if(L>=5&&k>0){let X=k-$.deltaPerSec;X>0&&$.deltaPerSec>0?G=Math.ceil(C/X):($.deltaPerSec<=0,G=null)}let B="no-samples";return L>=5&&k>0&&(B=$.deltaPerSec>0?"live":"stable"),k>0&&y!==null&&(R=Math.ceil(y/k)),i.json({embedder:l,worker:m,modelInstalled:g,project:E||null,queueDepthForProject:y,etaForProject:R,remainingChunks:C,throughput:{chunksPerSec:k,samples:L,source:x?"local-measured":"no-samples-yet"},etaSeconds:V,etaSecondsDetail:{currentQueue:V,withGrowth:G,source:B},queueGrowthChunksPerSec:$.deltaPerSec})}),t.post("/api/semantic/install",Fe,async i=>{if(Ge())return i.json({ok:!0,status:"already_installed"});if(Da)return i.json({error:"a scan is already running"},409);Da=!0;try{return await sf(),await Be(),vn(),i.json({ok:!0,status:"installed"})}catch(l){let m=l instanceof Error?l.message:"unknown error";return i.json({ok:!1,error:m},500)}finally{Da=!1}}),t.get("/api/semantic/reindex-preview",Fe,async i=>{let l=(i.req.query("project")??"").trim();if(!l)return i.json({error:"project name required"},400);let m=_(),g=m.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 h=m.prepare(`SELECT
2042
2090
  COUNT(*) AS total,
2043
2091
  SUM(CASE WHEN s.message_count >= 3 THEN 1 ELSE 0 END) AS eligible,
2044
2092
  SUM(CASE WHEN s.message_count >= 3 AND EXISTS
2045
2093
  (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id) THEN 1 ELSE 0 END)
2046
2094
  AS indexed
2047
- FROM sessions s WHERE s.project_id = ?`).get(g.id),E=_.eligible??0,T=_.indexed??0,w=Math.max(0,E-T),x=m.prepare(`SELECT COUNT(*) AS n FROM chunk_queue
2048
- WHERE session_id NOT IN (SELECT id FROM sessions WHERE project_id = ?)`).get(g.id).n,A=ii(),N=2,C=30,I=N,O=C,H=N*C,J="fallback-baseline",Q=0;if(A)I=A.sessionsPerSec,O=A.avgChunksPerSession,H=A.chunksPerSec,J="local-measured",Q=A.samples;else if(et()){if(!pe().loaded)try{await Ge()}catch{}try{let z=["[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"],te=Date.now();await Et(z);let Z=Date.now()-te;Z>0&&(H=z.length*1e3/Z,I=H/C,J="live-benchmark")}catch{}}let Y=z=>m.prepare(`SELECT
2095
+ FROM sessions s WHERE s.project_id = ?`).get(g.id),E=h.eligible??0,y=h.indexed??0,R=Math.max(0,E-y),O=m.prepare(`SELECT COUNT(*) AS n FROM chunk_queue
2096
+ WHERE session_id NOT IN (SELECT id FROM sessions WHERE project_id = ?)`).get(g.id).n,N=_i(),C=2,x=30,k=C,L=x,$=C*x,q="fallback-baseline",V=0;if(N)k=N.sessionsPerSec,L=N.avgChunksPerSession,$=N.chunksPerSec,q="local-measured",V=N.samples;else if(Ge()){if(!le().loaded)try{await Be()}catch{}try{let z=["[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"],te=Date.now();await Ve(z);let Q=Date.now()-te;Q>0&&($=z.length*1e3/Q,k=$/x,q="live-benchmark")}catch{}}let G=z=>m.prepare(`SELECT
2049
2097
  COALESCE(SUM(CASE WHEN ec > 5 THEN 5 ELSE ec END), 0) AS total_quick,
2050
2098
  COALESCE(SUM(CASE WHEN ec > 80 THEN 80 ELSE ec END), 0) AS total_standard,
2051
2099
  COALESCE(SUM(CASE WHEN ec > 200 THEN 200 ELSE ec END), 0) AS total_full,
@@ -2058,9 +2106,9 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2058
2106
  END AS ec
2059
2107
  FROM sessions s
2060
2108
  WHERE s.project_id = ? AND s.message_count >= 3 ${z}
2061
- )`).get(g.id),B=Y("AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)"),X=Y("");return i.json({project:g.name,total:_.total??0,eligible:E,indexed:T,pendingNew:w,pendingForce:E,modelInstalled:et(),modelName:"BAAI/bge-base-en-v1.5",embedderLoaded:pe().loaded,workerRunning:Me().running,queueDepthOther:x,sessionsPerSec:I,avgChunksPerSession:O,chunksPerSec:H,throughputSource:J,throughputSamples:Q,estimatedChunksByDepth:{new:{quick:B.total_quick,standard:B.total_standard,full:B.total_full,uncapped:B.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",Fe,async i=>{let l={};try{l=await i.req.json()}catch{}let m=(l.project??"").trim(),g=h(),_=0,E=null;if(m){let T=g.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(m,m);if(!T)return i.json({error:`project not found: ${m}`},404);let w=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(T.id).n;g.prepare("DELETE FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").run(T.id),_=w,E=T.id}else{let T=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n;g.prepare("DELETE FROM chunk_queue").run(),_=T}return E!==null?Pu(E):Fu(),Me().queueDepth===0&&Uu(),i.json({cleared:_,project:m||null,queueDepth:Me().queueDepth})}),t.post("/api/semantic/reindex-project",Fe,async i=>{if(!et())return i.json({error:"embedder not installed \u2014 run `recall semantic install` first"},503);let l={};try{l=await i.req.json()}catch{}let m=(l.project??"").trim();if(!m)return i.json({error:"project name required"},400);let g=!!l.force,_=Math.max(0,Math.floor(Number(l.maxChunks??0)));ju(_);let E=h(),T=E.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(m,m);if(!T)return i.json({error:`project not found: ${m}`},404);let w=g?"SELECT id FROM sessions WHERE project_id = ? AND message_count >= 3":`SELECT s.id FROM sessions s
2109
+ )`).get(g.id),B=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:h.total??0,eligible:E,indexed:y,pendingNew:R,pendingForce:E,modelInstalled:Ge(),modelName:"BAAI/bge-base-en-v1.5",embedderLoaded:le().loaded,workerRunning:Me().running,queueDepthOther:O,sessionsPerSec:k,avgChunksPerSession:L,chunksPerSec:$,throughputSource:q,throughputSamples:V,estimatedChunksByDepth:{new:{quick:B.total_quick,standard:B.total_standard,full:B.total_full,uncapped:B.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",Fe,async i=>{let l={};try{l=await i.req.json()}catch{}let m=(l.project??"").trim(),g=_(),h=0,E=null;if(m){let y=g.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(m,m);if(!y)return i.json({error:`project not found: ${m}`},404);let R=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(y.id).n;g.prepare("DELETE FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").run(y.id),h=R,E=y.id}else{let y=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n;g.prepare("DELETE FROM chunk_queue").run(),h=y}return E!==null?dd(E):pd(),Me().queueDepth===0&&gd(),i.json({cleared:h,project:m||null,queueDepth:Me().queueDepth})}),t.post("/api/semantic/reindex-project",Fe,async i=>{if(!Ge())return i.json({error:"embedder not installed \u2014 run `recall semantic install` first"},503);let l={};try{l=await i.req.json()}catch{}let m=(l.project??"").trim();if(!m)return i.json({error:"project name required"},400);let g=!!l.force,h=Math.max(0,Math.floor(Number(l.maxChunks??0)));ld(h);let E=_(),y=E.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(m,m);if(!y)return i.json({error:`project not found: ${m}`},404);let R=g?"SELECT id FROM sessions WHERE project_id = ? AND message_count >= 3":`SELECT s.id FROM sessions s
2062
2110
  WHERE s.project_id = ? AND s.message_count >= 3
2063
- AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)`,x=E.prepare(w).all(T.id);if(x.length===0)return i.json({enqueued:0,queueDepth:Me().queueDepth,message:"nothing to do \u2014 every session in this project is already vectorized (use force:true to re-embed)"});let A=E.prepare("INSERT INTO chunk_queue(session_id, action) VALUES (?, 'embed')");if(E.transaction(()=>{for(let C of x)A.run(C.id)})(),!pe().loaded)try{await Ge()}catch(C){let I=C instanceof Error?C.message:"unknown error";return i.json({error:`embedder load failed: ${I}`},500)}return Me().running||Nn(),i.json({enqueued:x.length,queueDepth:Me().queueDepth,project:m,appliedMaxChunks:_})}),t.get("/api/sessions/:id/similar",Fe,async i=>{if(!pe().loaded)return i.json({error:"vector model not loaded"},503);let l=i.req.param("id"),m=Math.max(1,Math.min(50,Number(i.req.query("limit")??10)));try{let g=await bg(l,m);return i.json({sessionId:l,similar:g})}catch(g){let _=g instanceof Error?g.message:"unknown error";return i.json({error:_},500)}}),t.get("/api/search",Fe,async i=>{let l=h(),m=i.req.query("q")?.trim();if(!m)return i.json({query:"",hits:[],tags:[]});if(m.length>500)return i.json({error:"query too long (max 500 chars)"},400);let g=i.req.query("project"),_=m.split(/\s+/).filter(K=>K.length>0),E=_.filter(K=>K.startsWith("#")).map(K=>it(K)).filter(Boolean),T=_.filter(K=>!K.startsWith("#")),w=T.length>20,A=(w?T.slice(0,20):T).map(K=>`"${K.replace(/"/g,"")}"`),N=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=Mr("s",I);if(A.length===0&&E.length>0){let K=`
2111
+ AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)`,O=E.prepare(R).all(y.id);if(O.length===0)return i.json({enqueued:0,queueDepth:Me().queueDepth,message:"nothing to do \u2014 every session in this project is already vectorized (use force:true to re-embed)"});let N=E.prepare("INSERT INTO chunk_queue(session_id, action) VALUES (?, 'embed')");if(E.transaction(()=>{for(let x of O)N.run(x.id)})(),!le().loaded)try{await Be()}catch(x){let k=x instanceof Error?x.message:"unknown error";return i.json({error:`embedder load failed: ${k}`},500)}return Me().running||vn(),i.json({enqueued:O.length,queueDepth:Me().queueDepth,project:m,appliedMaxChunks:h})}),t.get("/api/sessions/:id/similar",Fe,async i=>{if(!le().loaded)return i.json({error:"vector model not loaded"},503);let l=i.req.param("id"),m=Math.max(1,Math.min(50,Number(i.req.query("limit")??10)));try{let g=await Kg(l,m);return i.json({sessionId:l,similar:g})}catch(g){let h=g instanceof Error?g.message:"unknown error";return i.json({error:h},500)}}),t.get("/api/search",Fe,async i=>{let l=_(),m=i.req.query("q")?.trim();if(!m)return i.json({query:"",hits:[],tags:[]});if(m.length>500)return i.json({error:"query too long (max 500 chars)"},400);let g=i.req.query("project"),h=m.split(/\s+/).filter(Y=>Y.length>0),E=h.filter(Y=>Y.startsWith("#")).map(Y=>ut(Y)).filter(Boolean),y=h.filter(Y=>!Y.startsWith("#")),R=y.length>20,N=(R?y.slice(0,20):y).map(Y=>`"${Y.replace(/"/g,"")}"`),C=N.join(" "),x=Math.max(1,Math.min(200,Number(i.req.query("limit")??30))),k=i.req.query("system")==="1"||i.req.query("system")==="true",L=Hr("s",k);if(N.length===0&&E.length>0){let Y=`
2064
2112
  SELECT s.id AS session_id,
2065
2113
  s.id AS message_uuid,
2066
2114
  p.name AS project,
@@ -2072,8 +2120,8 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2072
2120
  FROM sessions s
2073
2121
  JOIN projects p ON p.id = s.project_id
2074
2122
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2075
- WHERE 1=1${O}
2076
- `,ie={limit:C};g&&(K+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Gn(g)}%`),E.forEach(($e,ge)=>{K+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ge})`,ie[`tag_${ge}`]=$e}),K+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let nt=l.prepare(K).all(ie);return i.json({query:m,hits:nt,tags:E,truncated:w})}let H=`
2123
+ WHERE 1=1${L}
2124
+ `,ie={limit:x};g&&(Y+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Vn(g)}%`),E.forEach(($e,ge)=>{Y+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ge})`,ie[`tag_${ge}`]=$e}),Y+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let ot=l.prepare(Y).all(ie);return i.json({query:m,hits:ot,tags:E,truncated:R})}let $=`
2077
2125
  SELECT m.session_id AS session_id,
2078
2126
  m.uuid AS message_uuid,
2079
2127
  p.name AS project,
@@ -2087,8 +2135,8 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2087
2135
  JOIN sessions s ON s.id = m.session_id
2088
2136
  JOIN projects p ON p.id = s.project_id
2089
2137
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2090
- WHERE messages_fts MATCH @fts${O}
2091
- `,J={fts:N,limit:C};g&&(H+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",J.proj=`%${Gn(g)}%`),E.forEach((K,ie)=>{H+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ie})`,J[`tag_${ie}`]=K}),H+=" ORDER BY bm25(messages_fts) LIMIT @limit";let Y=l.prepare(H).all(J).map(K=>({...K,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:m,hits:Y,tags:E,truncated:w});let X=[];try{let K=`
2138
+ WHERE messages_fts MATCH @fts${L}
2139
+ `,q={fts:C,limit:x};g&&($+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",q.proj=`%${Vn(g)}%`),E.forEach((Y,ie)=>{$+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ie})`,q[`tag_${ie}`]=Y}),$+=" ORDER BY bm25(messages_fts) LIMIT @limit";let G=l.prepare($).all(q).map(Y=>({...Y,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:m,hits:G,tags:E,truncated:R});let X=[];try{let Y=`
2092
2140
  SELECT s.id AS session_id,
2093
2141
  s.id AS message_uuid,
2094
2142
  p.name AS project,
@@ -2103,30 +2151,31 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
2103
2151
  JOIN sessions s ON s.id = ss.session_id
2104
2152
  JOIN projects p ON p.id = s.project_id
2105
2153
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
2106
- WHERE sessions_fts MATCH @fts${O}
2107
- `,ie={fts:N,limit:C};g&&(K+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Gn(g)}%`),E.forEach((nt,$e)=>{K+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${$e})`,ie[`tag_${$e}`]=nt}),K+=" ORDER BY rank LIMIT @limit",X=l.prepare(K).all(ie)}catch(K){console.error("[search.semantic] failed:",K)}if(pe().loaded)try{let K=await Eg(m,C),ie=Y.map(ue=>({id:String(ue.session_id),data:ue,lane:"bm25"})),nt=X.map(ue=>({id:String(ue.session_id),data:ue,lane:"summary"})),$e=K.map(ue=>({id:ue.sessionId,data:{session_id:ue.sessionId,snippet:ue.text,matched_via:"vector"},lane:"vector"})),zg=Sg([ie,nt,$e]).slice(0,C).map(ue=>({...ue.data,session_id:ue.id,rrf_score:ue.score,lanes:ue.lanes,matched_via:ue.lanes.length>1?"fused":ue.lanes[0]}));return i.json({query:m,hits:zg,tags:E,mode:"semantic",fusion:"rrf",truncated:w})}catch(K){console.error("[search.vector] failed, falling back:",K)}let z=new Set(Y.map(K=>String(K.session_id))),te=X.filter(K=>!z.has(String(K.session_id))).map(({rank:K,...ie})=>({...ie,matched_via:"semantic"})),Z=[...Y,...te].slice(0,C);return i.json({query:m,hits:Z,tags:E,mode:"semantic",truncated:w})}),t.get("/api/sessions/:id/context",Fe,i=>{let l=h(),m=i.req.param("id"),g=i.req.query("mode")==="full"?"full":"condensed",_=i.req.query("subagents")==="1",E=i.req.query("prelude")??null,T=l.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
2154
+ WHERE sessions_fts MATCH @fts${L}
2155
+ `,ie={fts:C,limit:x};g&&(Y+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Vn(g)}%`),E.forEach((ot,$e)=>{Y+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${$e})`,ie[`tag_${$e}`]=ot}),Y+=" ORDER BY rank LIMIT @limit",X=l.prepare(Y).all(ie)}catch(Y){console.error("[search.semantic] failed:",Y)}if(le().loaded)try{let Y=await zg(m,x),ie=G.map(de=>({id:String(de.session_id),data:de,lane:"bm25"})),ot=X.map(de=>({id:String(de.session_id),data:de,lane:"summary"})),$e=Y.map(de=>({id:de.sessionId,data:{session_id:de.sessionId,snippet:de.text,matched_via:"vector"},lane:"vector"})),Of=Vg([ie,ot,$e]).slice(0,x).map(de=>({...de.data,session_id:de.id,rrf_score:de.score,lanes:de.lanes,matched_via:de.lanes.length>1?"fused":de.lanes[0]}));return i.json({query:m,hits:Of,tags:E,mode:"semantic",fusion:"rrf",truncated:R})}catch(Y){console.error("[search.vector] failed, falling back:",Y)}let z=new Set(G.map(Y=>String(Y.session_id))),te=X.filter(Y=>!z.has(String(Y.session_id))).map(({rank:Y,...ie})=>({...ie,matched_via:"semantic"})),Q=[...G,...te].slice(0,x);return i.json({query:m,hits:Q,tags:E,mode:"semantic",truncated:R})}),t.get("/api/sessions/:id/context",Fe,i=>{let l=_(),m=i.req.param("id"),g=i.req.query("mode")==="full"?"full":"condensed",h=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,
2108
2156
  s.started_at, s.ended_at, s.message_count, s.git_branch
2109
2157
  FROM sessions s JOIN projects p ON p.id = s.project_id
2110
- WHERE s.id = ?`).get(m);if(!T)return i.json({error:"not found"},404);let w=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2158
+ WHERE s.id = ?`).get(m);if(!y)return i.json({error:"not found"},404);let R=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2111
2159
  FROM messages
2112
2160
  WHERE session_id = ?
2113
- ORDER BY COALESCE(timestamp, ''), rowid`).all(m),x=xd(T,w,{mode:g,includeSidechain:_,prelude:E});return i.text(x)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:iu(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),m=Ye(l);if(!m)return i.json({error:"not found"},404);let g=au(l,!0);return i.json({collection:m,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 m=yn({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(m,201)}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/collections/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);try{let g=lu(l,m);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 m=uu(l);return i.json(m)}catch(m){return i.json({error:m.message},404)}}),t.post("/api/collections/:id/restore",i=>{let l=i.req.param("id");try{let m=du(l);return i.json(m)}catch(m){return i.json({error:m.message},404)}}),t.post("/api/collections/:id/members",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.session_id!="string")return i.json({error:"session_id required"},400);try{let g=Tn(l,m.session_id,m.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"),m=i.req.param("sid");try{let g=pu(l,m);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:cu(l)})});let k=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:hu()})),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||!k.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let m=Zo({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(m,201)}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/auto-collections/rules/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);try{let g=Eu(l,m);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 m=bu(l);return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.get("/api/auto-collections/suggestions",i=>{let l=i.req.query("dismissed")==="1";return i.json({suggestions:qs({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let m=yu(l);return i.json({rule:m})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/auto-collections/suggestions/:id/dismiss",i=>{let l=i.req.param("id");try{return Su(l),i.json({ok:!0})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=Xs();return i.json({suggestions:l})}),t.get("/api/auto-collections/suggestions/:id/preview",i=>{let l=i.req.param("id"),m=Math.max(1,Math.min(20,Number(i.req.query("limit"))||3)),_=qs({includeDismissed:!1}).find(T=>T.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=fu(_.type,_.pattern,m);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(Tu());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Oi({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),m=ce(l);if(!m)return i.json({error:"thread not found"},404);let g=m.edges.map(_=>({..._,alias_source:_.alias==null?null:j.isSessionAutoLinked(_.session_id)?"auto":"manual"}));return i.json({thread:{...m,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 m=rr({name:l.name,summary:l.summary??null,originSessionId:l.originSessionId});return i.json({thread:m})}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/threads/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));try{m.name&&qd(l,m.name),m.close&&Xd(l),m.reopen&&Jd(l),m.archive&&Gd(l),"folder_id"in m&&mp(l,m.folder_id??null);let g=ce(l);return g?i.json({thread:g}):i.json({error:"thread not found"},404)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/thread-folders",i=>i.json({folders:Ii()})),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 m=ap({name:l.name,parentFolderId:l.parent_folder_id??null,projectScope:l.project_scope??null});return i.json({folder:m})}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/thread-folders/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));try{let g;return m.name&&(g=lp(l,m.name)),"parent_folder_id"in m&&(g=up(l,m.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 pp(l),i.json({ok:!0})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/thread-folders/reorder",async i=>{let l=await i.req.json().catch(()=>({})),m=l.ordered_ids;if(!Array.isArray(m))return i.json({error:"ordered_ids must be an array"},400);try{return dp(l.parent_folder_id??null,l.project_scope??null,m),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"),m=await i.req.json().catch(()=>({}));if(!m.sessionId)return i.json({error:"sessionId required"},400);try{let g=or({threadId:l,sessionId:m.sessionId,parentSessionId:m.parentSessionId??null,role:m.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"),m=i.req.param("sessionId"),g=Wd(l,m);return i.json(g)}),t.patch("/api/threads/:id/sessions/:sessionId",async i=>{let l=i.req.param("id"),m=i.req.param("sessionId"),g=await i.req.json().catch(()=>({}));try{let _=Ln(l,m,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"),m=await i.req.json().catch(()=>({}));if(!m.sourceId)return i.json({error:"sourceId required"},400);try{let g=Yd(m.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"),m=await i.req.json().catch(()=>({}));if(!m.sessionIds?.length||!m.newThreadName)return i.json({error:"sessionIds and newThreadName required"},400);try{let g=Kd({threadId:l,sessionIds:m.sessionIds,newThreadName:m.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:Bd(l)})});let M=$.object({enabled:$.boolean(),band_lo:$.number().min(0).max(1).optional(),band_hi:$.number().min(0).max(1).optional(),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),P=$.object({project:$.string().min(1),threshold:$.number().min(0).max(1).optional(),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:M});t.post("/api/threads/scan/preflight",async i=>{let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=P.safeParse(m);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Mm({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 G=$.object({project:$.string().min(1),threshold:$.number().min(0).max(1).optional(),llm_names:$.boolean().optional(),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:M});t.post("/api/threads/scan/apply",async i=>{let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=G.safeParse(m);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Dm({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(!na(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return tt(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of Pm(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=na(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=De(i);return l||(Fm(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let D=$.object({project_id:$.number().int().positive(),mode:$.enum(["preflight","apply"]),window_hours:$.number().min(.5).max(168).optional(),score_threshold:$.number().min(0).max(1).optional(),use_live_pids:$.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),m=D.safeParse(l);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);try{let g=await rp(m.data.project_id,{windowHours:m.data.window_hours,scoreThreshold:m.data.score_threshold,useLivePids:m.data.use_live_pids});if(m.data.mode==="preflight")return i.json({plan:g});let _=op(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"),m=ce(l);if(!m)return i.json({error:"thread not found"},404);let g=h(),_=0;for(let E of m.edges)g.prepare("SELECT auto_title_source FROM sessions WHERE id = ?").get(E.session_id)?.auto_title_source==="agent"&&(_+=1);return i.json({total:m.edges.length,alreadyTitled:_,untitled:m.edges.length-_})}),t.post("/api/threads/:id/titles/generate",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({})),g=ce(l);if(!g)return i.json({error:"thread not found"},404);if(g.edges.length===0)return i.json({error:"thread has no sessions"},400);let _=Be(),E=m.model??_.model,T=Ap({threadId:l,force:m.force??!1,model:E});return i.json({jobId:T})}),t.get("/api/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Mi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return tt(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of xp(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/jobs/:jobId",i=>{let l=Mi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>Np(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 m=j.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(m==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance",count:j.size()});let g=j.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:m,count:j.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 m=j.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(m==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance"});let g=j.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let _=Mg(l.shell_pid,l.tab_name);return i.json({ok:!0,ownership:m,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 m=j.remove(l.shell_pid);return i.json({ok:!0,removed:m,count:j.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):(j.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:j.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 m=l.text.length>8192?l.text.slice(-8192):l.text,g=typeof l.captured_at=="string"?l.captured_at:new Date().toISOString();return j.setOutputTail(l.shell_pid,m,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 m=new Map;for(let O of j.all())m.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=[],T=g.filter(O=>{let H=j.claimPidOwnership(O.shell_pid,_);return E.push({shell_pid:O.shell_pid,ownership:H}),H!=="rejected"}),w=j.sync(T),x=0;for(let O of T){let H=m.get(O.shell_pid),J=j.get(O.shell_pid)?.tab_name??O.tab_name,Y=!!J&&!de(J)&&!le(J)?J:O.tab_name;H!==void 0&&H!==Y&&(x+=Mg(O.shell_pid,Y))}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 N=await Mk(),C={resolved:0,expired:0};try{C=vl()}catch{}let I={rebound:0,ghosts:0,ambiguous:0};try{I=Ll()}catch{}return Jk(),i.json({ok:!0,count:j.size(),diff:w,propagated:x,live_sweep:N,deferred_resolved:C,rebound:I})}),t.get("/api/terminal/registry",i=>i.json({terminals:j.all(),count:j.size()})),t.get("/api/terminal/sessions/:shellPid",i=>{let l=i.req.param("shellPid"),m=Number(l);if(!Number.isInteger(m)||m<=0)return i.json({error:"shellPid must be a positive integer"},400);let g=j.sessionsFor(m);return i.json({shell_pid:m,sessions:g})}),t.get("/api/sessions/:id/linked-terminal",i=>{let l=i.req.param("id"),m=j.all().find(_=>j.sessionsFor(_.shell_pid).includes(l)),g=[];if(!m){let _=h().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l);if(_?.cwd&&_.started_at){let E=Date.parse(_.started_at);if(Number.isFinite(E)){let T=_.cwd.replace(/\/+$/,""),w=300*1e3;for(let x of j.all()){if(!x.cwd||x.cwd.replace(/\/+$/,"")!==T||de(x.tab_name))continue;let A=Date.parse(x.opened_at),N=Date.parse(x.last_seen_at);!Number.isFinite(A)||!Number.isFinite(N)||A>E||N+w<E||g.push({shell_pid:x.shell_pid,tab_name:x.tab_name,cwd:x.cwd,opened_at:x.opened_at,last_seen_at:x.last_seen_at,reason:"time-overlap"})}g.sort((x,A)=>Date.parse(A.last_seen_at)-Date.parse(x.last_seen_at))}}}return m?i.json({linked:{shell_pid:m.shell_pid,tab_name:m.tab_name,cwd:m.cwd},suggested:[]}):i.json({linked:null,suggested:g})}),t.post("/api/sessions/:id/auto-relink",async i=>{let l=i.req.param("id");if(ye(l))return i.json({applied:!1,reason:"has-alias"});if(j.all().some(T=>j.sessionsFor(T.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=h().prepare("SELECT cwd, git_branch, started_at FROM sessions WHERE id = ?").get(l);if(!g?.cwd)return i.json({applied:!1,reason:"no-cwd"});let _=g.cwd.replace(/\/+$/,""),E=j.all().filter(T=>T.cwd&&T.cwd.replace(/\/+$/,"")===_&&!de(T.tab_name));if(E.length===1){let T=E[0],w=Cn({sessionStartedAt:g.started_at??null,terminalOpenedAt:T.opened_at??null});if(!w.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:w.reason});let x=j.getOrigin(l),A=Lt({tabName:T.tab_name,origin:x??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return A?(he(l,A),j.linkSession(l,T.shell_pid),i.json({applied:!0,alias:A,linked_pid:T.shell_pid,linked_tab_name:T.tab_name,method:"cwd-singleton"})):i.json({applied:!1,reason:"no-usable-name"})}if(E.length>1){let T=await Cl(l);if(T){let x=j.get(T.shell_pid),A=Cn({sessionStartedAt:g.started_at??null,terminalOpenedAt:x?.opened_at??null});if(!A.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:A.reason});let N=j.getOrigin(l),C=Lt({tabName:T.tab_name,origin:N??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(C)return he(l,C),j.linkSession(l,T.shell_pid),i.json({applied:!0,alias:C,linked_pid:T.shell_pid,linked_tab_name:T.tab_name,matched_fingerprints:T.matched_fingerprints,method:"content-match"})}let w=6e4;if(g.started_at){let x=Date.parse(g.started_at);if(Number.isFinite(x)){let A=E.filter(C=>Cn({sessionStartedAt:g.started_at,terminalOpenedAt:C.opened_at??null}).allowed).map(C=>({t:C,gap:x-Date.parse(C.opened_at??"")})).filter(C=>Number.isFinite(C.gap)&&C.gap>=0&&C.gap<=w);if(A.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:A.length});let N=A[0];if(N){let C=j.getOrigin(l),I=Lt({tabName:N.t.tab_name,origin:C??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(I)return he(l,I),j.linkSession(l,N.t.shell_pid),i.json({applied:!0,alias:I,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"),m=await i.req.json().catch(()=>null);if(m?.clear)return j.unlinkSession(l),ks(l),i.json({ok:!0,alias:null,linked_pid:null});if(!m||typeof m.shell_pid!="number")return i.json({error:"shell_pid required"},400);let g=j.get(m.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let _=j.getOrigin(l),E=h().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),T=null,w=g.tab_name?.trim()??"";if(w&&!de(w)&&!le(w))T=w;else if(w&&le(w)){let x=Ct(w);x&&!de(x)&&(T=x)}return T?(j.unlinkSession(l),he(l,T),i.json({ok:!0,alias:T,linked_pid:m.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"),m=h().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!m?.file_path)return i.json({error:"session not found"},404);j.unlinkSession(l),ks(l),await Ns(m.file_path);let g=ye(l);return i.json({ok:!0,alias:g,linked_pid:j.all().find(_=>j.sessionsFor(_.shell_pid).includes(l))?.shell_pid??null})}),t.get("/api/paste-expand",async i=>{let l=i.req.query("session"),m=i.req.query("message"),g=i.req.query("path");if(!l||!m||!g)return i.json({error:"session, message and path are required"},400);let _=h(),E=_.prepare("SELECT rowid, content_text FROM messages WHERE uuid = ? AND session_id = ?").get(m,l);if(!E)return i.json({error:"message not found in session"},404);let T=g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(!new RegExp(`\\[Pasted text #\\d+ \\+\\d+ lines\\]\\s*${T}`).test(E.content_text??""))return i.json({error:"path not referenced by this message"},403);let x=_.prepare(`SELECT content_text FROM messages
2161
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(m),O=rp(y,R,{mode:g,includeSidechain:h,prelude:E});return i.text(O)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:Iu(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),m=Qe(l);if(!m)return i.json({error:"not found"},404);let g=Mu(l,!0);return i.json({collection:m,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 m=kn({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(m,201)}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/collections/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);try{let g=ju(l,m);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 m=Pu(l);return i.json(m)}catch(m){return i.json({error:m.message},404)}}),t.post("/api/collections/:id/restore",i=>{let l=i.req.param("id");try{let m=Fu(l);return i.json(m)}catch(m){return i.json({error:m.message},404)}}),t.post("/api/collections/:id/members",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.session_id!="string")return i.json({error:"session_id required"},400);try{let g=An(l,m.session_id,m.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"),m=i.req.param("sid");try{let g=$u(l,m);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:Du(l)})});let A=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:qu()})),t.post("/api/auto-collections/rules",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string"||typeof l.pattern!="string"||!l.type||!A.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let m=li({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(m,201)}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/auto-collections/rules/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);try{let g=Xu(l,m);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 m=Ju(l);return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.get("/api/auto-collections/suggestions",i=>{let l=i.req.query("dismissed")==="1";return i.json({suggestions:Ks({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let m=Yu(l);return i.json({rule:m})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/auto-collections/suggestions/:id/dismiss",i=>{let l=i.req.param("id");try{return Gu(l),i.json({ok:!0})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=Vs();return i.json({suggestions:l})}),t.get("/api/auto-collections/suggestions/:id/preview",i=>{let l=i.req.param("id"),m=Math.max(1,Math.min(20,Number(i.req.query("limit"))||3)),h=Ks({includeDismissed:!1}).find(y=>y.id===l);if(!h)return i.json({error:"suggestion not found"},404);let E=Bu(h.type,h.pattern,m);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(zu());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Bi({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),m=ce(l);if(!m)return i.json({error:"thread not found"},404);let g=m.edges.map(h=>({...h,alias_source:h.alias==null?null:D.isSessionAutoLinked(h.session_id)?"auto":"manual"}));return i.json({thread:{...m,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 m=dr({name:l.name,summary:l.summary??null,originSessionId:l.originSessionId});return i.json({thread:m})}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/threads/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));try{m.name&&Tp(l,m.name),m.close&&yp(l),m.reopen&&wp(l),m.archive&&Rp(l),"folder_id"in m&&qp(l,m.folder_id??null);let g=ce(l);return g?i.json({thread:g}):i.json({error:"thread not found"},404)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/thread-folders",i=>i.json({folders:Ji()})),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 m=Fp({name:l.name,parentFolderId:l.parent_folder_id??null,projectScope:l.project_scope??null});return i.json({folder:m})}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/thread-folders/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));try{let g;return m.name&&(g=Up(l,m.name)),"parent_folder_id"in m&&(g=Hp(l,m.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 Wp(l),i.json({ok:!0})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/thread-folders/reorder",async i=>{let l=await i.req.json().catch(()=>({})),m=l.ordered_ids;if(!Array.isArray(m))return i.json({error:"ordered_ids must be an array"},400);try{return Bp(l.parent_folder_id??null,l.project_scope??null,m),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"),m=await i.req.json().catch(()=>({}));if(!m.sessionId)return i.json({error:"sessionId required"},400);try{let g=pr({threadId:l,sessionId:m.sessionId,parentSessionId:m.parentSessionId??null,role:m.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"),m=i.req.param("sessionId"),g=Sp(l,m);return i.json(g)}),t.patch("/api/threads/:id/sessions/:sessionId",async i=>{let l=i.req.param("id"),m=i.req.param("sessionId"),g=await i.req.json().catch(()=>({}));try{let h=Dn(l,m,g.parentSessionId??null);return i.json({edge:h})}catch(h){return i.json({error:h.message},400)}}),t.post("/api/threads/:id/merge",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));if(!m.sourceId)return i.json({error:"sourceId required"},400);try{let g=kp(m.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"),m=await i.req.json().catch(()=>({}));if(!m.sessionIds?.length||!m.newThreadName)return i.json({error:"sessionIds and newThreadName required"},400);try{let g=Ap({threadId:l,sessionIds:m.sessionIds,newThreadName:m.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:bp(l)})});let I=P.object({enabled:P.boolean(),band_lo:P.number().min(0).max(1).optional(),band_hi:P.number().min(0).max(1).optional(),model:P.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),j=P.object({project:P.string().min(1),threshold:P.number().min(0).max(1).optional(),model:P.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:I});t.post("/api/threads/scan/preflight",async i=>{let l=je(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=j.safeParse(m);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let h=dg({project:g.data.project,threshold:g.data.threshold,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in h?i.json({error:h.error},400):i.json(h)});let J=P.object({project:P.string().min(1),threshold:P.number().min(0).max(1).optional(),llm_names:P.boolean().optional(),model:P.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:I});t.post("/api/threads/scan/apply",async i=>{let l=je(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=J.safeParse(m);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let h=mg({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 h?i.json({error:h.error},400):i.json({jobId:h.jobId,reused:h.reused},h.reused?409:200)}),t.get("/api/threads/scan/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!fa(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async h=>{let E=!1,y=setInterval(()=>{E||h.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of gg(l,g))if(E||(await h.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=fa(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/threads/scan/jobs/:jobId",i=>{let l=je(i);return l||(fg(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let M=P.object({project_id:P.number().int().positive(),mode:P.enum(["preflight","apply"]),window_hours:P.number().min(.5).max(168).optional(),score_threshold:P.number().min(0).max(1).optional(),use_live_pids:P.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),m=M.safeParse(l);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);try{let g=await Dp(m.data.project_id,{windowHours:m.data.window_hours,scoreThreshold:m.data.score_threshold,useLivePids:m.data.use_live_pids});if(m.data.mode==="preflight")return i.json({plan:g});let h=jp(g);return i.json({plan:g,result:h})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/threads/:id/titles/preflight",i=>{let l=i.req.param("id"),m=ce(l);if(!m)return i.json({error:"thread not found"},404);let g=_(),h=0;for(let E of m.edges)g.prepare("SELECT auto_title_source FROM sessions WHERE id = ?").get(E.session_id)?.auto_title_source==="agent"&&(h+=1);return i.json({total:m.edges.length,alreadyTitled:h,untitled:m.edges.length-h})}),t.post("/api/threads/:id/titles/generate",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({})),g=ce(l);if(!g)return i.json({error:"thread not found"},404);if(g.edges.length===0)return i.json({error:"thread has no sessions"},400);let h=qe(),E=m.model??h.model,y=sm({threadId:l,force:m.force??!1,model:E});return i.json({jobId:y})}),t.get("/api/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Gi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return rt(i,async h=>{let E=!1,y=setInterval(()=>{E||h.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of rm(l,g))if(E||(await h.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/jobs/:jobId",i=>{let l=Gi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>om(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 m=D.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(m==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance",count:D.size()});let g=D.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:m,count:D.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 m=D.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(m==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance"});let g=D.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let h=pf(l.shell_pid,l.tab_name);return i.json({ok:!0,ownership:m,entry:g,propagated:h})}),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 m=D.remove(l.shell_pid);return i.json({ok:!0,removed:m,count:D.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):(D.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:D.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 m=l.text.length>8192?l.text.slice(-8192):l.text,g=typeof l.captured_at=="string"?l.captured_at:new Date().toISOString();return D.setOutputTail(l.shell_pid,m,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 m=new Map;for(let L of D.all())m.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()})),h=l.extension_instance_id??null,E=[],y=g.filter(L=>{let $=D.claimPidOwnership(L.shell_pid,h);return E.push({shell_pid:L.shell_pid,ownership:$}),$!=="rejected"}),R=D.sync(y),O=0;for(let L of y){let $=m.get(L.shell_pid),q=D.get(L.shell_pid)?.tab_name??L.tab_name,G=!!q&&!pe(q)&&!ue(q)?q:L.tab_name;$!==void 0&&$!==G&&(O+=pf(L.shell_pid,G))}let N=E.filter(L=>L.ownership==="rejected").length;N>0&&console.log(`[terminal/sync] dropped ${N} tab_name update(s), pid(s) owned by a different extension instance`);let C=await SA(),x={resolved:0,expired:0};try{x=iu()}catch{}let k={rebound:0,ghosts:0,ambiguous:0};try{k=ou()}catch{}return vA(),i.json({ok:!0,count:D.size(),diff:R,propagated:O,live_sweep:C,deferred_resolved:x,rebound:k})}),t.get("/api/terminal/registry",i=>i.json({terminals:D.all(),count:D.size()})),t.get("/api/terminal/sessions/:shellPid",i=>{let l=i.req.param("shellPid"),m=Number(l);if(!Number.isInteger(m)||m<=0)return i.json({error:"shellPid must be a positive integer"},400);let g=D.sessionsFor(m);return i.json({shell_pid:m,sessions:g})}),t.get("/api/sessions/:id/linked-terminal",i=>{let l=i.req.param("id"),m=D.all().find(h=>D.sessionsFor(h.shell_pid).includes(l)),g=[];if(!m){let h=_().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l);if(h?.cwd&&h.started_at){let E=Date.parse(h.started_at);if(Number.isFinite(E)){let y=h.cwd.replace(/\/+$/,""),R=300*1e3;for(let O of D.all()){if(!O.cwd||O.cwd.replace(/\/+$/,"")!==y||pe(O.tab_name))continue;let N=Date.parse(O.opened_at),C=Date.parse(O.last_seen_at);!Number.isFinite(N)||!Number.isFinite(C)||N>E||C+R<E||g.push({shell_pid:O.shell_pid,tab_name:O.tab_name,cwd:O.cwd,opened_at:O.opened_at,last_seen_at:O.last_seen_at,reason:"time-overlap"})}g.sort((O,N)=>Date.parse(N.last_seen_at)-Date.parse(O.last_seen_at))}}}return m?i.json({linked:{shell_pid:m.shell_pid,tab_name:m.tab_name,cwd:m.cwd},suggested:[]}):i.json({linked:null,suggested:g})}),t.post("/api/sessions/:id/auto-relink",async i=>{let l=i.req.param("id");if(Se(l))return i.json({applied:!1,reason:"has-alias"});if(D.all().some(y=>D.sessionsFor(y.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=_().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 h=g.cwd.replace(/\/+$/,""),E=D.all().filter(y=>y.cwd&&y.cwd.replace(/\/+$/,"")===h&&!pe(y.tab_name));if(E.length===1){let y=E[0],R=Mn({sessionStartedAt:g.started_at??null,terminalOpenedAt:y.opened_at??null});if(!R.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:R.reason});let O=D.getOrigin(l),N=It({tabName:y.tab_name,origin:O??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return N?(_e(l,N),D.linkSession(l,y.shell_pid),i.json({applied:!0,alias:N,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,method:"cwd-singleton"})):i.json({applied:!1,reason:"no-usable-name"})}if(E.length>1){let y=await ru(l);if(y){let O=D.get(y.shell_pid),N=Mn({sessionStartedAt:g.started_at??null,terminalOpenedAt:O?.opened_at??null});if(!N.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:N.reason});let C=D.getOrigin(l),x=It({tabName:y.tab_name,origin:C??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(x)return _e(l,x),D.linkSession(l,y.shell_pid),i.json({applied:!0,alias:x,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,matched_fingerprints:y.matched_fingerprints,method:"content-match"})}let R=6e4;if(g.started_at){let O=Date.parse(g.started_at);if(Number.isFinite(O)){let N=E.filter(x=>Mn({sessionStartedAt:g.started_at,terminalOpenedAt:x.opened_at??null}).allowed).map(x=>({t:x,gap:O-Date.parse(x.opened_at??"")})).filter(x=>Number.isFinite(x.gap)&&x.gap>=0&&x.gap<=R);if(N.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:N.length});let C=N[0];if(C){let x=D.getOrigin(l),k=It({tabName:C.t.tab_name,origin:x??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(k)return _e(l,k),D.linkSession(l,C.t.shell_pid),i.json({applied:!0,alias:k,linked_pid:C.t.shell_pid,linked_tab_name:C.t.tab_name,method:"closest-before-temporal",gap_ms:C.gap})}}}return i.json({applied:!1,reason:"ambiguous",candidate_count:E.length})}return i.json({applied:!1,reason:"no-candidates"})}),t.post("/api/sessions/:id/relink",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(m?.clear)return D.unlinkSession(l),vs(l),i.json({ok:!0,alias:null,linked_pid:null});if(!m||typeof m.shell_pid!="number")return i.json({error:"shell_pid required"},400);let g=D.get(m.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let h=D.getOrigin(l),E=_().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),y=null,R=g.tab_name?.trim()??"";if(R&&!pe(R)&&!ue(R))y=R;else if(R&&ue(R)){let O=vt(R);O&&!pe(O)&&(y=O)}return y?(D.unlinkSession(l),_e(l,y),i.json({ok:!0,alias:y,linked_pid:m.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"),m=_().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!m?.file_path)return i.json({error:"session not found"},404);D.unlinkSession(l),vs(l),await Ds(m.file_path);let g=Se(l);return i.json({ok:!0,alias:g,linked_pid:D.all().find(h=>D.sessionsFor(h.shell_pid).includes(l))?.shell_pid??null})}),t.get("/api/paste-expand",async i=>{let l=i.req.query("session"),m=i.req.query("message"),g=i.req.query("path");if(!l||!m||!g)return i.json({error:"session, message and path are required"},400);let h=_(),E=h.prepare("SELECT rowid, content_text FROM messages WHERE uuid = ? AND session_id = ?").get(m,l);if(!E)return i.json({error:"message not found in session"},404);let y=g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(!new RegExp(`\\[Pasted text #\\d+ \\+\\d+ lines\\]\\s*${y}`).test(E.content_text??""))return i.json({error:"path not referenced by this message"},403);let O=h.prepare(`SELECT content_text FROM messages
2114
2162
  WHERE session_id = ? AND rowid > ?
2115
- ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let A of x){let N=A.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 A=await Ck(g),N=vk();if(!A.startsWith(N+"/")&&!A.startsWith(N+"\\"))return i.json({error:"path outside allowed root"},403);let C=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],O=A.slice(N.length+1).split("/")[0].split("\\")[0];if(C.includes(O))return i.json({error:"path inside sensitive directory"},403);let H=await Nk(A),J=2*1024*1024;if(H.size>J)return i.json({error:"file too large",size:H.size,max:J},413);let Q=await Ok(A,"utf8");return i.json({source:"disk",content:Q})}catch(A){return i.json({source:"missing",error:A.message})}}),t.get("/api/projects/:name/stats",i=>{let l=h(),m=i.req.param("name"),g=l.prepare(`SELECT
2163
+ ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let N of O){let C=N.content_text??"";if(C.includes("**Tool result**")&&C.includes(g))return i.json({source:"tool-result",content:C});if(/^\s*1\t/.test(C)&&C.length>200)return i.json({source:"tool-result",content:C})}try{let N=await _A(g),C=EA();if(!N.startsWith(C+"/")&&!N.startsWith(C+"\\"))return i.json({error:"path outside allowed root"},403);let x=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],L=N.slice(C.length+1).split("/")[0].split("\\")[0];if(x.includes(L))return i.json({error:"path inside sensitive directory"},403);let $=await gA(N),q=2*1024*1024;if($.size>q)return i.json({error:"file too large",size:$.size,max:q},413);let V=await fA(N,"utf8");return i.json({source:"disk",content:V})}catch(N){return i.json({source:"missing",error:N.message})}}),t.get("/api/projects/:name/stats",i=>{let l=_(),m=i.req.param("name"),g=l.prepare(`SELECT
2116
2164
  (SELECT COUNT(*) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=? AND s.message_count > 2) AS sessions,
2117
2165
  (SELECT COALESCE(SUM(s.message_count), 0) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=?) AS messages,
2118
2166
  (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,
2119
- (SELECT MAX(COALESCE(s.ended_at, s.started_at)) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE (s.ended_at IS NOT NULL OR s.started_at IS NOT NULL) AND p.name=?) AS latest`).get(m,m,m,m),_=l.prepare(`SELECT DISTINCT s.git_branch FROM sessions s
2167
+ (SELECT MAX(COALESCE(s.ended_at, s.started_at)) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE (s.ended_at IS NOT NULL OR s.started_at IS NOT NULL) AND p.name=?) AS latest`).get(m,m,m,m),h=l.prepare(`SELECT DISTINCT s.git_branch FROM sessions s
2120
2168
  JOIN projects p ON p.id = s.project_id
2121
2169
  WHERE p.name = ? AND s.git_branch IS NOT NULL
2122
2170
  ORDER BY s.git_branch
2123
- LIMIT 20`).all(m).map(E=>E.git_branch);return i.json({...g,branches:_})});function v(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function F(i){if(!e)return i;let l=`<meta name="recall-token" content="${v(e)}" />`,m=i.indexOf("</head>");return m!==-1?i.slice(0,m)+l+i.slice(m):l+i}function U(i){let l=Dg();return i.html(Ad({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:jg}))}function L(){try{return Ra(Fg,"utf8")}catch{return null}}return Hk?(t.use("/assets/*",Ig({root:Aa})),t.get("/favicon.svg",Ig({root:Aa})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=L();return l===null?U(i):i.html(F(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=L();return l===null?U(i):i.html(F(l))})):t.get("/",i=>U(i)),t}function Qk(){if(Ar(),!!dt().heuristicEnabled){try{let{updated:e}=Kl();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}=zl();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}=Vl();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}=Ql();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 Hg(e,t){let n=Vk(t);return new Promise((s,r)=>{try{let o=Ak({fetch:n.fetch,port:e,hostname:"127.0.0.1"},()=>{s(o),setImmediate(()=>{try{Qk()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}import{createServer as Wg}from"node:net";function Bg(e){return new Promise(t=>{let n=Wg();n.once("error",()=>t(!1)),n.once("listening",()=>{n.close(()=>t(!0))}),n.listen(e,"127.0.0.1")})}async function qg(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await Bg(t))return t;for(let n=0;n<50;n++){let s=49152+Math.floor(Math.random()*16383);if(!e.has(s)&&await Bg(s))return s}return new Promise((n,s)=>{let r=Wg();r.once("error",s),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>n(a))}else r.close(),s(new Error("could not determine a free port"))})})}function Xg(e){let t=Date.now(),n=e.prepare("SELECT id, decoded_path FROM projects WHERE repo_root IS NULL AND is_repo = 0").all();if(n.length===0)return{total:0,ephemeral:0,walked:0,folded:0,deferred:0,durationMs:Date.now()-t};let s=new Set,r=e.prepare("SELECT DISTINCT repo_root FROM projects WHERE repo_root IS NOT NULL AND repo_root != ?").all(jt);for(let y of r)s.add(y.repo_root);let o=[],a=e.prepare(`UPDATE projects
2171
+ LIMIT 20`).all(m).map(E=>E.git_branch);return i.json({...g,branches:h})});function F(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function ne(i){if(!e)return i;let l=`<meta name="recall-token" content="${F(e)}" />`,m=i.indexOf("</head>");return m!==-1?i.slice(0,m)+l+i.slice(m):l+i}function U(i){let l=gf();return i.html(sp({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:mf}))}function v(){try{return Pa(_f,"utf8")}catch{return null}}return xA?(t.use("/assets/*",df({root:$a})),t.get("/favicon.svg",df({root:$a})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=v();return l===null?U(i):i.html(ne(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=v();return l===null?U(i):i.html(ne(l))})):t.get("/",i=>U(i)),t}function FA(){if(Ir(),!!ft().heuristicEnabled){try{let{updated:e}=yu();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}=wu();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}=Ru();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}=ku();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 bf(e,t){let n=PA(t);return new Promise((s,r)=>{try{let o=pA({fetch:n.fetch,port:e,hostname:"127.0.0.1"},()=>{s(o),setImmediate(()=>{try{FA()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}import{createServer as Tf}from"node:net";function Sf(e){return new Promise(t=>{let n=Tf();n.once("error",()=>t(!1)),n.once("listening",()=>{n.close(()=>t(!0))}),n.listen(e,"127.0.0.1")})}async function yf(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await Sf(t))return t;for(let n=0;n<50;n++){let s=49152+Math.floor(Math.random()*16383);if(!e.has(s)&&await Sf(s))return s}return new Promise((n,s)=>{let r=Tf();r.once("error",s),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>n(a))}else r.close(),s(new Error("could not determine a free port"))})})}function wf(e){let t=Date.now(),n=e.prepare("SELECT id, decoded_path FROM projects WHERE repo_root IS NULL AND is_repo = 0").all();if(n.length===0)return{total:0,ephemeral:0,walked:0,folded:0,deferred:0,durationMs:Date.now()-t};let s=new Set,r=e.prepare("SELECT DISTINCT repo_root FROM projects WHERE repo_root IS NOT NULL AND repo_root != ?").all(Pt);for(let T of r)s.add(T.repo_root);let o=[],a=e.prepare(`UPDATE projects
2124
2172
  SET repo_root = ?, main_repo = NULL, is_repo = 0, is_worktree = 0
2125
2173
  WHERE id = ?`),c=e.prepare(`UPDATE projects
2126
2174
  SET repo_root = ?, main_repo = ?, is_repo = 1, is_worktree = ?
2127
- WHERE id = ?`),u=e.prepare("UPDATE projects SET repo_root = ?, is_repo = 1 WHERE id = ?"),d=0,p=0,f=0;e.transaction(()=>{for(let y of n)Rn(y.decoded_path)&&(a.run(jt,y.id),d++);for(let y of n){if(Rn(y.decoded_path))continue;let b=Gs(y.decoded_path);b.isRepo?(c.run(b.root,b.mainRepo,b.isWorktree?1:0,y.id),s.add(b.root),p++):o.push(y)}for(let y of o){let b=ku(y.decoded_path,s);b&&(u.run(b,y.id),f++)}})();let S=o.length-f;return{total:n.length,ephemeral:d,walked:p,folded:f,deferred:S,durationMs:Date.now()-t}}yo();q();ne();bt();function Oa(){let e=[];return tn("stale-claude-json-mcp-paths",()=>{for(let t of Zk())e.push(t)}),tn("zombie-mcp",()=>{for(let t of tA())e.push(t)}),tn("chunk-queue-growth",()=>{for(let t of nA())e.push(t)}),tn("watcher-reflag-loop",()=>{for(let t of sA())e.push(t)}),tn("disk-pressure-backups",()=>{for(let t of rA())e.push(t)}),tn("daemon-state-files",()=>{for(let t of iA())e.push(t)}),e}function tn(e,t){try{t()}catch(n){let s=n instanceof Error?n.message:String(n);process.stderr.write(`[doctor-tick] ${e} check failed: ${s}
2128
- `)}}function Zk(){let e=kd();if(!e.configExists)return[];let t=[];for(let n of e.findings)t.push(eA(n));return t}function eA(e){return{check:"stale_claude_json_mcp_path",severity:e.severity==="HIGH"?"critical":"high",keyFacts:{name:e.name,stalePath:e.stalePath},message:`~/.claude.json mcpServers.${e.name} points at a deleted path: ${e.stalePath}`,remediation:e.remediation}}function tA(){let e=pi();return!e.flagged||!e.message?[]:[{check:"zombie_mcp_threshold",severity:"critical",keyFacts:{countBucket:Math.floor(e.count/5)*5,aggregateGbBucket:Math.floor(e.aggregateRssKb/(1024*1024))},message:e.message,remediation:"`recall mcp-prune --all` reaps. If that returns no children on a stale build, `pkill -f mcp-server.js` is the nuclear option."}]}function nA(){let e=bi();return e.status==="ok"?[]:[{check:"chunk_queue_growth",severity:{critical:"critical",high:"high",medium:"medium"}[e.status],keyFacts:{sizeBucket:Math.floor(e.currentSize/1e3)*1e3,semanticEnabled:e.semanticEnabled},message:e.message,remediation:e.remediation??"Re-run `recall doctor` for context."}]}function sA(){let e=Ks(20),t=[];for(let n of e){if(n.count<=Td)continue;let s=n.path.replace(/'/g,"''");t.push({check:"watcher_reflag_loop",severity:"critical",keyFacts:{path:n.path},message:`Watcher reindexed ${n.path} ${n.count.toLocaleString()} times in the last hour \u2014 reflag loop.`,remediation:`Mark it skipped with a SQL one-liner:
2129
- sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${s}';"`})}return t}function rA(){let e=wd();if(e.severity==="ok"||e.severity==="low")return[];let t=e.severity==="high"?"critical":"medium",n=e.severity==="high"?`Disk pressure HIGH \u2014 ${ft(e.freeBytes)} free of ${ft(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${ft(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`:`Disk pressure MEDIUM \u2014 ${ft(e.freeBytes)} free of ${ft(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${ft(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`;return[{check:"disk_pressure_backups",severity:t,keyFacts:{freePercentBucket:Math.floor(e.freePercent/5)*5,backupGbBucket:Math.floor(e.backupTotalBytes/(1024*1024*1024)),severity:e.severity},message:n,remediation:oA(e.oldFiles)}]}function oA(e){return e.length===0?"No snapshots older than 30 days \u2014 recent ones may still be rollback-critical, leave them alone unless you know what you are doing.":`Review snapshots older than 30 days then delete: ${e.slice(0,3).map(n=>`${n.name} (${ft(n.sizeBytes)}, ${n.ageDays}d old)`).join("; ")}. See memory \`partial_corpus_swap_bug_20260519\` \u2014 keep most recent .pre-swap for rollback.`}function iA(){let e=Rd();return!e.flagged||!e.message?[]:[{check:"daemon_state_files_missing",severity:"critical",keyFacts:{missing:[...e.missing].sort().join(",")},message:e.message,remediation:e.remediation??"recall stop && recall start"}]}function ft(e){return!Number.isFinite(e)||e<=0?"0 B":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}var Jg=300*1e3;function aA(){let e=process.env.RECALL_DOCTOR_TICK_MS;if(!e)return Jg;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<100?Jg:t}function Gg(e=Oa,t=new Date){let n=[];try{n=e()}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] check pipeline failed: ${a}
2130
- `),n=[]}let s=yi(),r=Ed(s,n,t);Ti(r);try{gd().catch(o=>{let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] auto-prune failed: ${a}
2175
+ WHERE id = ?`),u=e.prepare("UPDATE projects SET repo_root = ?, is_repo = 1 WHERE id = ?"),d=0,p=0,f=0;e.transaction(()=>{for(let T of n)Nn(T.decoded_path)&&(a.run(Pt,T.id),d++);for(let T of n){if(Nn(T.decoded_path))continue;let S=Zs(T.decoded_path);S.isRepo?(c.run(S.root,S.mainRepo,S.isWorktree?1:0,T.id),s.add(S.root),p++):o.push(T)}for(let T of o){let S=Qu(T.decoded_path,s);S&&(u.run(S,T.id),f++)}})();let b=o.length-f;return{total:n.length,ephemeral:d,walked:p,folded:f,deferred:b,durationMs:Date.now()-t}}Oo();H();Z();We();function Ba(){let e=[];return rn("stale-claude-json-mcp-paths",()=>{for(let t of $A())e.push(t)}),rn("zombie-mcp",()=>{for(let t of HA())e.push(t)}),rn("chunk-queue-growth",()=>{for(let t of BA())e.push(t)}),rn("watcher-reflag-loop",()=>{for(let t of WA())e.push(t)}),rn("disk-pressure-backups",()=>{for(let t of qA())e.push(t)}),rn("daemon-state-files",()=>{for(let t of JA())e.push(t)}),e}function rn(e,t){try{t()}catch(n){let s=n instanceof Error?n.message:String(n);process.stderr.write(`[doctor-tick] ${e} check failed: ${s}
2176
+ `)}}function $A(){let e=np();if(!e.configExists)return[];let t=[];for(let n of e.findings)t.push(UA(n));return t}function UA(e){return{check:"stale_claude_json_mcp_path",severity:e.severity==="HIGH"?"critical":"high",keyFacts:{name:e.name,stalePath:e.stalePath},message:`~/.claude.json mcpServers.${e.name} points at a deleted path: ${e.stalePath}`,remediation:e.remediation}}function HA(){let e=ki();return!e.flagged||!e.message?[]:[{check:"zombie_mcp_threshold",severity:"critical",keyFacts:{countBucket:Math.floor(e.orphanCount/5)*5,aggregateGbBucket:Math.floor(e.orphanRssKb/(1024*1024))},message:e.message,remediation:"`recall mcp-prune --all` reaps. If that returns no children on a stale build, `pkill -f mcp-server.js` is the nuclear option."}]}function BA(){let e=vi();return e.status==="ok"?[]:[{check:"chunk_queue_growth",severity:{critical:"critical",high:"high",medium:"medium"}[e.status],keyFacts:{sizeBucket:Math.floor(e.currentSize/1e3)*1e3,semanticEnabled:e.semanticEnabled},message:e.message,remediation:e.remediation??"Re-run `recall doctor` for context."}]}function WA(){let e=tr(20),t=[];for(let n of e){if(n.noProgressCount<=Zd)continue;let s=n.path.replace(/'/g,"''");t.push({check:"watcher_reflag_loop",severity:"critical",keyFacts:{path:n.path},message:`Watcher reindexed ${n.path} ${n.count.toLocaleString()} times in the last hour (${n.noProgressCount.toLocaleString()} with no new content) \u2014 reflag loop.`,remediation:`Mark it skipped with a SQL one-liner:
2177
+ sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${s}';"`})}return t}function qA(){let e=ep();if(e.severity==="ok"||e.severity==="low")return[];let t=e.severity==="high"?"critical":"medium",n=e.severity==="high"?`Disk pressure HIGH \u2014 ${bt(e.freeBytes)} free of ${bt(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${bt(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`:`Disk pressure MEDIUM \u2014 ${bt(e.freeBytes)} free of ${bt(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${bt(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`;return[{check:"disk_pressure_backups",severity:t,keyFacts:{freePercentBucket:Math.floor(e.freePercent/5)*5,backupGbBucket:Math.floor(e.backupTotalBytes/(1024*1024*1024)),severity:e.severity},message:n,remediation:XA(e.oldFiles)}]}function XA(e){return e.length===0?"No snapshots older than 30 days \u2014 recent ones may still be rollback-critical, leave them alone unless you know what you are doing.":`Review snapshots older than 30 days then delete: ${e.slice(0,3).map(n=>`${n.name} (${bt(n.sizeBytes)}, ${n.ageDays}d old)`).join("; ")}. See memory \`partial_corpus_swap_bug_20260519\` \u2014 keep most recent .pre-swap for rollback.`}function JA(){let e=tp();return!e.flagged||!e.message?[]:[{check:"daemon_state_files_missing",severity:"critical",keyFacts:{missing:[...e.missing].sort().join(",")},message:e.message,remediation:e.remediation??"recall stop && recall start"}]}function bt(e){return!Number.isFinite(e)||e<=0?"0 B":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}import{createInterface as m$}from"node:readline/promises";H();We();Wa();H();Wa();H();We();Z();var YF=768*4;We();function Rf(){let e=_();if(!e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get())return{outcome:"absent",message:"no vec_chunks_v1_backup present"};let n=e.prepare("SELECT completed_at FROM migration_state WHERE status = 'completed' ORDER BY id DESC LIMIT 1").get();return n?e.prepare("SELECT 1 AS x FROM migration_state WHERE status = 'completed' AND completed_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT 1").get()?{outcome:"kept",message:`backup retained \u2014 last migration (${n.completed_at}) is within the 30-day rollback window`}:(e.exec("DROP TABLE vec_chunks_v1_backup;"),{outcome:"pruned",message:`auto-pruned vec_chunks_v1_backup \u2014 last migration (${n.completed_at}) is older than 30 days`}):{outcome:"kept",message:"backup present but no completed migration on record \u2014 left for manual inspection"}}var kf=300*1e3;function GA(){let e=process.env.RECALL_DOCTOR_TICK_MS;if(!e)return kf;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<100?kf:t}function Af(e=Ba,t=new Date){let n=[];try{n=e()}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] check pipeline failed: ${a}
2178
+ `),n=[]}let s=Mi(),r=zd(s,n,t);Di(r);try{Xd().catch(o=>{let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] auto-prune failed: ${a}
2131
2179
  `)})}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] auto-prune failed (sync): ${a}
2132
- `)}return r}function Yg(e=Oa){let t=aA(),n=setInterval(()=>{Gg(e)},t);return typeof n.unref=="function"&&n.unref(),{stop:()=>clearInterval(n),runOnce:()=>{Gg(e)}}}var lA=Math.max(1,cA().length),Kg=String(Math.max(2,Math.floor(lA/2)));process.env.OMP_NUM_THREADS||(process.env.OMP_NUM_THREADS=Kg);process.env.ORT_NUM_THREADS||(process.env.ORT_NUM_THREADS=Kg);var uA=360*60*1e3,dA=60*1e3,pA=1440*60*1e3,mA=300*1e3,gA=300*1e3,fA=10*1e3,_A=1440*60*1e3,hA=30*1e3,EA=500,bA=1500,SA=6e4;async function yA(){let e=await qg(),t=Zu();(!t||t.length<32)&&(console.error("[daemon] FATAL: daemon token mint returned empty or undersized \u2014 refusing to start"),process.exit(1));try{zm().repointed&&console.log("[daemon] mcp self-heal: ~/.claude.json repointed (was stale)")}catch(L){console.error("[daemon] mcp self-heal failed (non-fatal, daemon continues):",L)}let n=await Hg(e,t),s={pid:process.pid,port:e,startedAt:new Date().toISOString()};ed({...s,token:t}),uo(ae().enabled,"boot");try{hn();let L=rl();L>0&&console.log(`[daemon] archive: migrated ${L} hot row(s) into archive.sqlite`)}catch(L){let i=L instanceof Error?L.message:String(L);console.error(`[daemon] archive migration failed: ${i}`)}let r=rd({db:h(),walPath:`${st}-wal`}),o=Ju(),a=setInterval(()=>{Gu()},SA),c=()=>{try{Xs()}catch(L){console.error("[daemon] suggestion scan failed:",L)}},u=setTimeout(c,dA),d=setInterval(c,uA),p=()=>{try{let L=j.reapStaleLinks();(L.pruned_pids||L.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${L.pruned_pids} pid${L.pruned_pids===1?"":"s"}, ${L.pruned_sessions} session link${L.pruned_sessions===1?"":"s"}`)}catch(L){console.error("[daemon] stale-link reaper failed:",L)}},f=setTimeout(p,mA),S=setInterval(p,pA),y=()=>{try{let L=j.gcDeadPids();(L.pruned_pids||L.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${L.pruned_pids} pid${L.pruned_pids===1?"":"s"}, ${L.pruned_sessions} session link${L.pruned_sessions===1?"":"s"}`)}catch(L){console.error("[daemon] dead-pid gc failed:",L)}},b=setTimeout(y,fA),R=setInterval(y,gA),k=()=>{Ha().then(L=>{L.ran&&L.revoked&&console.log(`[daemon] license check: REVOKED${L.reason?` (${L.reason})`:""}`)}).catch(L=>{console.error("[daemon] license check failed:",L)})},M=setTimeout(k,hA),P=setInterval(k,_A),G=hl();(async()=>{try{if(!et())return;let L=h().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get();if(L.n===0)return;if(!ae().autoResumeWorker){console.log(`[daemon] vector-worker dormant: ${L.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 l=await rt();if(l.tier!=="pro"){console.log(`[daemon] vector-worker auto-resume skipped: ${L.n} chunk(s) pending but license tier is ${l.tier}`);return}pe().loaded||await Ge(),Me().running||(Nn(),console.log(`[daemon] vector-worker auto-resumed: ${L.n} chunk(s) pending`))}catch(L){let i=L instanceof Error?L.message:String(L);console.error(`[daemon] vector-worker auto-resume failed: ${i}`)}})();let D=Yg(),v=setInterval(()=>{try{let{restored:L}=nd({...s,token:t});L.length>0&&console.log(`[daemon] state-files heal: restored ${L.join(",")}`)}catch(L){let i=L instanceof Error?L.message:String(L);console.error(`[daemon] state-files heal failed: ${i}`)}},3e4);typeof v.unref=="function"&&v.unref();let F=tr();console.log(F==="enabled"?"[auto-prune] mode=enabled \u2014 orphans >10min and runaway-CPU MCPs will be killed":F==="off"?"[auto-prune] mode=off \u2014 auto-prune fully disabled":"[auto-prune] mode=dry-run (set RECALL_AUTO_PRUNE=enabled to enforce)");let U=L=>{console.log(`[daemon] received ${L}, shutting down`),clearTimeout(u),clearInterval(d),clearTimeout(f),clearInterval(S),clearTimeout(b),clearInterval(R),clearTimeout(M),clearInterval(P),clearInterval(a),clearInterval(v),G.stop(),r.stop(),D.stop(),o.close(),n.close(),fi(),process.exit(0)};process.on("SIGTERM",()=>U("SIGTERM")),process.on("SIGINT",()=>U("SIGINT")),process.on("SIGHUP",()=>U("SIGHUP"));try{let L=Xg(h());L.total>0&&console.log(`[daemon] projects backfill: ephemeral=${L.ephemeral} walked=${L.walked} folded=${L.folded} deferred=${L.deferred} (total=${L.total}, ${L.durationMs}ms)`)}catch(L){console.error("[daemon] projects backfill failed:",L)}console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{Os().then(L=>{console.log(`[daemon] boot sweep: scanned ${L.scanned} live claude(s), linked ${L.linked}, renamed ${L.renamed}, ambiguous_cwd ${L.ambiguous_cwd}`)}).catch(L=>{console.error("[daemon] boot sweep failed:",L)})},EA),setTimeout(()=>{ui().then(L=>{console.log(`[daemon] ingestion sweep: scanned=${L.scanned} reindexed=${L.reindexed} up-to-date=${L.upToDate} skipped=${L.skipped} errors=${L.errors} (${L.durationMs}ms)`)}).catch(L=>{console.error("[daemon] ingestion sweep failed:",L)})},bA)}yA().catch(e=>{console.error("[daemon] fatal:",e),fi(),process.exit(1)});
2180
+ `)}try{let o=Rf();o.outcome==="pruned"&&console.log(`[doctor-tick] ${o.message}`)}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] backup auto-prune failed: ${a}
2181
+ `)}return r}function xf(e=Ba){let t=GA(),n=setInterval(()=>{Af(e)},t);return typeof n.unref=="function"&&n.unref(),{stop:()=>clearInterval(n),runOnce:()=>{Af(e)}}}var zA=Math.max(1,YA().length),Nf=String(Math.max(2,Math.floor(zA/2)));process.env.OMP_NUM_THREADS||(process.env.OMP_NUM_THREADS=Nf);process.env.ORT_NUM_THREADS||(process.env.ORT_NUM_THREADS=Nf);var KA=360*60*1e3,VA=60*1e3,QA=1440*60*1e3,ZA=300*1e3,ex=300*1e3,tx=10*1e3,nx=1440*60*1e3,sx=30*1e3,rx=500,ox=1500,ix=6e4;async function ax(){let e=await yf(),t=Ld();(!t||t.length<32)&&(console.error("[daemon] FATAL: daemon token mint returned empty or undersized \u2014 refusing to start"),process.exit(1));try{xg().repointed&&console.log("[daemon] mcp self-heal: ~/.claude.json repointed (was stale)")}catch(v){console.error("[daemon] mcp self-heal failed (non-fatal, daemon continues):",v)}let n=await bf(e,t),s={pid:process.pid,port:e,startedAt:new Date().toISOString()};Cd({...s,token:t}),bo(ae().enabled,"boot");try{Sn();let v=Rl();v>0&&console.log(`[daemon] archive: migrated ${v} hot row(s) into archive.sqlite`)}catch(v){let i=v instanceof Error?v.message:String(v);console.error(`[daemon] archive migration failed: ${i}`)}let r=Dd({db:_(),walPath:`${ze}-wal`}),o=yd(),a=setInterval(()=>{wd()},ix),c=()=>{try{Vs()}catch(v){console.error("[daemon] suggestion scan failed:",v)}},u=setTimeout(c,VA),d=setInterval(c,KA),p=()=>{try{let v=D.reapStaleLinks();(v.pruned_pids||v.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${v.pruned_pids} pid${v.pruned_pids===1?"":"s"}, ${v.pruned_sessions} session link${v.pruned_sessions===1?"":"s"}`)}catch(v){console.error("[daemon] stale-link reaper failed:",v)}},f=setTimeout(p,ZA),b=setInterval(p,QA),T=()=>{try{let v=D.gcDeadPids();(v.pruned_pids||v.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${v.pruned_pids} pid${v.pruned_pids===1?"":"s"}, ${v.pruned_sessions} session link${v.pruned_sessions===1?"":"s"}`)}catch(v){console.error("[daemon] dead-pid gc failed:",v)}},S=setTimeout(T,tx),w=setInterval(T,ex),A=()=>{tc().then(v=>{v.ran&&v.revoked&&console.log(`[daemon] license check: REVOKED${v.reason?` (${v.reason})`:""}`)}).catch(v=>{console.error("[daemon] license check failed:",v)})},I=setTimeout(A,sx),j=setInterval(A,nx),J=Hl();(async()=>{try{if(!Ge())return;let v=_().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get();if(v.n===0)return;if(!ae().autoResumeWorker){console.log(`[daemon] vector-worker dormant: ${v.n} chunk(s) pending in queue. Click Start in the web UI to drain, or set semantic.autoResumeWorker=true in ~/.recall/config.json to resume automatically on boot.`);return}let l=await it();if(l.tier!=="pro"){console.log(`[daemon] vector-worker auto-resume skipped: ${v.n} chunk(s) pending but license tier is ${l.tier}`);return}le().loaded||await Be(),Me().running||(vn(),console.log(`[daemon] vector-worker auto-resumed: ${v.n} chunk(s) pending`))}catch(v){let i=v instanceof Error?v.message:String(v);console.error(`[daemon] vector-worker auto-resume failed: ${i}`)}})();let M=xf(),F=setInterval(()=>{try{let{restored:v}=Id({...s,token:t});v.length>0&&console.log(`[daemon] state-files heal: restored ${v.join(",")}`)}catch(v){let i=v instanceof Error?v.message:String(v);console.error(`[daemon] state-files heal failed: ${i}`)}},3e4);typeof F.unref=="function"&&F.unref();let ne=cr();console.log(ne==="enabled"?"[auto-prune] mode=enabled \u2014 orphans >10min and runaway-CPU MCPs will be killed":ne==="off"?"[auto-prune] mode=off \u2014 auto-prune fully disabled":"[auto-prune] mode=dry-run (set RECALL_AUTO_PRUNE=enabled to enforce)");let U=v=>{console.log(`[daemon] received ${v}, shutting down`),clearTimeout(u),clearInterval(d),clearTimeout(f),clearInterval(b),clearTimeout(S),clearInterval(w),clearTimeout(I),clearInterval(j),clearInterval(a),clearInterval(F),J.stop(),r.stop(),M.stop(),o.close(),n.close(),Ni(),process.exit(0)};process.on("SIGTERM",()=>U("SIGTERM")),process.on("SIGINT",()=>U("SIGINT")),process.on("SIGHUP",()=>U("SIGHUP"));try{let v=wf(_());v.total>0&&console.log(`[daemon] projects backfill: ephemeral=${v.ephemeral} walked=${v.walked} folded=${v.folded} deferred=${v.deferred} (total=${v.total}, ${v.durationMs}ms)`)}catch(v){console.error("[daemon] projects backfill failed:",v)}console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{js().then(v=>{console.log(`[daemon] boot sweep: scanned ${v.scanned} live claude(s), linked ${v.linked}, renamed ${v.renamed}, ambiguous_cwd ${v.ambiguous_cwd}`)}).catch(v=>{console.error("[daemon] boot sweep failed:",v)})},rx),setTimeout(()=>{wi().then(v=>{console.log(`[daemon] ingestion sweep: scanned=${v.scanned} reindexed=${v.reindexed} up-to-date=${v.upToDate} skipped=${v.skipped} errors=${v.errors} (${v.durationMs}ms)`)}).catch(v=>{console.error("[daemon] ingestion sweep failed:",v)})},ox)}ax().catch(e=>{console.error("[daemon] fatal:",e),Ni(),process.exit(1)});