@clauderecallhq/cli 0.66.0 → 0.68.2

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 Bu=Object.defineProperty;var be=(e,t)=>()=>(e&&(t=e(e=0)),t);var vo=(e,t)=>{for(var s in t)Bu(e,s,{get:t[s],enumerable:!0})};import{homedir as jo}from"node:os";import{join as Tn,basename as CT}from"node:path";import{existsSync as Hu,mkdirSync as Wu,chmodSync as qu,readdirSync as vT,statSync as jT}from"node:fs";function J(){Hu($)||Wu($,{recursive:!0,mode:448}),process.platform!=="win32"&&qu($,448)}var Mo,$,yn,Z=be(()=>{"use strict";Mo=Tn(jo(),".claude","projects"),$=process.env.RECALL_HOME?process.env.RECALL_HOME:Tn(jo(),".recall"),yn=Tn($,"db.sqlite")});import{createRequire as gp}from"node:module";var _p,fp,hp,kn,An,Yo,Ko=be(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...s){let n=s[0];return t==="warning"&&n instanceof Error&&n.name==="ExperimentalWarning"&&/SQLite/i.test(n.message)?!1:e(t,...s)}}_p=gp(import.meta.url),fp=["node","sqlite"].join(":"),hp=_p(fp),kn=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let s=t.length===0?this.inner.run():this.inner.run(...t);return{changes:s.changes,lastInsertRowid:s.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},An=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new hp.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new kn(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,s={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(s.simple){let n=this.inner.prepare(`PRAGMA ${t}`).get();return n&&typeof n=="object"?Object.values(n)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...n)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...n);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,s){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),s===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,s)}},Yo=An});function Vo(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),s=new Set(t.map(S=>S.name)),n=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"]];for(let[S,y]of n)s.has(S)||e.exec(`ALTER TABLE sessions ADD COLUMN ${S} ${y}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(S=>S.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[S,y]of a)o.has(S)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${S} ${y}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),d=new Set(c.map(S=>S.name)),u=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[S,y]of u)d.has(S)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${S} ${y}`);let g=e.prepare("PRAGMA table_info(threads)").all();new Set(g.map(S=>S.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let h=e.prepare("PRAGMA table_info(thread_folders)").all(),b=new Set(h.map(S=>S.name));b.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),b.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
3
+ var xp=Object.defineProperty;var ue=(e,t)=>()=>(e&&(t=e(e=0)),t);var ds=(e,t)=>{for(var s in t)xp(e,s,{get:t[s],enumerable:!0})};import{homedir as ni}from"node:os";import{join as Dn,basename as $y}from"node:path";import{existsSync as Np,mkdirSync as Op,chmodSync as Lp,readdirSync as Hy,statSync as Wy}from"node:fs";function J(){Np(U)||Op(U,{recursive:!0,mode:448}),process.platform!=="win32"&&Lp(U,448)}var Fn,U,Pn,Q=ue(()=>{"use strict";Fn=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Dn(ni(),".claude","projects"),U=process.env.RECALL_HOME?process.env.RECALL_HOME:Dn(ni(),".recall"),Pn=Dn(U,"db.sqlite")});import{createRequire as em}from"node:module";var tm,sm,nm,Bn,Hn,fi,hi=ue(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...s){let n=s[0];return t==="warning"&&n instanceof Error&&n.name==="ExperimentalWarning"&&/SQLite/i.test(n.message)?!1:e(t,...s)}}tm=em(import.meta.url),sm=["node","sqlite"].join(":"),nm=tm(sm),Bn=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let s=t.length===0?this.inner.run():this.inner.run(...t);return{changes:s.changes,lastInsertRowid:s.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},Hn=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new nm.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new Bn(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,s={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(s.simple){let n=this.inner.prepare(`PRAGMA ${t}`).get();return n&&typeof n=="object"?Object.values(n)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...n)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...n);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,s){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),s===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,s)}},fi=Hn});function bi(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),s=new Set(t.map(T=>T.name)),n=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"]];for(let[T,S]of n)s.has(T)||e.exec(`ALTER TABLE sessions ADD COLUMN ${T} ${S}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(T=>T.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[T,S]of a)o.has(T)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${T} ${S}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(T=>T.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[T,S]of d)u.has(T)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${T} ${S}`);let m=e.prepare("PRAGMA table_info(threads)").all();new Set(m.map(T=>T.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let h=e.prepare("PRAGMA table_info(thread_folders)").all(),b=new Set(h.map(T=>T.name));b.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),b.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
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 Bu=Object.defineProperty;var be=(e,t)=>()=>(e&&(t=e(e=0)),t);var vo=(e,t)=>{
12
12
  );
13
13
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_session ON message_embeddings(session_id);
14
14
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_generated ON message_embeddings(generated_at DESC);
15
- `)}var zo,Zo=be(()=>{"use strict";zo=`
15
+ `)}var Ei,Si=ue(()=>{"use strict";Ei=`
16
16
  PRAGMA journal_mode = WAL;
17
17
  PRAGMA synchronous = NORMAL;
18
18
  PRAGMA foreign_keys = ON;
@@ -251,20 +251,70 @@ CREATE TABLE IF NOT EXISTS chunk_queue (
251
251
  );
252
252
  CREATE INDEX IF NOT EXISTS idx_chunk_queue_session ON chunk_queue(session_id);
253
253
 
254
- CREATE TRIGGER IF NOT EXISTS messages_vec_ai AFTER INSERT ON messages
255
- WHEN new.is_sidechain = 0 AND new.content_text IS NOT NULL
254
+ -- v0.67.0 -- retention/archive (P6). Power-user durability: sessions older
255
+ -- than the user chosen window can be moved to messages_archive so the hot
256
+ -- DB stays small without dropping data. Sessions metadata stays hot for
257
+ -- list/search by title; only the message bodies move. The "recall archive
258
+ -- restore <id>" command reverses the move. Source JSONLs are still the
259
+ -- immutable ground truth -- the archive table is a fast-path; in extremis
260
+ -- a session can be reindexed from its file_path on disk.
261
+ --
262
+ -- archive_status on the sessions row drives the UI: archived sessions show
263
+ -- a badge and a "restore to load full transcript" affordance.
264
+ CREATE TABLE IF NOT EXISTS messages_archive (
265
+ uuid TEXT PRIMARY KEY,
266
+ session_id TEXT NOT NULL,
267
+ parent_uuid TEXT,
268
+ type TEXT,
269
+ role TEXT,
270
+ timestamp TEXT,
271
+ is_sidechain INTEGER NOT NULL DEFAULT 0,
272
+ content_text TEXT,
273
+ tool_names TEXT,
274
+ raw_json TEXT,
275
+ archived_at TEXT NOT NULL DEFAULT (datetime('now'))
276
+ );
277
+ CREATE INDEX IF NOT EXISTS idx_messages_archive_session ON messages_archive(session_id);
278
+
279
+ -- v0.67.0 \u2014 semantic enqueue gate. Without this, every INSERT/DELETE/UPDATE
280
+ -- on messages enqueued a chunk_queue row, regardless of whether the embedder
281
+ -- worker was running. Power users with semantic disabled (the default)
282
+ -- accumulated 50M+ orphan rows in days, growing the DB by ~1 GB/day.
283
+ -- The gate is a single key in app_settings, kept in sync with
284
+ -- ~/.recall/config.json:semantic.enabled by the daemon at boot and on every
285
+ -- semantic-config write.
286
+ CREATE TABLE IF NOT EXISTS app_settings (
287
+ key TEXT PRIMARY KEY,
288
+ value TEXT NOT NULL
289
+ );
290
+ INSERT OR IGNORE INTO app_settings(key, value) VALUES ('semantic_enabled', '0');
291
+
292
+ -- Drop unconditional v0.10/v0.11-era triggers; replaced below with the gated
293
+ -- versions. Idempotent: SQLite no-ops when the trigger does not exist, so
294
+ -- this is safe to run on every boot and on fresh databases.
295
+ DROP TRIGGER IF EXISTS messages_vec_ai;
296
+ DROP TRIGGER IF EXISTS messages_vec_ad;
297
+ DROP TRIGGER IF EXISTS messages_vec_au;
298
+
299
+ CREATE TRIGGER messages_vec_ai AFTER INSERT ON messages
300
+ WHEN new.is_sidechain = 0
301
+ AND new.content_text IS NOT NULL
302
+ AND (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
256
303
  BEGIN
257
304
  INSERT INTO chunk_queue(session_id, message_uuid, action, enqueued_at)
258
305
  VALUES (new.session_id, new.uuid, 'embed', datetime('now'));
259
306
  END;
260
307
 
261
- CREATE TRIGGER IF NOT EXISTS messages_vec_ad AFTER DELETE ON messages BEGIN
308
+ CREATE TRIGGER messages_vec_ad AFTER DELETE ON messages
309
+ WHEN (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
310
+ BEGIN
262
311
  INSERT INTO chunk_queue(session_id, message_uuid, action, enqueued_at)
263
312
  VALUES (old.session_id, old.uuid, 'delete', datetime('now'));
264
313
  END;
265
314
 
266
- CREATE TRIGGER IF NOT EXISTS messages_vec_au AFTER UPDATE OF content_text ON messages
315
+ CREATE TRIGGER messages_vec_au AFTER UPDATE OF content_text ON messages
267
316
  WHEN new.is_sidechain = 0
317
+ AND (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
268
318
  BEGIN
269
319
  INSERT INTO chunk_queue(session_id, message_uuid, action, enqueued_at)
270
320
  VALUES (new.session_id, new.uuid, 'reembed', datetime('now'));
@@ -618,8 +668,9 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_target
618
668
  ON bug_synthesis_results(scope, target_id, created_at DESC);
619
669
  CREATE INDEX IF NOT EXISTS idx_synth_results_created
620
670
  ON bug_synthesis_results(created_at DESC);
621
- `});import*as Qo from"sqlite-vec";function f(){if(ie)return ie;J(),ie=new Yo(yn),Qo.load(ie),ie.pragma("cache_size = -64000"),ie.pragma("mmap_size = 268435456"),ie.pragma("temp_store = MEMORY"),ie.pragma("busy_timeout = 5000"),ie.pragma("journal_size_limit = 67108864"),ie.pragma("wal_autocheckpoint = 1000"),ie.exec(zo),Vo(ie);try{ie.exec("PRAGMA optimize")}catch{}return ie}var ie,H=be(()=>{"use strict";Ko();Z();Zo();ie=null});import{writeFileSync as Lp}from"node:fs";import{join as Cp}from"node:path";function De(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Je(e,t){let s=De(t);if(!s)throw new Error("tag must contain at least one alphanumeric character");let n=f(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?{tag:s,added:!1}:(n.transaction(()=>{n.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,s,r),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,s,r)})(),oi(),{tag:s,added:!0})}function ri(e,t){let s=De(t);if(!s)return{tag:"",removed:!1};let n=f(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?(n.transaction(()=>{n.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,s),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,s,r)})(),oi(),{tag:s,removed:!0}):{tag:s,removed:!1}}function wt(e){return f().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function Xe(){return f().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
622
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function oi(){try{J();let e=f(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),s=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),n={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:s};Lp(Ip,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var Ip,Ge=be(()=>{"use strict";H();Z();Ip=Cp($,"tags.json")});function vp(e,t){let s=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(s.length<=t)return s;let n=new Set;n.add(0),n.add(s.length-1);let r=(s.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)n.add(Math.floor(o*r));return Array.from(n).sort((o,a)=>o-a).slice(0,t).map(o=>s[o])}function Ye(e){let t=f(),s={limit:e.limit??500},n=e.sessionIds&&e.sessionIds.length>0,r=n?"1=1":"s.message_count > 2";if(n){let a=e.sessionIds.map((c,d)=>`@sid_${d}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,d)=>{s[`sid_${d}`]=c})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",s.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",s.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
671
+ `});import*as Ti from"sqlite-vec";function f(){if(de)return de;J(),de=new fi(Pn),Ti.load(de),de.pragma("cache_size = -64000"),de.pragma("mmap_size = 268435456"),de.pragma("temp_store = MEMORY"),de.pragma("busy_timeout = 5000"),de.pragma("journal_size_limit = 67108864"),de.pragma("wal_autocheckpoint = 1000"),de.exec(Ei),bi(de);try{de.exec("PRAGMA optimize")}catch{}return de}var de,H=ue(()=>{"use strict";hi();Q();Si();de=null});var Fi={};ds(Fi,{EmbedderUnavailableError:()=>Lt,embed:()=>Ct,embedQuery:()=>qn,getEmbedderStatus:()=>Ne,loadEmbedder:()=>gs,unloadEmbedder:()=>pm});import{join as Mi}from"node:path";function dm(){return Mi(U,"models","bge-base-en-v1.5")}async function gs(){if(ms&&Ot)return;let e;try{e=await import("@huggingface/transformers")}catch(n){throw new Lt(n)}let{pipeline:t,env:s}=e;s.localModelPath=Mi(U,"models"),s.allowRemoteModels=!1;try{Ot=await t("feature-extraction",Di,{local_files_only:!0,cache_dir:dm()}),ms=!0}catch(n){throw n instanceof Error&&/Cannot find module|onnxruntime_binding/.test(n.message)?new Lt(n):n}}function Ne(){return{loaded:ms,modelId:Di,dim:lm}}async function Ct(e){if(!Ot)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=[];for(let s=0;s<e.length;s+=ji){let n=e.slice(s,s+ji),o=(await Ot(n,{pooling:"cls",normalize:!0})).tolist();for(let a=0;a<n.length;a++){let c=o[a],u=Array.isArray(c[0])?c[0]:c;t.push(new Float32Array(u))}}return t}async function qn(e){let t=um+e,[s]=await Ct([t]);return s}function pm(){Ot=null,ms=!1}var Di,lm,ji,um,Ot,ms,Lt,ze=ue(()=>{"use strict";Q();Di="BAAI/bge-base-en-v1.5",lm=768,ji=16,um="Represent this sentence for searching relevant passages: ",Ot=null,ms=!1;Lt=class extends Error{cause;constructor(t){let s=t instanceof Error?t.message:String(t);super(["Semantic search is unavailable on this platform.","",`Reason: ${s}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) \u2014 or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
672
+ `)),this.name="EmbedderUnavailableError",this.cause=t}}});import{writeFileSync as mg}from"node:fs";import{join as gg}from"node:path";function Be(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Ve(e,t){let s=Be(t);if(!s)throw new Error("tag must contain at least one alphanumeric character");let n=f(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?{tag:s,added:!1}:(n.transaction(()=>{n.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,s,r),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,s,r)})(),ia(),{tag:s,added:!0})}function oa(e,t){let s=Be(t);if(!s)return{tag:"",removed:!1};let n=f(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?(n.transaction(()=>{n.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,s),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,s,r)})(),ia(),{tag:s,removed:!0}):{tag:s,removed:!1}}function Mt(e){return f().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function Ze(){return f().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
673
+ GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function ia(){try{J();let e=f(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),s=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),n={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:s};mg(_g,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var _g,Qe=ue(()=>{"use strict";H();Q();_g=gg(U,"tags.json")});function fg(e,t){let s=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(s.length<=t)return s;let n=new Set;n.add(0),n.add(s.length-1);let r=(s.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)n.add(Math.floor(o*r));return Array.from(n).sort((o,a)=>o-a).slice(0,t).map(o=>s[o])}function et(e){let t=f(),s={limit:e.limit??500},n=e.sessionIds&&e.sessionIds.length>0,r=n?"1=1":"s.message_count > 2";if(n){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{s[`sid_${u}`]=c})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",s.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",s.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
623
674
  NULLIF(sa.alias, '') AS alias,
624
675
  COALESCE(s.first_user_message, '') AS first_user_message
625
676
  FROM sessions s
@@ -629,21 +680,272 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
629
680
  ORDER BY COALESCE(s.started_at, '') DESC
630
681
  LIMIT @limit`).all(s).map(a=>{let c=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
631
682
  FROM messages WHERE session_id = ?
632
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),u=vp(c,5).map(g=>`${g.role}: ${g.content_text.slice(0,400)}`).join(`
683
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=fg(c,5).map(m=>`${m.role}: ${m.content_text.slice(0,400)}`).join(`
633
684
  ---
634
- `);return{id:a.id,project:a.project,git_branch:a.git_branch,alias:a.alias,first_user_message:a.first_user_message,message_sample:u,current_tags:wt(a.id)}})}var rs=be(()=>{"use strict";H();Ge()});import{z as ue}from"zod";function On(e){let t=e.minTags??2,s=e.maxTags??4,n=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(n&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${s} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
635
- `)}function Dp(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(`
636
- `)}function Pp(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(`
637
- `)}function $p(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(`
638
- `)}function ii(e){return Ln.find(t=>t.name===e)}var jp,Mp,Fp,Up,Bp,Hp,Wp,qp,Ln,Cn=be(()=>{"use strict";jp={project:ue.string().optional().describe("Exact project name match (optional)."),collectionId:ue.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:ue.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:ue.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:ue.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:ue.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:ue.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};Mp={sessionId:ue.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:ue.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};Fp={sessionId:ue.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};Up={sessionId:ue.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:ue.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};Bp={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:jp,build:On,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Hp={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:Mp,build:Dp,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Wp={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:Fp,build:Pp,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},qp={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:Up,build:$p,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},Ln=[Bp,Hp,Wp,qp]});function os(e,t){let s=Rt.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}function ai(e,t){let s=Rt.get(e);return s||(s=new Set,Rt.set(e,s)),s.add(t),()=>{let n=Rt.get(e);n&&(n.delete(t),n.size===0&&Rt.delete(e))}}var Rt,In=be(()=>{"use strict";Rt=new Map});var Pe={};vo(Pe,{buildScanPrompt:()=>ci,isClaudeCliAvailable:()=>oe,runClaudeCliScan:()=>vn,spawnClaudePrompt:()=>Fe});import{execFileSync as Jp,execSync as Xp,spawn as Gp}from"node:child_process";function Kp(){if(kt)return kt;try{kt=Xp("which claude",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{kt="claude"}return kt}function oe(){try{return Jp("command",["-v","claude"],{stdio:"ignore"}),!0}catch{return!1}}function ci(e){return On({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 zp(e,t){let s=t.get(e);return s||e.slice(0,8)}function Vp(e){try{return Ye(e).map(s=>({id:s.id,label:s.alias&&s.alias.trim().length>0?s.alias:s.first_user_message&&s.first_user_message.trim().length>0?s.first_user_message.slice(0,60):s.id.slice(0,8)}))}catch{return[]}}function Zp(e){let{scanId:t,total:s,labelTable:n}=e,r=new Set;return o=>{let a=o.trim();if(!a.startsWith("{"))return;let c;try{c=JSON.parse(a)}catch{return}if(!c||typeof c!="object")return;let d=c;if(!(d.type!=="assistant"||!d.message?.content))for(let u of d.message.content){if(u?.type!=="tool_use"||u.name!=="mcp__recall__apply_tags")continue;let g=u.input,h=typeof g?.sessionId=="string"?g.sessionId:null;!h||r.has(h)||(r.add(h),os(t,{type:"progress",current:r.size,total:s,sessionId:h,sessionLabel:zp(h,n)}))}}}function Qp(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
685
+ `);return{id:a.id,project:a.project,git_branch:a.git_branch,alias:a.alias,first_user_message:a.first_user_message,message_sample:d,current_tags:Mt(a.id)}})}var bs=ue(()=>{"use strict";H();Qe()});import{z as ge}from"zod";function er(e){let t=e.minTags??2,s=e.maxTags??4,n=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(n&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${s} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
686
+ `)}function bg(e){let t=e.mode==="detailed";return[`Summarize Claude Recall session ${e.sessionId} using the MCP tools available to you.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "condensed" to get the transcript as markdown.`,t?"2. Write a 1-paragraph overview (\u22643 sentences) of what this session was for, then 5-8 bullet points covering:":"2. Write 3-5 bullet points covering:"," - What was accomplished (shipped, decided, learned)"," - What was tried and abandoned"," - Any explicit open questions or follow-ups","","Rules:",'- Be concrete. Name files, functions, and decisions. Avoid vague "discussed X".',"- If nothing was actually shipped or decided, say so plainly.",'- Reply with just the summary \u2014 no preamble, no "Here is the summary:".'].join(`
687
+ `)}function Tg(e){return[`Extract every architectural or product decision made in Claude Recall session ${e.sessionId}.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "full" (so tool I/O is included \u2014 sometimes the decision lives in a commit message or diff).`,"2. For each distinct decision, emit one block:",""," - **Decision:** one sentence."," - **Why:** the stated rationale (quote if possible).",' - **Alternatives considered:** bullet list, or "none mentioned".',' - **Where it landed:** file path / function name / commit SHA if identifiable, otherwise "TBD".',"","Rules:","- Include only decisions that were actually made, not ideas merely discussed.","- If an alternative was rejected, list it with a one-line reason.","- Group related decisions under a short heading when useful.",'- If the session made zero real decisions, reply exactly with: "No decisions made in this session."',"- No preamble, no closing, just the decision blocks."].join(`
688
+ `)}function wg(e){let t=e.limit??5;return[`Find ${t} Recall sessions most similar to session ${e.sessionId}.`,"",`1. Call \`get_session\` with id "${e.sessionId}" \u2014 note its alias, first user message, tags, and git branch.`,'2. Derive 2-3 short search queries from that content (topic words, library names, error strings \u2014 NOT generic words like "fix" or "add").',`3. Call \`search\` once per query (limit: ${Math.max(5,t*2)}). Dedupe hits by session_id.`,"4. Also call `list_sessions` with the same tag(s) if the target session has any (pick the most specific tag).","5. Union the results. Exclude the target session itself. Rank by a mix of:"," - Shared tags (strongest signal)"," - Matching search hits across multiple queries"," - Recency as a tiebreaker","",`6. Return the top ${t} as a numbered list. For each:`," - **<short_id> \xB7 <project>** \u2014 <alias or first_user_message truncated>",' - One sentence on WHY it is similar (not a generic "same topic" \u2014 be specific).',"","If fewer than 2 genuinely-similar sessions exist, say so rather than padding with weak matches.","No preamble. Just the ranked list."].join(`
689
+ `)}function aa(e){return tr.find(t=>t.name===e)}var hg,Eg,Sg,yg,Rg,Ag,kg,xg,tr,sr=ue(()=>{"use strict";hg={project:ge.string().optional().describe("Exact project name match (optional)."),collectionId:ge.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:ge.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:ge.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:ge.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:ge.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:ge.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};Eg={sessionId:ge.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:ge.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};Sg={sessionId:ge.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};yg={sessionId:ge.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:ge.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};Rg={name:"auto_tag_sessions",title:"Auto-tag Recall sessions",description:"Have the agent auto-tag Recall sessions using the Recall MCP tools. Scope can be restricted to a project, collection, or single session.",argsSchema:hg,build:er,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Ag={name:"summarize_session",title:"Summarize a session",description:"Produce a concise, concrete summary of one session \u2014 what shipped, what was tried, what's still open.",argsSchema:Eg,build:bg,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},kg={name:"extract_decisions",title:"Extract architectural decisions",description:"Scan a session and emit one structured block per architectural / product decision: what, why, alternatives, where it landed.",argsSchema:Sg,build:Tg,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},xg={name:"find_similar_sessions",title:"Find similar sessions",description:"Given a session, find other sessions that touched the same topic / library / error \u2014 ranked with reasons.",argsSchema:yg,build:wg,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},tr=[Rg,Ag,kg,xg]});function Ss(e,t){let s=Dt.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}function ca(e,t){let s=Dt.get(e);return s||(s=new Set,Dt.set(e,s)),s.add(t),()=>{let n=Dt.get(e);n&&(n.delete(t),n.size===0&&Dt.delete(e))}}var Dt,nr=ue(()=>{"use strict";Dt=new Map});var We={};ds(We,{buildScanPrompt:()=>la,isClaudeCliAvailable:()=>le,runClaudeCliScan:()=>rr,spawnClaudePrompt:()=>He});import{execFileSync as Ng,execSync as Og,spawn as Lg}from"node:child_process";function vg(){if(Ft)return Ft;try{Ft=Og("which claude",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{Ft="claude"}return Ft}function le(){try{return Ng("command",["-v","claude"],{stdio:"ignore"}),!0}catch{return!1}}function la(e){return er({project:e.project,collectionId:e.collectionId,sessionId:e.sessionIds&&e.sessionIds.length===1?e.sessionIds[0]:void 0,untaggedOnly:e.untaggedOnly,limit:e.limit})}function Ig(e,t){let s=t.get(e);return s||e.slice(0,8)}function jg(e){try{return et(e).map(s=>({id:s.id,label:s.alias&&s.alias.trim().length>0?s.alias:s.first_user_message&&s.first_user_message.trim().length>0?s.first_user_message.slice(0,60):s.id.slice(0,8)}))}catch{return[]}}function Mg(e){let{scanId:t,total:s,labelTable:n}=e,r=new Set;return o=>{let a=o.trim();if(!a.startsWith("{"))return;let c;try{c=JSON.parse(a)}catch{return}if(!c||typeof c!="object")return;let u=c;if(!(u.type!=="assistant"||!u.message?.content))for(let d of u.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let m=d.input,h=typeof m?.sessionId=="string"?m.sessionId:null;!h||r.has(h)||(r.add(h),Ss(t,{type:"progress",current:r.size,total:s,sessionId:h,sessionLabel:Ig(h,n)}))}}}function Dg(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
639
690
  `);for(;n!==-1;){let r=t.slice(0,n);t=t.slice(n+1),r.length>0&&e(r),n=t.indexOf(`
640
- `)}}}async function vn(e,t={},s){let n=!!t.scanId,r=n?Vp(e):[],o=new Map(r.map(d=>[d.id,d.label])),a=r.length,c;return n&&t.scanId&&(c=Zp({scanId:t.scanId,total:a,labelTable:o})),li({prompt:ci(e),allowedTools:Yp.split(","),opts:t,onProgress:s,onStdoutLine:c,outputFormat:n?"stream-json":"json"})}async function Fe(e,t,s={},n){return li({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function li(e){let{prompt:t,allowedTools:s,opts:n,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",s.join(","),"--permission-mode","bypassPermissions"];return a==="stream-json"&&c.push("--verbose"),n.model&&c.push("--model",n.model),new Promise(d=>{let u=Gp(Kp(),c,{stdio:["ignore","pipe","pipe"]}),g=[],h=[],b=o?Qp(o):void 0;u.stdout.on("data",y=>{g.push(y),b&&b(y)}),u.stderr.on("data",y=>{if(h.push(y),r){let k=y.toString("utf8").trim();k&&r(k)}});let S=setTimeout(()=>{u.kill("SIGKILL")},1800*1e3);u.on("close",y=>{clearTimeout(S),d({success:y===0,stdout:Buffer.concat(g).toString("utf8"),stderr:Buffer.concat(h).toString("utf8"),exitCode:y})}),u.on("error",y=>{clearTimeout(S),d({success:!1,stdout:"",stderr:String(y),exitCode:null})})})}var Yp,kt,ge=be(()=>{"use strict";rs();Cn();In();Yp=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var Xc={};vo(Xc,{EmbedderUnavailableError:()=>Ut,embed:()=>$t,embedQuery:()=>Tr,getEmbedderStatus:()=>ke,loadEmbedder:()=>Ws,unloadEmbedder:()=>th});import{join as qc}from"node:path";function eh(){return qc($,"models","bge-base-en-v1.5")}async function Ws(){if(Hs&&Pt)return;let e;try{e=await import("@huggingface/transformers")}catch(n){throw new Ut(n)}let{pipeline:t,env:s}=e;s.localModelPath=qc($,"models"),s.allowRemoteModels=!1;try{Pt=await t("feature-extraction",Jc,{local_files_only:!0,cache_dir:eh()}),Hs=!0}catch(n){throw n instanceof Error&&/Cannot find module|onnxruntime_binding/.test(n.message)?new Ut(n):n}}function ke(){return{loaded:Hs,modelId:Jc,dim:Zf}}async function $t(e){if(!Pt)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=[];for(let s=0;s<e.length;s+=Wc){let n=e.slice(s,s+Wc),o=(await Pt(n,{pooling:"cls",normalize:!0})).tolist();for(let a=0;a<n.length;a++){let c=o[a],d=Array.isArray(c[0])?c[0]:c;t.push(new Float32Array(d))}}return t}async function Tr(e){let t=Qf+e,[s]=await $t([t]);return s}function th(){Pt=null,Hs=!1}var Jc,Zf,Wc,Qf,Pt,Hs,Ut,ot=be(()=>{"use strict";Z();Jc="BAAI/bge-base-en-v1.5",Zf=768,Wc=16,Qf="Represent this sentence for searching relevant passages: ",Pt=null,Hs=!1;Ut=class extends Error{cause;constructor(t){let s=t instanceof Error?t.message:String(t);super(["Semantic search is unavailable on this platform.","",`Reason: ${s}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) \u2014 or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
641
- `)),this.name="EmbedderUnavailableError",this.cause=t}}});import{Hono as Yb}from"hono";import{serve as Kb}from"@hono/node-server";Z();import{existsSync as Ju,readFileSync as Xu,writeFileSync as FT,unlinkSync as PT}from"node:fs";import{join as Gu}from"node:path";var Do=Gu($,"license.json");function St(){if(!Ju(Do))return null;try{let e=Xu(Do,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as Yu,importSPKI as Ku}from"jose";var Fo=`-----BEGIN PUBLIC KEY-----
691
+ `)}}}async function rr(e,t={},s){let n=!!t.scanId,r=n?jg(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return n&&t.scanId&&(c=Mg({scanId:t.scanId,total:a,labelTable:o})),ua({prompt:la(e),allowedTools:Cg.split(","),opts:t,onProgress:s,onStdoutLine:c,outputFormat:n?"stream-json":"json"})}async function He(e,t,s={},n){return ua({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function ua(e){let{prompt:t,allowedTools:s,opts:n,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",s.join(","),"--permission-mode","bypassPermissions"];return a==="stream-json"&&c.push("--verbose"),n.model&&c.push("--model",n.model),new Promise(u=>{let d=Lg(vg(),c,{stdio:["ignore","pipe","pipe"]}),m=[],h=[],b=o?Dg(o):void 0;d.stdout.on("data",S=>{m.push(S),b&&b(S)}),d.stderr.on("data",S=>{if(h.push(S),r){let w=S.toString("utf8").trim();w&&r(w)}});let T=setTimeout(()=>{d.kill("SIGKILL")},1800*1e3);d.on("close",S=>{clearTimeout(T),u({success:S===0,stdout:Buffer.concat(m).toString("utf8"),stderr:Buffer.concat(h).toString("utf8"),exitCode:S})}),d.on("error",S=>{clearTimeout(T),u({success:!1,stdout:"",stderr:String(S),exitCode:null})})})}var Cg,Ft,he=ue(()=>{"use strict";bs();sr();nr();Cg=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var xa={};ds(xa,{RetentionConfigSchema:()=>pr,readRetentionConfig:()=>mr,writeRetentionConfig:()=>Os});import{existsSync as ya,mkdirSync as w_,readFileSync as R_,writeFileSync as A_}from"node:fs";import{homedir as k_}from"node:os";import{join as wa}from"node:path";import{z as xs}from"zod";function Ra(){return process.env.RECALL_HOME??wa(k_(),".recall")}function x_(){let e=Ra();ya(e)||w_(e,{recursive:!0})}function Aa(){return wa(Ra(),"config.json")}function ka(){let e=Aa();if(!ya(e))return{};try{return JSON.parse(R_(e,"utf8"))}catch(t){let s=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${s}`),{}}}function mr(){let e=ka().retention;if(!e)return{...Ns};let t=pr.safeParse({...Ns,...e});return t.success?t.data:{...Ns}}function Os(e){x_();let t=ka(),s=pr.parse({...Ns,...t.retention??{},...e}),n={...t,retention:s};return A_(Aa(),JSON.stringify(n,null,2)),s}var pr,Ns,gr=ue(()=>{"use strict";pr=xs.object({autoArchiveEnabled:xs.boolean().default(!1),autoArchiveAfterDays:xs.number().int().min(7).max(3650).default(90),lastRunAt:xs.string().nullable().default(null)}),Ns={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Re from"chalk";import{formatDistanceToNowStrict as eR,parseISO as tR}from"date-fns";var Ee,Ls=ue(()=>{"use strict";Ee={dim:Re.gray,bold:Re.bold,project:Re.cyan,user:Re.blue,assistant:Re.green,tool:Re.magenta,warn:Re.yellow,err:Re.red,ok:Re.green,accent:Re.hex("#f97316")}});import{existsSync as N_}from"node:fs";import{join as O_}from"node:path";function Ut(){if(Na&&N_(Pt))return;J();let e=f(),t=Pt.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
692
+ CREATE TABLE IF NOT EXISTS archive.messages_archive (
693
+ uuid TEXT PRIMARY KEY,
694
+ session_id TEXT NOT NULL,
695
+ parent_uuid TEXT,
696
+ type TEXT,
697
+ role TEXT,
698
+ timestamp TEXT,
699
+ is_sidechain INTEGER NOT NULL DEFAULT 0,
700
+ content_text TEXT,
701
+ tool_names TEXT,
702
+ raw_json TEXT,
703
+ archived_at TEXT NOT NULL DEFAULT (datetime('now'))
704
+ );
705
+ CREATE INDEX IF NOT EXISTS archive.idx_messages_archive_session ON messages_archive(session_id);
706
+ `)}finally{e.exec("DETACH DATABASE archive")}Na=!0}function Oa(){let e=f();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;Ut();let s=Pt.replace(/'/g,"''"),n=0;e.exec(`ATTACH DATABASE '${s}' AS archive`);try{e.transaction(()=>{n=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
707
+ (uuid, session_id, parent_uuid, type, role, timestamp,
708
+ is_sidechain, content_text, tool_names, raw_json, archived_at)
709
+ SELECT uuid, session_id, parent_uuid, type, role, timestamp,
710
+ is_sidechain, content_text, tool_names, raw_json, archived_at
711
+ FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return n}var Pt,Na,_r=ue(()=>{"use strict";Q();H();Pt=O_(U,"archive.sqlite"),Na=!1});var Ca={};ds(Ca,{runArchive:()=>F_});function fr(e){Ut();let t=f(),s=Pt.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${s}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function L_(){return fr(e=>{let t=e.prepare(`SELECT
712
+ SUM(CASE WHEN archive_status = 'archived' THEN 1 ELSE 0 END) AS archived,
713
+ SUM(CASE WHEN archive_status != 'archived' THEN 1 ELSE 0 END) AS live
714
+ FROM sessions`).get(),s=e.prepare("SELECT COUNT(*) AS n FROM messages").get().n,n=e.prepare(`SELECT
715
+ (SELECT COUNT(*) FROM main.messages_archive) +
716
+ (SELECT COUNT(*) FROM archive.messages_archive) AS n`).get().n,r=e.prepare("SELECT MIN(timestamp) AS t FROM messages WHERE timestamp IS NOT NULL").get(),o=e.prepare(`SELECT MAX(t) AS t FROM (
717
+ SELECT MAX(timestamp) AS t FROM main.messages_archive WHERE timestamp IS NOT NULL
718
+ UNION ALL
719
+ SELECT MAX(timestamp) AS t FROM archive.messages_archive WHERE timestamp IS NOT NULL
720
+ )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:s,archivedMessages:n,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function La(e){return e?e.slice(0,10):"\u2014"}function C_(){let e=L_();return console.log(Ee.dim("\u2014 Archive state \u2014")),console.log(` Live sessions ${e.liveSessions.toLocaleString()}`),console.log(` Archived sessions ${e.archivedSessions.toLocaleString()}`),console.log(` Live messages ${e.liveMessages.toLocaleString()}`),console.log(` Archived messages ${e.archivedMessages.toLocaleString()}`),console.log(` Oldest live ${La(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${La(e.newestArchivedTimestamp)}`),console.log(""),console.log(Ee.dim(" recall archive run --before YYYY-MM-DD")),console.log(Ee.dim(" recall archive restore <session-id>")),0}function v_(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let s=f().prepare(`SELECT s.id, s.ended_at, s.message_count
721
+ FROM sessions s
722
+ WHERE s.archive_status != 'archived'
723
+ AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(s.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let n=s.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${s.length.toLocaleString()} session(s), ${n.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(Ee.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=s.map(c=>c.id),o=Date.now(),a=fr(c=>c.transaction(d=>{let m=0,h=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
724
+ (uuid, session_id, parent_uuid, type, role, timestamp,
725
+ is_sidechain, content_text, tool_names, raw_json, archived_at)
726
+ SELECT uuid, session_id, parent_uuid, type, role, timestamp,
727
+ is_sidechain, content_text, tool_names, raw_json, datetime('now')
728
+ FROM messages WHERE session_id = ?`),b=c.prepare("DELETE FROM messages WHERE session_id = ?"),T=c.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let S of d){h.run(S);let w=b.run(S);m+=Number(w.changes??0),T.run(S)}return m})(r));return console.log(`Archived ${s.length.toLocaleString()} session(s), moved ${a.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(Ee.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function I_(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let s=f().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!s)return console.error(`Session ${e} not found.`),1;if(s.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${s.archive_status}).`),1;let n=fr(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
729
+ (uuid, session_id, parent_uuid, type, role, timestamp,
730
+ is_sidechain, content_text, tool_names, raw_json)
731
+ SELECT uuid, session_id, parent_uuid, type, role, timestamp,
732
+ is_sidechain, content_text, tool_names, raw_json
733
+ FROM main.messages_archive WHERE session_id = ?`),c=r.prepare(`INSERT OR IGNORE INTO messages
734
+ (uuid, session_id, parent_uuid, type, role, timestamp,
735
+ is_sidechain, content_text, tool_names, raw_json)
736
+ SELECT uuid, session_id, parent_uuid, type, role, timestamp,
737
+ is_sidechain, content_text, tool_names, raw_json
738
+ FROM archive.messages_archive WHERE session_id = ?`),u=r.prepare("DELETE FROM main.messages_archive WHERE session_id = ?"),d=r.prepare("DELETE FROM archive.messages_archive WHERE session_id = ?"),m=Number(a.run(e).changes??0),h=Number(c.run(e).changes??0);return u.run(e),d.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),m+h})());return console.log(`Restored ${n.toLocaleString()} message(s) for session ${e}.`),0}function j_(){let e=mr();return console.log(Ee.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?Ee.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function M_(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let s=Os({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${s.autoArchiveAfterDays} days).`),console.log(Ee.dim(" The daemon will run a daily archive pass on the next tick.")),0}function D_(){return Os({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function F_(e){let t=e._action??"list";if(t==="list"||t==="stats")return C_();if(t==="run")return e.before?v_({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return I_(e._sessionId??"");if(t==="auto"){let s=e._subAction??"status";return s==="status"?j_():s==="on"||s==="enable"?M_(e.after):s==="off"||s==="disable"?D_():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
739
+ list \u2014 show archive counts
740
+ run --before YYYY-MM-DD [--dry-run] \u2014 move sessions older than DATE
741
+ restore <session-id> \u2014 pull a session back from archive
742
+ auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var va=ue(()=>{"use strict";Ls();H();_r();gr()});import{Hono as HT}from"hono";import{serve as WT}from"@hono/node-server";Q();import{existsSync as Cp,readFileSync as vp,writeFileSync as Jy,unlinkSync as Yy}from"node:fs";import{join as Ip}from"node:path";var ri=Ip(U,"license.json");function kt(){if(!Cp(ri))return null;try{let e=vp(ri,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as jp,importSPKI as Mp}from"jose";var oi=`-----BEGIN PUBLIC KEY-----
642
743
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
643
744
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
644
745
  -----END PUBLIC KEY-----
645
- `,wn="ES256",Po="clauderecall.com",Uo="clauderecall-cli";var ss=null;async function zu(){return ss||(ss=await Ku(Fo,wn),ss)}async function $o(e){try{let t=await zu(),{payload:s}=await Yu(e,t,{issuer:Po,audience:Uo,algorithms:[wn]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Vu}from"node:crypto";import{hostname as Zu,userInfo as Qu,platform as ep,arch as tp}from"node:os";function Bo(){let e="unknown";try{e=Qu().username}catch{}let t=[Zu(),e,ep(),tp()];return Vu("sha256").update(t.join("\0")).digest("hex")}Z();import{existsSync as sp,readFileSync as np,writeFileSync as rp}from"node:fs";import{join as op}from"node:path";function Ho(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),s;try{s=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let n=s.hostname==="127.0.0.1"||s.hostname==="localhost"||s.hostname==="::1";if(s.protocol==="https:"||s.protocol==="http:"&&n)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var Rn=op($,"license-check.json"),ip=1440*60*1e3,ap=720*60*60*1e3,cp=1e4;function Wo(){if(!sp(Rn))return null;try{let e=JSON.parse(np(Rn,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function lp(e){J(),rp(Rn,JSON.stringify(e,null,2)+`
646
- `,{mode:384})}async function dp(e,t){let s=null,n=null;try{s=new AbortController,n=setTimeout(()=>s?.abort(),cp);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:s.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{n&&clearTimeout(n)}}async function qo(e,t={}){let s=Wo(),n=t.apiUrl??`${Ho()}/api/license/check`,r=s?.license_key===e,o=!s||!r||Date.now()-new Date(s.last_checked_at).getTime()>=ip;if(!t.force&&!o)return s;let a=await dp(e,n);if(!a)return r?s:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return lp(c),c}function Jo(e){let t=Wo();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()>ap?{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 up=Date.UTC(2026,5,1,7,0,0);var pp=1440*60*1e3,ny=60*pp;async function Tt(){let e=St();if(!e)return{tier:"free"};let t=await $o(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Bo())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=Jo(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:mp(e,t.claims)}async function Xo(e){let t=St();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let s=await qo(t.license_key,{force:e?.force??!1});return s?{ran:!0,revoked:s.revoked,reason:s.reason,last_checked_at:s.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function mp(e,t){let s=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:s?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...s?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function Go(){return(await Tt()).tier==="pro"}H();import{serveStatic as cu}from"@hono/node-server/serve-static";import{existsSync as zb,readFileSync as hn}from"node:fs";import{stat as Vb,readFile as Zb,realpath as Qb}from"node:fs/promises";import{timingSafeEqual as eS}from"node:crypto";import{homedir as tS}from"node:os";import{dirname as Eo,join as bn}from"node:path";import{fileURLToPath as bo}from"node:url";import{existsSync as xy,readFileSync as Ny,statSync as Oy,statfsSync as Ep}from"node:fs";import Se from"chalk";import{formatDistanceToNowStrict as wy,parseISO as Ry}from"date-fns";var ei={dim:Se.gray,bold:Se.bold,project:Se.cyan,user:Se.blue,assistant:Se.green,tool:Se.magenta,warn:Se.yellow,err:Se.red,ok:Se.green,accent:Se.hex("#f97316")};H();Z();var bp=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],vy=new RegExp(`^(${bp.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var jy=5*6e4;function xn(){try{let e=Ep($);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function ti(e){let{projects:t,sessions:s,messages:n,port:r,version:o}=e;return`<!DOCTYPE html>
746
+ `,Un="ES256",ii="clauderecall.com",ai="clauderecall-cli";var ps=null;async function Dp(){return ps||(ps=await Mp(oi,Un),ps)}async function ci(e){try{let t=await Dp(),{payload:s}=await jp(e,t,{issuer:ii,audience:ai,algorithms:[Un]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Fp}from"node:crypto";import{hostname as Pp,userInfo as Up,platform as $p,arch as Bp}from"node:os";function li(){let e="unknown";try{e=Up().username}catch{}let t=[Pp(),e,$p(),Bp()];return Fp("sha256").update(t.join("\0")).digest("hex")}Q();import{existsSync as Hp,readFileSync as Wp,writeFileSync as qp}from"node:fs";import{join as Xp}from"node:path";function ui(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),s;try{s=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let n=s.hostname==="127.0.0.1"||s.hostname==="localhost"||s.hostname==="::1";if(s.protocol==="https:"||s.protocol==="http:"&&n)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var $n=Xp(U,"license-check.json"),Jp=1440*60*1e3,Yp=720*60*60*1e3,Gp=1e4;function di(){if(!Hp($n))return null;try{let e=JSON.parse(Wp($n,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function zp(e){J(),qp($n,JSON.stringify(e,null,2)+`
747
+ `,{mode:384})}async function Kp(e,t){let s=null,n=null;try{s=new AbortController,n=setTimeout(()=>s?.abort(),Gp);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:s.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{n&&clearTimeout(n)}}async function pi(e,t={}){let s=di(),n=t.apiUrl??`${ui()}/api/license/check`,r=s?.license_key===e,o=!s||!r||Date.now()-new Date(s.last_checked_at).getTime()>=Jp;if(!t.force&&!o)return s;let a=await Kp(e,n);if(!a)return r?s:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return zp(c),c}function mi(e){let t=di();return!t||t.license_key!==e?null:t.revoked?{revoked:!0,reason:t.reason?`license revoked: ${t.reason}`:"license revoked by issuer"}:Date.now()-new Date(t.last_checked_at).getTime()>Yp?{revoked:!0,reason:"license has not been validated with the server in 30+ days. Reconnect to the internet and run `recall license check`"}:null}var Vp=Date.UTC(2026,5,1,7,0,0);var Zp=1440*60*1e3,pw=60*Zp;async function xt(){let e=kt();if(!e)return{tier:"free"};let t=await ci(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==li())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=mi(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:Qp(e,t.claims)}async function gi(e){let t=kt();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let s=await pi(t.license_key,{force:e?.force??!1});return s?{ran:!0,revoked:s.revoked,reason:s.reason,last_checked_at:s.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function Qp(e,t){let s=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:s?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...s?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function _i(){return(await xt()).tier==="pro"}H();H();import{createHash as mm}from"node:crypto";H();Q();import{writeFileSync as rm,readFileSync as Cw,existsSync as om,mkdirSync as im,readdirSync as vw,unlinkSync as Iw}from"node:fs";import{join as yi}from"node:path";import{randomUUID as wi}from"node:crypto";var Wn=yi(U,"bug-patterns");function am(){J(),om(Wn)||im(Wn,{recursive:!0})}function ve(e){return{id:e.id,signature_hash:e.signature_hash,example_message:e.example_message,occurrence_count:e.occurrence_count,first_seen_at:e.first_seen_at,last_seen_at:e.last_seen_at,resolved_in_session_id:e.resolved_in_session_id,fix_summary:e.fix_summary}}function Ri(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function Ai(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=f(),s=new Date().toISOString(),n=e.id??wi(),r=e.first_seen_at??s,o=e.last_seen_at??s,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
748
+ (id, signature_hash, example_message, occurrence_count,
749
+ first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
750
+ VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(n,e.signature_hash,e.example_message,a.length,r,o);let u=t.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
751
+ VALUES (?, ?, ?)
752
+ ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(n,d,s)})();let c=ki(n);if(!c)throw new Error("createCluster succeeded but read-back failed");return Nt(n),c}function ki(e){let t=f(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:ve(s),members:n.map(Ri)}}function xi(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let s=f();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;s.transaction(()=>{let c=s.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
753
+ VALUES (?, ?, ?)
754
+ ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let u of Array.from(new Set(t)))c.run(e,u,r).changes>0&&(o+=1);if(o>0){let u=s.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;s.prepare(`UPDATE bug_pattern_clusters
755
+ SET occurrence_count = ?, last_seen_at = ?
756
+ WHERE id = ?`).run(u,r,e)}})(),o>0&&Nt(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:ve(a),added:o}}function Ni(e={}){let t=f(),s=[],n=[];typeof e.minOccurrenceCount=="number"&&(s.push("c.occurrence_count >= ?"),n.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&s.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(s.push(`EXISTS (
757
+ SELECT 1 FROM bug_pattern_members m
758
+ JOIN sessions s ON s.id = m.session_id
759
+ JOIN projects p ON p.id = s.project_id
760
+ WHERE m.cluster_id = c.id AND p.name = ?
761
+ )`),n.push(e.project));let r=s.length>0?`WHERE ${s.join(" AND ")}`:"",o=t.prepare(`SELECT COUNT(*) AS n FROM bug_pattern_clusters c ${r}`).get(...n),a=Math.max(1,Math.min(5e3,e.limit??100)),c=Math.max(0,Math.floor(e.offset??0));return{clusters:t.prepare(`SELECT c.* FROM bug_pattern_clusters c ${r}
762
+ ORDER BY c.occurrence_count DESC, c.last_seen_at DESC
763
+ LIMIT ? OFFSET ?`).all(...n,a,c).map(ve),total:o.n}}function cm(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,s=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:s,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function Oi(e){let t=f(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
764
+ NULLIF(sa.alias, '') AS alias,
765
+ s.auto_title,
766
+ s.first_user_message,
767
+ p.name AS project,
768
+ s.started_at
769
+ FROM bug_pattern_members m
770
+ LEFT JOIN sessions s ON s.id = m.session_id
771
+ LEFT JOIN session_aliases sa ON sa.session_id = m.session_id
772
+ LEFT JOIN projects p ON p.id = s.project_id
773
+ WHERE m.cluster_id = ?
774
+ ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:ve(s),members:n.map(cm)}}function Li(e,t,s){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof s!="string"||!s.trim())throw new Error("fixSummary is required");let n=f();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!n.prepare("SELECT 1 FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?").get(e,t))throw new Error(`session ${t} is not a member of cluster ${e}`);n.prepare(`UPDATE bug_pattern_clusters
775
+ SET resolved_in_session_id = ?, fix_summary = ?
776
+ WHERE id = ?`).run(t,s.trim(),e),Nt(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:ve(a)}}function Ci(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("memberSessionIds must be a non-empty array");let s=f(),n=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=s.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
777
+ WHERE cluster_id = ? AND session_id IN (${o})`).all(e,...r);if(a.length===0)throw new Error(`none of the supplied session_ids are members of cluster ${e}`);let c=s.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;if(a.length>=c)throw new Error(`cannot split: would leave the original cluster empty (move ${a.length} of ${c})`);let u=wi(),d=new Date().toISOString(),m=[];s.transaction(()=>{s.prepare(`INSERT INTO bug_pattern_clusters
778
+ (id, signature_hash, example_message, occurrence_count,
779
+ first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
780
+ VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(u,n.signature_hash,n.example_message,a.length,n.first_seen_at,d);let T=s.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
781
+ VALUES (?, ?, ?)`),S=s.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let R of a)T.run(u,R.session_id,R.matched_at),S.run(e,R.session_id);let w=c-a.length;s.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(w,e),m=s.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),Nt(e),Nt(u);let h=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),b=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:ve(h),split:ve(b),movedMembers:m.map(Ri)}}function Nt(e){try{am();let t=ki(e);if(!t)return;let s=yi(Wn,`${e}.json`),n={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};rm(s,JSON.stringify(n,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function vi(e){return e?f().prepare(`SELECT * FROM bug_pattern_clusters
782
+ WHERE signature_hash = ?
783
+ ORDER BY last_seen_at DESC, id ASC`).all(e).map(ve):[]}function Ii(e){if(!e)return new Set;let s=f().prepare(`SELECT DISTINCT m.session_id
784
+ FROM bug_pattern_members m
785
+ JOIN bug_pattern_clusters c ON c.id = m.cluster_id
786
+ WHERE c.signature_hash = ?`).all(e);return new Set(s.map(n=>n.session_id))}var gm=/\b0x[0-9a-fA-F]+\b/g,_m=/\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g,fm=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,hm=/:\d+:\d+/g,Em=/\bline\s+\d+\b/gi,bm=/\bcolumn\s+\d+\b/gi,Sm=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,Tm=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,ym=/\b\d{4,}\b/g,wm=/(['"`])[^'"`\n]{1,128}\1/g;function Rm(e){if(!e)return"";let t=String(e);return t=t.replace(gm,"<hex>"),t=t.replace(_m,"<uuid>"),t=t.replace(fm,"<ts>"),t=t.replace(hm,":<line>:<col>"),t=t.replace(Em,"line <n>"),t=t.replace(bm,"column <n>"),t=t.replace(Sm,"pid <n>"),t=t.replace(Tm,"port <n>"),t=t.replace(ym,"<num>"),t=t.replace(wm,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function Am(e){let t=(e.error_type??"unknown").toLowerCase().trim(),s=Rm(e.snippet??e.message_hash??""),n=`${t}|${s}`;return mm("sha256").update(n).digest("hex").slice(0,16)}function km(e){let t=f(),s=["oi.bug_signatures IS NOT NULL"],n=[];e&&(s.push("p.name = ?"),n.push(e));let r=`WHERE ${s.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
787
+ p.name AS project,
788
+ s.started_at AS started_at,
789
+ oi.bug_signatures AS bug_signatures
790
+ FROM session_output_index oi
791
+ LEFT JOIN sessions s ON s.id = oi.session_id
792
+ LEFT JOIN projects p ON p.id = s.project_id
793
+ ${r}`).all(...n),a=[];for(let c of o){if(!c.bug_signatures)continue;let u=[];try{let d=JSON.parse(c.bug_signatures);Array.isArray(d)&&(u=d)}catch{continue}for(let d of u){if(!d||typeof d!="object"||!(d.snippet??"").trim())continue;let h=Am(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:h})}}return a}function xm(e){let t=new Map;for(let n of e){let r=t.get(n.fingerprint);r||(r=[],t.set(n.fingerprint,r)),r.push(n)}let s=[];for(let[n,r]of t){let o=new Set,a=[];for(let m of r)o.has(m.session_id)||(o.add(m.session_id),a.push(m));let c=[...a].sort((m,h)=>{let b=m.started_at??"",T=h.started_at??"";return b&&T?b<T?-1:b>T?1:0:b?-1:T?1:0}),u=c.find(m=>m.started_at),d=[...c].reverse().find(m=>m.started_at);s.push({fingerprint:n,example_message:a[0].signature.snippet??a[0].signature.message_hash??"",members:a,first_seen_at:u?.started_at??new Date().toISOString(),last_seen_at:d?.started_at??new Date().toISOString()})}return s}function Nm(e,t){if(e.length!==t.length)return 0;let s=0,n=0,r=0;for(let a=0;a<e.length;a++)s+=e[a]*t[a],n+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(n)*Math.sqrt(r);return o>0?s/o:0}function Om(e){let{records:t,vectors:s,epsilon:n,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let m=0;m<o;m++){let h=[];for(let b=0;b<o;b++){if(m===b)continue;1-Nm(s[m],s[b])<=n&&h.push(b)}a.push(h)}let c=new Array(o).fill(!1),u=new Array(o).fill(-1),d=[];for(let m=0;m<o;m++){if(c[m])continue;c[m]=!0;let h=a[m];if(h.length<r)continue;let b=d.length;d.push({members:[t[m]]}),u[m]=b;let T=[...h];for(;T.length>0;){let S=T.shift();if(!c[S]&&(c[S]=!0,a[S].length>=r))for(let w of a[S])(!c[w]||u[w]===-1)&&T.push(w);u[S]===-1&&(u[S]=b,d[b].members.push(t[S]))}}return d}async function Lm(e,t,s,n){if(e.length===0)return[];let r=e.map(u=>{let d=u.signature.snippet??u.signature.message_hash??"";return`${u.signature.error_type??""}: ${d}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let a=Om({records:e,vectors:o,epsilon:s,minPts:n}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,m=[];for(let R of u.members)d.has(R.session_id)||(d.add(R.session_id),m.push(R));if(m.length===0)continue;let b=`sem:${[...m.map(R=>R.fingerprint)].sort()[0]}`,T=[...m].sort((R,D)=>{let O=R.started_at??"",Y=D.started_at??"";return O<Y?-1:O>Y?1:0}),S=T.find(R=>R.started_at),w=[...T].reverse().find(R=>R.started_at);c.push({fingerprint:b,example_message:m[0].signature.snippet??m[0].signature.message_hash??"",members:m,first_seen_at:S?.started_at??new Date().toISOString(),last_seen_at:w?.started_at??new Date().toISOString()})}return c}function Pi(e,t){let s={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let n of e){if(n.members.length<t)continue;let r=vi(n.fingerprint),o=Ii(n.fingerprint),a=n.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=Ai({signature_hash:n.fingerprint,example_message:n.example_message.slice(0,256),member_session_ids:n.members.map(m=>m.session_id),first_seen_at:n.first_seen_at,last_seen_at:n.last_seen_at});s.clusters_created+=1,s.members_added+=d.members.length,s.cluster_ids.push(d.cluster.id);continue}if(a.length===0){s.cluster_ids.push(r[0].id);continue}let c=r[0],u=xi(c.id,a);u.added>0&&(s.clusters_merged+=1,s.members_added+=u.added),s.cluster_ids.push(c.id)}return s}async function Ui(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),s=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),n=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=km(e.project),a=new Set(o.map(O=>O.session_id)),c=xm(o),u=[],d=!1;if(e.semantic){let O=e.embedder??null;if(!O)try{O=await Im()}catch(Y){let W=(Y instanceof Error?Y.message:String(Y)).split(`
794
+ `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${W}
795
+ Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(O){let Y=[];for(let M of c)M.members.length===1&&Y.push(M.members[0]);Y.length>=2&&(u=await Lm(Y,O,n,r))}}let m=c.filter(O=>O.members.length>=t),h=u.filter(O=>O.members.length>=t),b=Pi(m,t),T=Pi(h,t),S=[...b.cluster_ids,...T.cluster_ids],w=Array.from(new Set(S)),R=[];if(w.length>0){let O=f(),Y=w.map(()=>"?").join(","),M=O.prepare(`SELECT * FROM bug_pattern_clusters
796
+ WHERE id IN (${Y})
797
+ ORDER BY occurrence_count DESC, last_seen_at DESC
798
+ LIMIT ?`).all(...w,s);for(let W of M)R.push({id:W.id,signature_hash:W.signature_hash,example_message:W.example_message,occurrence_count:W.occurrence_count,first_seen_at:W.first_seen_at,last_seen_at:W.last_seen_at,resolved_in_session_id:W.resolved_in_session_id,fix_summary:W.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:m.length,semantic_groups:h.length,clusters_created:b.clusters_created+T.clusters_created,clusters_merged:b.clusters_merged+T.clusters_merged,members_added:b.members_added+T.members_added,semantic_skipped:d},clusters:R}}var Cm=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:s}=await Promise.resolve().then(()=>(ze(),Fi));return s().loaded||await t(),e},vm=Cm;async function Im(){return vm()}H();H();Q();import{writeFileSync as $i,readFileSync as Jw,existsSync as Bi,mkdirSync as Hi,readdirSync as Yw}from"node:fs";import{join as _s}from"node:path";var jm=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),Wi=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),Mm=new Set(["pending","approved","rejected"]),Dm=new Set(["L1","L2","L3","L4","user"]),Xn=_s(U,"links"),Jn=_s(U,"suggestions"),Fm=_s(Jn,"index.json");function Pm(){J(),Bi(Xn)||Hi(Xn,{recursive:!0})}function Um(){J(),Bi(Jn)||Hi(Jn,{recursive:!0})}function qi(e){try{return JSON.parse(e)}catch{return e}}function fs(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,source:e.source,evidence:qi(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Yn(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,evidence:qi(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function Xi(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function Gn(e){if(!jm.has(e))throw new Error(`invalid link_type: ${e}`)}function $m(e){if(!Wi.has(e))throw new Error(`invalid source: ${e}`)}function Ji(e){if(!Dm.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Yi(e,t){if(!e||!t)throw new Error("source_session_id and target_session_id are required");if(e===t)throw new Error("a session cannot link to itself")}function Gi(e){Yi(e.source_session_id,e.target_session_id),Gn(e.link_type),$m(e.source),Xi(e.confidence);let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
799
+ (source_session_id, target_session_id, link_type,
800
+ confidence, source, evidence, approved,
801
+ created_at, updated_at)
802
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
803
+ ON CONFLICT(source_session_id, target_session_id, link_type) DO UPDATE SET
804
+ confidence = excluded.confidence,
805
+ source = excluded.source,
806
+ evidence = excluded.evidence,
807
+ approved = excluded.approved,
808
+ updated_at = excluded.updated_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,e.source,n,r,s,s);let o=t.prepare(`SELECT * FROM session_links
809
+ WHERE source_session_id = ?
810
+ AND target_session_id = ?
811
+ AND link_type = ?`).get(e.source_session_id,e.target_session_id,e.link_type);if(!o)throw new Error("createLink succeeded but read-back failed");return Kn(e.source_session_id),fs(o)}function hs(e={}){let t=f(),s=[],n=[];e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.linkType&&(Gn(e.linkType),s.push("link_type = ?"),n.push(e.linkType)),e.approvedOnly&&s.push("approved = 1");let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
812
+ ORDER BY confidence DESC, updated_at DESC
813
+ LIMIT ?`).all(...n,o).map(fs)}function vt(e){return f().prepare(`SELECT * FROM session_links
814
+ WHERE source_session_id = ? OR target_session_id = ?
815
+ ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(fs)}function zi(e){let t=f(),s=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!s)return{removed:0,sourceSessionId:null};let n=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return n.changes>0&&Kn(s.source_session_id),{removed:n.changes,sourceSessionId:s.source_session_id}}function It(e){Yi(e.source_session_id,e.target_session_id),Gn(e.link_type),Xi(e.confidence),Ji(e.inferred_by);let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
816
+ (source_session_id, target_session_id, link_type,
817
+ confidence, evidence, status, inferred_by,
818
+ created_at, decided_at)
819
+ VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, NULL)
820
+ ON CONFLICT(source_session_id, target_session_id, link_type, inferred_by) DO UPDATE SET
821
+ confidence = CASE
822
+ WHEN session_link_suggestions.status = 'pending'
823
+ THEN excluded.confidence
824
+ ELSE session_link_suggestions.confidence
825
+ END,
826
+ evidence = CASE
827
+ WHEN session_link_suggestions.status = 'pending'
828
+ THEN excluded.evidence
829
+ ELSE session_link_suggestions.evidence
830
+ END,
831
+ created_at = session_link_suggestions.created_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,n,e.inferred_by,s);let r=t.prepare(`SELECT * FROM session_link_suggestions
832
+ WHERE source_session_id = ?
833
+ AND target_session_id = ?
834
+ AND link_type = ?
835
+ AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!r)throw new Error("createSuggestion succeeded but read-back failed");return Ki(),Yn(r)}function Ke(e={}){let t=f(),s=[],n=[];if(e.status){if(!Mm.has(e.status))throw new Error(`invalid status: ${e.status}`);s.push("status = ?"),n.push(e.status)}e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.inferredBy&&(Ji(e.inferredBy),s.push("inferred_by = ?"),n.push(e.inferredBy));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
836
+ ORDER BY confidence DESC, created_at DESC
837
+ LIMIT ?`).all(...n,o).map(Yn)}function zn(e,t,s={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let n=s.source??"manual";if(!Wi.has(n))throw new Error(`invalid source: ${n}`);let r=f(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let a=new Date().toISOString(),c;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
838
+ SET status = ?, decided_at = ?
839
+ WHERE id = ?`).run(t,a,e),t==="approved"&&(r.prepare(`INSERT INTO session_links
840
+ (source_session_id, target_session_id, link_type,
841
+ confidence, source, evidence, approved,
842
+ created_at, updated_at)
843
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)
844
+ ON CONFLICT(source_session_id, target_session_id, link_type) DO UPDATE SET
845
+ confidence = excluded.confidence,
846
+ source = excluded.source,
847
+ evidence = excluded.evidence,
848
+ approved = 1,
849
+ updated_at = excluded.updated_at`).run(o.source_session_id,o.target_session_id,o.link_type,o.confidence,n,o.evidence,a,a),c=r.prepare(`SELECT * FROM session_links
850
+ WHERE source_session_id = ?
851
+ AND target_session_id = ?
852
+ AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),Ki(),t==="approved"&&Kn(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:Yn(u),link:c?fs(c):null}}function Kn(e){try{Pm();let t=hs({sourceSessionId:e}),s=_s(Xn,`${e}.json`);if(t.length===0)return;let n={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};$i(s,JSON.stringify(n,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function Ki(){try{Um();let e=Ke({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};$i(Fm,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}H();Q();import{writeFileSync as Bm,readFileSync as Qw,existsSync as Hm,mkdirSync as Wm,readdirSync as e0}from"node:fs";import{join as Vi}from"node:path";var Vn=Vi(U,"output-index");function qm(){J(),Hm(Vn)||Wm(Vn,{recursive:!0})}function jt(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Xm(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function Zi(e){return{session_id:e.session_id,files_written:jt(e.files_written),brands_mentioned:jt(e.brands_mentioned),terms_introduced:jt(e.terms_introduced),plan_ids_referenced:jt(e.plan_ids_referenced),bug_signatures:jt(e.bug_signatures),raw_extraction:Xm(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function Qi(e){if(!e.session_id)throw new Error("session_id is required");let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),a=JSON.stringify(e.plan_ids_referenced??[]),c=JSON.stringify(e.bug_signatures??[]),u=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),d=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
853
+ (session_id, files_written, brands_mentioned, terms_introduced,
854
+ plan_ids_referenced, bug_signatures, raw_extraction,
855
+ extracted_at, extractor_version)
856
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
857
+ ON CONFLICT(session_id) DO UPDATE SET
858
+ files_written = excluded.files_written,
859
+ brands_mentioned = excluded.brands_mentioned,
860
+ terms_introduced = excluded.terms_introduced,
861
+ plan_ids_referenced = excluded.plan_ids_referenced,
862
+ bug_signatures = excluded.bug_signatures,
863
+ raw_extraction = excluded.raw_extraction,
864
+ extracted_at = excluded.extracted_at,
865
+ extractor_version = excluded.extractor_version`).run(e.session_id,n,r,o,a,c,u,s,d);let m=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!m)throw new Error("setOutputIndex succeeded but read-back failed");let h=Zi(m);return Jm(e.session_id),h}function $e(e){let s=f().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return s?Zi(s):null}function Jm(e){try{qm();let t=$e(e);if(!t)return;let s=Vi(Vn,`${e}.json`),n={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};Bm(s,JSON.stringify(n,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var Zn={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},Ym=2,Gm=.25,zm=5,Km=60,Vm=25;function Es(e){return e.trim().toLowerCase()}function Zm(e){let t=new Set;for(let s of e.files_written)t.add(`file:${Es(s)}`);for(let s of e.brands_mentioned)t.add(`brand:${Es(s)}`);for(let s of e.terms_introduced)t.add(`term:${Es(s)}`);for(let s of e.plan_ids_referenced)t.add(`plan:${Es(s)}`);return t}function Qm(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Km);return Math.max(.2,t)}function eg(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function tg(e){let t=$e(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(s=>s.term),plan_ids_referenced:t.plan_ids_referenced}:null}function sg(e){return f().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
866
+ FROM sessions s
867
+ JOIN session_output_index oi ON oi.session_id = s.id
868
+ WHERE s.project_id = ?
869
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function ng(e,t){let s=new Map,n=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let a=t.get(o.id);if(!a)continue;let c=Zm(a);if(c.size!==0){n.set(o.id,c);for(let u of c){let d=s.get(u);d?d.push(o.id):s.set(u,[o.id])}}}return{posting:s,vocab:n,startedAt:r}}function rg(e,t){let s=t.vocab.get(e),n=t.startedAt.get(e)??null;if(!s||!n)return[];let r=new Map;for(let a of s){let c=t.posting.get(a);if(c)for(let u of c){if(u===e)continue;let d=t.startedAt.get(u);if(!d||d>=n)continue;let m=r.get(u);m?m.push(a):r.set(u,[a])}}let o=[];for(let[a,c]of r){let u=c.length;if(u<Ym)continue;let d=t.startedAt.get(a)??null,m=eg(n,d),h=Qm(m),b=Math.min(1,u/zm*h);if(b<Gm)continue;let T=c.slice(0,12);o.push({target_session_id:a,matched_terms:T,overlap:u,days_apart:Math.round(m*10)/10,recency:Math.round(h*1e3)/1e3,confidence:Math.round(b*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,Vm)}async function ea(e){if(Zn.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=sg(e.projectId),s=new Map;for(let a of t){let c=tg(a.id);c&&s.set(a.id,c)}let n=ng(t,s),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let a of t){if(e.signal?.aborted)break;r.current_session_id=a.id,e.onProgress?.({...r});let c=rg(a.id,n);for(let u of c)try{let d=It({source_session_id:a.id,target_session_id:u.target_session_id,link_type:"citation",confidence:u.confidence,evidence:{matched_terms:u.matched_terms,overlap_count:u.overlap,recency:u.recency,days_apart:u.days_apart},inferred_by:"L2"});o.push(d.id),r.suggestions_created+=1}catch(d){console.error("[citation-inference] createSuggestion failed:",d)}r.processed_sessions+=1,e.onProgress?.({...r})}return r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:o}}H();var og=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,ig=[/\bv\d+\.\d+(?:\.[A-Za-z]+)?\b/g,/\bPhase\s+[A-Ha-h]\b/g,/\bL\d+\b/g,/MASTER-PLAN-[A-Za-z-]+/g],ag=.95,cg=.85;var Qn=50;function lg(e){if(!e)return[];let t=new Set,s=[],n=e.match(og);if(!n)return s;for(let r of n){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),s.push(o),s.length>=Qn))break}return s}function ug(e){if(!e)return[];let t=new Set,s=[];for(let n of ig){n.lastIndex=0;let r=e.match(n);if(r){for(let o of r){let a=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!a||a.length>64)&&!t.has(a)&&(t.add(a),s.push(a),s.length>=Qn))break}if(s.length>=Qn)break}}return s}function ta(e){let t=f();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function sa(e){let t=f();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
870
+ s.project_id
871
+ FROM messages m
872
+ JOIN sessions s ON s.id = m.session_id
873
+ WHERE s.project_id = ?
874
+ AND m.is_sidechain = 0
875
+ AND m.content_text IS NOT NULL
876
+ AND length(m.content_text) > 0`).all(e):t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
877
+ s.project_id
878
+ FROM messages m
879
+ JOIN sessions s ON s.id = m.session_id
880
+ WHERE m.is_sidechain = 0
881
+ AND m.content_text IS NOT NULL
882
+ AND length(m.content_text) > 0`).all()}function dg(e){let t=f(),s=typeof e=="number"?"WHERE s.project_id = ?":"",n=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
883
+ s.project_id AS project_id,
884
+ oi.plan_ids_referenced AS plan_ids_json
885
+ FROM session_output_index oi
886
+ JOIN sessions s ON s.id = oi.session_id
887
+ ${s}`).all(...n)}function pg(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>typeof s=="string")}catch{}return[]}function na(e={}){if(Zn.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=ta(e.projectId),s=new Set(t.map(o=>o.id)),n=new Map;for(let o of t)n.set(o.id,o.project_id);let r=0;for(let o of sa(e.projectId)){if(e.signal?.aborted)break;let a=lg(o.content_text);if(a.length===0)continue;let c=n.get(o.session_id);if(c!==void 0)for(let u of a){if(u===o.session_id)continue;let d=n.get(u);if(!(d===void 0&&!s.has(u))&&!(d!==void 0&&c!==void 0&&d!==c))try{It({source_session_id:o.session_id,target_session_id:u,link_type:"citation",confidence:ag,evidence:{matched_uuid:u,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function ra(e={}){let t=dg(e.projectId);if(t.length===0)return{created:0};let s=new Map;for(let c of t){let u=pg(c.plan_ids_json);for(let d of u){let m=d.trim().toLowerCase();if(!m)continue;let h=s.get(m);h?h.push({id:c.session_id,project_id:c.project_id}):s.set(m,[{id:c.session_id,project_id:c.project_id}])}}if(s.size===0)return{created:0};let n=ta(e.projectId),r=new Map;for(let c of n)r.set(c.id,c.project_id);let o=0,a=new Map;for(let c of sa(e.projectId)){if(e.signal?.aborted)break;let u=ug(c.content_text);if(u.length===0)continue;let d=r.get(c.session_id);if(d!==void 0)for(let m of u){let h=s.get(m);if(h)for(let b of h){if(b.id===c.session_id||b.project_id!==d)continue;let T=a.get(c.session_id);T||(T=new Map,a.set(c.session_id,T));let S=T.get(b.id);S||(S=new Set,T.set(b.id,S)),S.add(m)}}}for(let[c,u]of a)for(let[d,m]of u)try{It({source_session_id:c,target_session_id:d,link_type:"citation",confidence:cg,evidence:{matched_plan_ids:Array.from(m).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}H();he();import{existsSync as Hg,mkdirSync as Wg,writeFileSync as qg}from"node:fs";import{homedir as Xg}from"node:os";import{join as ir}from"node:path";H();import{existsSync as da,mkdirSync as Fg,readFileSync as Pg,writeFileSync as Ug}from"node:fs";import{homedir as $g}from"node:os";import{join as pa}from"node:path";import{z as Oe}from"zod";function ma(){return process.env.RECALL_HOME??pa($g(),".recall")}function Bg(){let e=ma();da(e)||Fg(e,{recursive:!0})}function ga(){return pa(ma(),"config.json")}var ys=Oe.object({enabled:Oe.boolean().default(!1),model:Oe.string().optional(),ratePerMinute:Oe.number().int().min(1).max(600).default(30),lastProcessedSessionId:Oe.string().nullable().default(null),backfillPaused:Oe.boolean().default(!1),autoExtractEnabled:Oe.boolean().default(!1),autoExtractIntervalMinutes:Oe.number().int().min(5).max(720).default(60),autoExtractBatchSize:Oe.number().int().min(1).max(20).default(1)}),Ts={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1};function _a(){let e=ga();if(!da(e))return{};try{return JSON.parse(Pg(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ae(){let e=_a().semantic;if(!e)return{...Ts};let t=ys.safeParse({...Ts,...e});return t.success?t.data:{...Ts}}function ws(e){Bg();let t=_a(),s=ys.parse({...Ts,...t.semantic??{},...e}),n={...t,semantic:s};return Ug(ga(),JSON.stringify(n,null,2)),or(s.enabled),s}function or(e){try{f().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
888
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(e?"1":"0")}catch(t){let s=t instanceof Error?t.message:String(t);console.error(`[semantic-config] failed to sync semantic_enabled: ${s}`)}}var Jg=1,Yg=12e3,fa=3,Gg=[];function zg(){return process.env.RECALL_HOME??ir(Xg(),".recall")}function ha(){return ir(zg(),"semantic")}function Kg(){let e=ha();Hg(e)||Wg(e,{recursive:!0})}function Vg(e){let t=f(),s=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
889
+ p.name AS project,
890
+ NULLIF(sa.alias, '') AS alias
891
+ FROM sessions s
892
+ JOIN projects p ON p.id = s.project_id
893
+ LEFT JOIN session_aliases sa ON sa.session_id = s.id
894
+ WHERE s.id = ?`).get(e);if(!s)return null;let n=t.prepare(`SELECT role, content_text
895
+ FROM messages
896
+ WHERE session_id = ? AND is_sidechain = 0
897
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of n){if(!a.content_text)continue;let c=a.role??"system",u=a.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!u)continue;let d=u.length>1500?u.slice(0,1500)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>Yg)break;r.push(m),o+=m.length}return{id:s.id,alias:s.alias,project:s.project,firstUserMessage:s.first_user_message,excerpt:r.join(`
898
+
899
+ `),messageCount:s.message_count}}function Zg(e){return["You are summarizing a Claude Code session for a local semantic-search index. The summary will be stored as plain text and matched against future natural-language queries.","",`Session: ${e.alias??e.id}`,`Project: ${e.project}`,e.firstUserMessage?`Opening prompt: ${e.firstUserMessage}`:"","","Transcript excerpt (truncated):","---",e.excerpt,"---","","Output a single JSON object on one line, with no Markdown fences and no commentary:",'{"summary": "<3 sentences describing what the user was trying to do, what was built or debugged, and the outcome>", "keywords": ["<concept>", "<technology>", "<problem>", ...]}',"","Constraints:","- summary: 3 sentences, plain prose, no bullet points",'- keywords: 10\u201315 lowercase tokens, multi-word entries hyphenated (e.g. "memory-leak"); no duplicates; no generic words like "code" or "session"',"- Output JSON only. Do not echo this prompt."].filter(Boolean).join(`
900
+ `)}function Qg(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+1);try{let o=JSON.parse(r),a=typeof o.summary=="string"?o.summary.trim():"",u=(Array.isArray(o.keywords)?o.keywords:[]).filter(d=>typeof d=="string").map(d=>d.trim().toLowerCase()).filter(d=>d.length>0&&d.length<64);return!a||u.length===0?null:{summary:a,keywords:Array.from(new Set(u)).slice(0,20)}}catch{return null}}function e_(e){let t=f(),s=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
901
+ (session_id, summary, keywords, model, source_message_count, generated_at)
902
+ VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
903
+ ON CONFLICT(session_id) DO UPDATE SET
904
+ summary = excluded.summary,
905
+ keywords = excluded.keywords,
906
+ model = excluded.model,
907
+ source_message_count = excluded.source_message_count,
908
+ generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:s,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),Kg();let n=ir(ha(),`${e.sessionId}.json`);qg(n,JSON.stringify({version:Jg,session_id:e.sessionId,summary:e.summary,keywords:e.keywords,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt},null,2))}var Rs=null;function t_(){let t=ae().ratePerMinute,s=t/6e4;return(!Rs||Rs.capacity!==t)&&(Rs={tokens:t,capacity:t,refillPerMs:s,lastRefill:Date.now()}),Rs}function s_(e){let t=Date.now(),s=t-e.lastRefill;s>0&&(e.tokens=Math.min(e.capacity,e.tokens+s*e.refillPerMs),e.lastRefill=t)}async function n_(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=t_();if(s_(t),t.tokens>=1){t.tokens-=1;return}let s=1-t.tokens,n=Math.max(50,Math.ceil(s/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(n,5e3)))}}async function As(e,t={}){let s=ae();if(!s.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!le())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let n=Vg(e);if(!n)return{sessionId:e,ok:!1,reason:"session-not-found"};if(n.messageCount<fa)return{sessionId:e,ok:!1,reason:"too-short"};if(!n.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await n_(t.signal);let r=Zg(n),o=await He(r,Gg,{model:s.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:s.model??null};let a=Qg(o.stdout);return a?(e_({sessionId:n.id,summary:a.summary,keywords:a.keywords,model:s.model??null,sourceMessageCount:n.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:s.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:s.model??null}}async function ks(e={}){let t=ae();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let s=f(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let a=s.prepare(`SELECT s.id
909
+ FROM sessions s
910
+ LEFT JOIN session_semantic ss ON ss.session_id = s.id
911
+ WHERE ${o}
912
+ ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
913
+ LIMIT @limit`).all(r),c={total:a.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(c);for(let{id:u}of a){if(e.signal?.aborted||ae().backfillPaused)break;c.currentSessionId=u,e.onProgress?.({...c});try{(await As(u,{signal:e.signal})).ok?c.ok+=1:c.failed+=1}catch(m){c.failed+=1,console.error("[semantic.backfill] failed for",u,m)}c.processed+=1,ws({lastProcessedSessionId:u}),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function Ea(e){if(!ae().enabled)return;let n=f().prepare(`SELECT s.message_count, s.ended_at,
914
+ ss.generated_at, ss.source_message_count
915
+ FROM sessions s
916
+ LEFT JOIN session_semantic ss ON ss.session_id = s.id
917
+ WHERE s.id = ?`).get(e);if(n&&!(n.message_count<fa)&&!(n.generated_at&&n.source_message_count!=null&&n.source_message_count>=n.message_count))try{await As(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function ar(){let e=ae(),t=f(),s=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,n=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:le(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:s,processedSessions:n,pendingSessions:Math.max(0,s-n),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}H();import{createHash as a_}from"node:crypto";var r_=[{name:"Anthropic API key",regex:/sk-ant-[a-zA-Z0-9_\-]{40,}/g,severity:"high"},{name:"OpenAI API key",regex:/sk-(?:proj-)?[a-zA-Z0-9]{32,}/g,severity:"high"},{name:"AWS access key ID",regex:/AKIA[0-9A-Z]{16}/g,severity:"high"},{name:"GitHub PAT",regex:/gh[pousr]_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Stripe live/test key",regex:/(?:sk|rk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,severity:"high"},{name:"Slack token",regex:/xox[abprs]-[A-Za-z0-9\-]{10,}/g,severity:"high"},{name:"Google API key",regex:/AIza[0-9A-Za-z_\-]{35}/g,severity:"high"},{name:"Private key block",regex:/-----BEGIN (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,severity:"high"},{name:"Apify token",regex:/apify_api_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Notion integration",regex:/(?:secret_|ntn_)[A-Za-z0-9]{40,}/g,severity:"high"},{name:"Vercel token",regex:/vercel_[A-Za-z0-9]{24,}/g,severity:"high"},{name:"Supabase service key",regex:/sbp_[A-Za-z0-9]{40,}/g,severity:"high"},{name:"SendGrid key",regex:/SG\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}/g,severity:"high"},{name:"Mailgun key",regex:/key-[a-f0-9]{32}/g,severity:"high"},{name:"Twilio SID",regex:/AC[a-f0-9]{32}/g,severity:"high"},{name:"Discord bot token",regex:/[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27,38}/g,severity:"high"},{name:"npm token",regex:/npm_[A-Za-z0-9]{36}/g,severity:"high"},{name:"HuggingFace token",regex:/hf_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"Replicate token",regex:/r8_[A-Za-z0-9]{32,}/g,severity:"high"},{name:"Figma token",regex:/figd_[A-Za-z0-9_\-]{30,}/g,severity:"high"},{name:"Linear key",regex:/lin_api_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"DigitalOcean token",regex:/dop_v1_[a-f0-9]{64}/g,severity:"high"},{name:"Generic provider token",regex:/\b[a-z][a-z0-9]{2,20}_(?:api|pat|token|sk|pk|key|auth)_(?=[A-Za-z0-9_\-]*\d)[A-Za-z0-9_\-]{20,}\b/g,severity:"high"},{name:"Bearer token",regex:/\b[Bb]earer\s+[A-Za-z0-9_\-\.=]{24,}\b/g,severity:"medium"},{name:"Slack webhook URL",regex:/https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Discord webhook URL",regex:/https:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_\-]{40,}/g,severity:"high"},{name:"Teams webhook URL",regex:/https:\/\/[a-zA-Z0-9.\-]+\.webhook\.office\.com\/webhookb2\/[A-Za-z0-9@_\-\/]{30,}/g,severity:"high"},{name:"Secret near keyword",regex:/\b(?:webhook[_\s\-]?secret|signing[_\s\-]?secret|webhook[_\s\-]?signing[_\s\-]?secret|api[_\s\-]?secret|client[_\s\-]?secret|private[_\s\-]?key|access[_\s\-]?token|auth[_\s\-]?token|api[_\s\-]?key)\b[\s\S]{0,200}?\b(?:[a-fA-F0-9]{32,}|[A-Za-z0-9+/_\-]{20,}(?:\.[A-Za-z0-9+/_\-]{10,}){1,2}|[A-Za-z0-9+/_\-]{40,}={0,2})\b/gi,severity:"high"},{name:"JWT",regex:/eyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}/g,severity:"medium"},{name:"URL with password",regex:/https?:\/\/[^:\s/@]+:[^@\s]{6,}@[^\s/]+/g,severity:"high"},{name:"Password assignment",regex:/(?<![A-Za-z])(?:password|passwd|pwd|secret|token|api[_\-]?key|access[_\-]?key|auth[_\-]?token|webhook[_\-]?secret|client[_\-]?secret|private[_\-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_\-+/=]{16,}/gi,severity:"high"}];function o_(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),s=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-s.length))}${s}`}function i_(e){let t=5381;for(let s=0;s<e.length;s++)t=(t<<5)+t+e.charCodeAt(s)|0;return(t>>>0).toString(36)}function _e(e){if(!e)return{redacted:e,count:0};let t=e,s=0,n=new Set;for(let r of r_)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${i_(o)}`;return n.has(a)||(n.add(a),s+=1),`[REDACTED ${r.name}: ${o_(o)}]`});return{redacted:t,count:s}}he();var ur=1,qe="claude-haiku-4-5-20251001",c_=3,l_=32e3,ba=2e3,u_=30,d_=30,p_=30,m_=30;function g_(e){let s=f().prepare(`SELECT s.id,
918
+ NULLIF(sa.alias, '') AS alias,
919
+ s.auto_title,
920
+ s.auto_title_source,
921
+ s.title_quality,
922
+ s.message_count,
923
+ s.first_user_message,
924
+ p.name AS project
925
+ FROM sessions s
926
+ LEFT JOIN session_aliases sa ON sa.session_id = s.id
927
+ LEFT JOIN projects p ON p.id = s.project_id
928
+ WHERE s.id = ?`).get(e);return s?{...s,alias_source:s.alias?"manual":null}:null}function Sa(e,t={}){if(e.message_count<c_)return{eligible:!1,reason:"too-short"};let s=e.title_quality;if(s==="programmatic"||s==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let n=$e(e.id);if(n&&n.extractor_version>=ur)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function __(e){let t=g_(e);if(!t)return null;let n=f().prepare(`SELECT role, content_text
929
+ FROM messages
930
+ WHERE session_id = ?
931
+ AND is_sidechain = 0
932
+ AND content_text IS NOT NULL
933
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of n){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>ba?u.slice(0,ba)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>l_)break;r.push(m),o+=m.length}return{meta:t,excerpt:r.join(`
934
+
935
+ `)}}function f_(e){let{meta:t}=e;return["You are extracting a structured Output Index from a Claude Code session for a local knowledge graph.","","Read the transcript and produce a single JSON object with EXACTLY these fields. Output JSON only \u2014 no Markdown fences, no commentary, no explanation.","","Fields (every field MUST appear; use [] for empty):","- files_written: array of strings. Relative or absolute file paths the assistant created, edited, or extensively discussed (Write/Edit tool calls or paths quoted verbatim).",'- brands_mentioned: array of strings. Distinct proper-noun company / product / brand names mentioned. Examples of valid: "Glaser Group", "Apollo", "TikTok", "Cloudflare R2". NOT generic terms like "the company" or "users".','- terms_introduced: array of objects { "term": "<lowercase phrase>", "freq": <int> }. Distinctive multi-word noun phrases the session introduced or extensively discussed. Skip generic words. Cap at 30 entries.','- plan_ids_referenced: array of strings. Planning identifiers like "v0.18.A", "Phase D", "L3", "MASTER-PLAN-cognitive-graph". Empty array if none.','- bug_signatures: array of objects { "error_type": "<class or null>", "snippet": "<first line of the error>", "file": "<path or null>" }. Errors actually encountered in the session (not hypothetical). The error_type is the exception class (e.g. "TypeError", "ReferenceError") or null if unspecified.',"","Hard constraints:","- Do NOT fabricate. If a field has no concrete content from the transcript, output an empty array.","- Output JSON ONLY. No backticks, no markdown, no preamble.","- terms_introduced \u2264 30 entries; brands_mentioned \u2264 30 distinct entries; plan_ids_referenced \u2264 30; bug_signatures \u2264 30.","",`Session: ${t.alias??t.id.slice(0,8)}`,`Project: ${t.project??"unknown"}`,t.first_user_message?`Opening prompt: ${t.first_user_message.slice(0,500)}`:"","","Transcript excerpt (may be truncated):","---",e.excerpt,"---"].filter(Boolean).join(`
936
+ `)}function h_(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function cr(e,t,s=!1){if(!Array.isArray(e))return[];let n=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=s?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!n.has(a)&&(n.add(a),r.push(a),r.length>=t))break}return r}function E_(e,t){if(!Array.isArray(e))return[];let s=new Set,n=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,a=r.freq;if(typeof o!="string")continue;let c=o.trim().toLowerCase();if(!c||c.length>128||s.has(c))continue;s.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(n.push({term:c,frequency:u}),n.length>=t)break}return n}function b_(e,t){if(!Array.isArray(e))return[];let s=[];for(let n of e){if(!n||typeof n!="object")continue;let r=n.error_type,o=n.snippet,a=n.file,c=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",u=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!u)continue;let d=typeof a=="string"&&a.trim().length>0?a.trim().slice(0,256):null,m=a_("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(s.push({error_type:c,message_hash:m,snippet:u,file:d}),s.length>=t)break}return s}function S_(e){let t=e.trim();try{let b=JSON.parse(t);typeof b.result=="string"&&(t=b.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+1),o;try{o=JSON.parse(r)}catch{return null}let a=cr(o.files_written,200),c=cr(o.brands_mentioned,d_),u=E_(o.terms_introduced,u_),d=cr(o.plan_ids_referenced,p_),m=b_(o.bug_signatures,m_);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&m.length===0&&!h_(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:m}}var lr=null;async function T_(e,t){return lr?lr(e,t):He(e,[],{model:t})}async function dr(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let s=__(e);if(!s)return{session_id:e,ok:!1,skipped:"session-not-found"};let n=Sa(s.meta,{force:t.force});if(!n.eligible)return{session_id:e,ok:!1,skipped:n.reason};if(!lr&&!le())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=f_(s),o=t.model??qe,a=await T_(r,o);if(!a.success){let m=(a.stderr||a.stdout||"").slice(0,400),h=m?_e(m).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:h}}let c=S_(a.stdout);if(!c){let m=a.stdout.slice(0,400),h=m?_e(m).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:h}}let u=y_(a.stdout),d=Qi({session_id:e,files_written:c.files_written,brands_mentioned:c.brands_mentioned,terms_introduced:c.terms_introduced,plan_ids_referenced:c.plan_ids_referenced,bug_signatures:c.bug_signatures,raw_extraction:{model:o,usage:u,raw_response_excerpt:a.stdout.slice(0,4e3)},extractor_version:ur});return{session_id:e,ok:!0,index:d,usage:u}}function y_(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let s=t.usage,n={};return typeof s.input_tokens=="number"&&(n.input_tokens=s.input_tokens),typeof s.output_tokens=="number"&&(n.output_tokens=s.output_tokens),n}}catch{}return null}function tt(e={}){let t=f(),s=[],n=[];typeof e.projectId=="number"&&(s.push("s.project_id = ?"),n.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=s.length?`WHERE ${s.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
937
+ NULLIF(sa.alias, '') AS alias,
938
+ s.auto_title,
939
+ s.auto_title_source,
940
+ s.title_quality,
941
+ s.message_count,
942
+ s.first_user_message,
943
+ p.name AS project
944
+ FROM sessions s
945
+ LEFT JOIN session_aliases sa ON sa.session_id = s.id
946
+ LEFT JOIN projects p ON p.id = s.project_id
947
+ ${o}
948
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(...n),c=[],u=new Map;for(let d of a){let m={...d,alias_source:d.alias?"manual":null},h=Sa(m,{force:e.force});if(!h.eligible){let b=h.reason??"session-not-found";u.set(b,(u.get(b)??0)+1);continue}if(c.push(m),c.length>=r)break}return{eligible:c,skipped:u}}async function Ta(e={}){let t=tt({projectId:e.projectId,limit:e.limit,force:e.force}),s={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())s.skipped+=r;e.onProgress?.({...s});let n=[];for(let r of t.eligible){if(e.signal?.aborted)break;s.current_session_id=r.id,e.onProgress?.({...s});let o=await dr(r.id,{model:e.model,force:e.force,signal:e.signal});n.push(o),e.onResult?.(o),o.ok?s.ok+=1:o.skipped?s.skipped+=1:s.failed+=1,o.usage?.input_tokens&&(s.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(s.total_output_tokens+=o.usage.output_tokens),s.processed+=1,e.onProgress?.({...s})}return s.current_session_id=null,e.onProgress?.({...s}),{progress:s,results:n}}he();var te="[daemon:inference]",hr=!1,Er=!1,br=!1,Sr=!1,Tr=!1,Ia=0,yr=!1,wr=!1,Cs=3,Xe=0,st=!1,vs=null,nt=null,ja=!1;function P_(){return f().prepare("SELECT id, name FROM projects").all()}async function Ma(){if(hr)return;hr=!0;let e=Date.now();try{let s=(await Ui({minClusterSize:2})).progress;(s.clusters_created||s.clusters_merged||s.members_added)&&console.log(`${te} bug-patterns: created=${s.clusters_created} merged=${s.clusters_merged} members_added=${s.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${te} bug-patterns failed:`,t)}finally{hr=!1}}async function Da(){if(Er)return;Er=!0;let e=Date.now();try{let t=0,s=0;for(let n of P_())try{let r=await ea({projectId:n.id});t+=r.progress.suggestions_created,s+=1}catch(r){console.error(`${te} citations failed for project "${n.name}":`,r)}t>0&&console.log(`${te} citations: ${t} suggestion(s) across ${s} project(s) (${Date.now()-e}ms)`)}catch(t){console.error(`${te} citations failed:`,t)}finally{Er=!1}}function Fa(){if(br)return;br=!0;let e=Date.now();try{let t=na({}),s=ra({});t.created+s.created>0&&console.log(`${te} l1: uuid=${t.created} plan=${s.created} (${Date.now()-e}ms)`)}catch(t){console.error(`${te} l1 failed:`,t)}finally{br=!1}}async function Pa(){if(Sr)return;let e=ae();if(!e.enabled||e.backfillPaused)return;Sr=!0;let t=Date.now();try{let s=await ks({limit:25});s.processed>0&&console.log(`${te} backfill: processed=${s.processed} ok=${s.ok} failed=${s.failed} (${Date.now()-t}ms)`)}catch(s){console.error(`${te} backfill failed:`,s)}finally{Sr=!1}}async function Ua(){if(Tr)return;let e=ae();if(e.autoExtractEnabled&&!ja&&st&&(U_(),console.log(`${te} auto-extract: circuit breaker reset (config toggled on).`)),ja=e.autoExtractEnabled,!e.autoExtractEnabled||st)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-Ia<t)return;if(!le()){yr||(console.log(`${te} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),yr=!0);return}if(yr=!1,!await _i().catch(()=>!1)){wr||(console.log(`${te} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),wr=!0);return}wr=!1,Tr=!0,Ia=Date.now();let n=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=tt({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,m=new Map,h=null;for(let S of o){let w=await dr(S.id,{model:qe});if(w.ok)a+=1;else if(!w.skipped){c+=1;let R=w.failed??"unknown";m.set(R,(m.get(R)??0)+1),!h&&w.stderr_excerpt&&(h=w.stderr_excerpt)}w.usage?.input_tokens&&(u+=w.usage.input_tokens),w.usage?.output_tokens&&(d+=w.usage.output_tokens)}let b=m.size>0?" reasons="+Array.from(m.entries()).map(([S,w])=>`${S}:${w}`).join(","):"";if(console.log(`${te} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-n}ms)${b}`),h){let w=_e(h).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${te} auto-extract: first failure excerpt: ${w}`)}o.length>0&&u===0&&d===0?(Xe+=1,Xe>=Cs&&(st=!0,vs=Date.now(),nt=`${Cs} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${te} auto-extract: CIRCUIT BREAKER tripped \u2014 ${nt}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(Xe=0)}catch(r){console.error(`${te} auto-extract failed:`,r),Xe+=1,Xe>=Cs&&(st=!0,vs=Date.now(),nt=`${Cs} consecutive thrown errors`,console.log(`${te} auto-extract: CIRCUIT BREAKER tripped \u2014 ${nt}. Pausing nibbler.`))}finally{Tr=!1}}function Ba(){return{broken:st,brokenAt:vs,reason:nt,consecutiveZeroTokenRuns:Xe}}function U_(){st=!1,vs=null,nt=null,Xe=0}var Rr=!1;async function $a(){if(Rr)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(gr(),xa)),s=e();if(!s.autoArchiveEnabled)return;let n=1380*60*1e3;if(s.lastRunAt&&Date.now()-Date.parse(s.lastRunAt)<n)return;Rr=!0;let r=Date.now();try{let a=new Date(Date.now()-s.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(va(),Ca)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${te} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${te} auto-archive failed: ${a}`)}finally{Rr=!1}}function Ha(){let m=[],h=[];m.push(setTimeout(()=>{Ma()},9e4)),h.push(setInterval(()=>{Ma()},18e5)),m.push(setTimeout(()=>{Da()},18e4)),h.push(setInterval(()=>{Da()},36e5)),m.push(setTimeout(()=>Fa(),12e4)),h.push(setInterval(()=>Fa(),18e5)),m.push(setTimeout(()=>{Pa()},24e4)),h.push(setInterval(()=>{Pa()},9e5)),m.push(setTimeout(()=>{Ua()},3e5)),h.push(setInterval(()=>{Ua()},9e5));let b=3600*1e3,T=300*1e3;return m.push(setTimeout(()=>{$a()},T)),h.push(setInterval(()=>{$a()},b)),console.log(`${te} scheduled: bug-patterns (30m), citations (60m), l1 (30m), backfill (15m, when enabled), auto-extract (60m, when enabled), auto-archive (24h, when enabled)`),{startupTimers:m,intervalTimers:h,stop:()=>{for(let S of m)clearTimeout(S);for(let S of h)clearInterval(S)}}}H();import{serveStatic as op}from"@hono/node-server/serve-static";import{existsSync as qT,readFileSync as Yo}from"node:fs";import{stat as XT,readFile as JT,realpath as YT}from"node:fs/promises";import{timingSafeEqual as GT}from"node:crypto";import{homedir as zT}from"node:os";import{dirname as Ko,join as Mn}from"node:path";import{fileURLToPath as Vo}from"node:url";import{existsSync as RR,readFileSync as AR,statSync as kR,statfsSync as $_}from"node:fs";Ls();H();Q();var B_=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],LR=new RegExp(`^(${B_.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var CR=5*6e4;function Ar(){try{let e=$_(U);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function Wa(e){let{projects:t,sessions:s,messages:n,port:r,version:o}=e;return`<!DOCTYPE html>
647
949
  <html lang="en">
648
950
  <head>
649
951
  <meta charset="utf-8" />
@@ -698,71 +1000,71 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
698
1000
  </footer>
699
1001
  </main>
700
1002
  </body>
701
- </html>`}var Sp=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,Tp=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,yp=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function wp(e){return e.replace(Sp,"").trim()}function Rp(e){let t=e.replace(Tp,"[tool call]");return t=t.replace(yp,"[tool result]"),t=t.replace(/_\(unknown block: thinking\)_/g,""),t=t.replace(/(?:\[tool call\]|\[tool result\])(?:\s*(?:\[tool call\]|\[tool result\]))+/g,"[tool activity]"),t=t.replace(/\n{3,}/g,`
1003
+ </html>`}var H_=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,W_=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,q_=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function X_(e){return e.replace(H_,"").trim()}function J_(e){let t=e.replace(W_,"[tool call]");return t=t.replace(q_,"[tool result]"),t=t.replace(/_\(unknown block: thinking\)_/g,""),t=t.replace(/(?:\[tool call\]|\[tool result\])(?:\s*(?:\[tool call\]|\[tool result\]))+/g,"[tool activity]"),t=t.replace(/\n{3,}/g,`
702
1004
 
703
- `),t.trim()}function kp(e){return e.role??e.type??"message"}function si(e,t,s={}){let n=s.mode??"condensed",r=s.includeSidechain===!0,o=s.since?Date.parse(s.since):0,a=t.filter(g=>!(!r&&g.is_sidechain===1||o&&g.timestamp&&Date.parse(g.timestamp)<o)),c=[];s.prelude&&(c.push(s.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${n})`),c.push(""),c.push(`- **Project**: ${e.project_name}`),e.decoded_path&&c.push(`- **Path**: \`${e.decoded_path}\``),c.push(`- **Session ID**: \`${e.id}\``),e.started_at&&c.push(`- **Started**: ${e.started_at}`),e.ended_at&&c.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&c.push(`- **Branch**: \`${e.git_branch}\``),c.push(`- **Messages**: ${a.length}`),c.push(""),c.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),c.push(""),c.push("---"),c.push("");let d=0,u=0;for(let g of a){let h=g.content_text??"",b=wp(h);n==="condensed"&&(b=Rp(b));let S=b.length>0,y=!!g.tool_names&&g.tool_names.length>0;if(!S&&!y){u+=1;continue}let k=kp(g),w=g.timestamp?` \`${g.timestamp}\``:"";c.push(`## ${k}${w}`),c.push(""),y&&n==="condensed"&&(c.push(`_tools used: ${g.tool_names}_`),c.push("")),S&&(c.push(b),c.push("")),d+=1}return c.push("---"),c.push(""),c.push(`_${d} messages included_`+(u?`, ${u} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),c.join(`
704
- `)}H();Z();import{writeFileSync as Ap}from"node:fs";import{join as xp}from"node:path";var Np=xp($,"aliases.json");function Nn(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Te(e){return f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function Op(){return f().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:Nn(t.previous_aliases)}))}function me(e,t){let s=t.trim();if(!s)throw new Error("alias must be non-empty");let n=f(),r=new Date().toISOString(),o=n.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=Nn(o.previous_aliases),o.alias!==s&&a.push({alias:o.alias,replaced_at:r})),n.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
1005
+ `),t.trim()}function Y_(e){return e.role??e.type??"message"}function qa(e,t,s={}){let n=s.mode??"condensed",r=s.includeSidechain===!0,o=s.since?Date.parse(s.since):0,a=t.filter(m=>!(!r&&m.is_sidechain===1||o&&m.timestamp&&Date.parse(m.timestamp)<o)),c=[];s.prelude&&(c.push(s.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${n})`),c.push(""),c.push(`- **Project**: ${e.project_name}`),e.decoded_path&&c.push(`- **Path**: \`${e.decoded_path}\``),c.push(`- **Session ID**: \`${e.id}\``),e.started_at&&c.push(`- **Started**: ${e.started_at}`),e.ended_at&&c.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&c.push(`- **Branch**: \`${e.git_branch}\``),c.push(`- **Messages**: ${a.length}`),c.push(""),c.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),c.push(""),c.push("---"),c.push("");let u=0,d=0;for(let m of a){let h=m.content_text??"",b=X_(h);n==="condensed"&&(b=J_(b));let T=b.length>0,S=!!m.tool_names&&m.tool_names.length>0;if(!T&&!S){d+=1;continue}let w=Y_(m),R=m.timestamp?` \`${m.timestamp}\``:"";c.push(`## ${w}${R}`),c.push(""),S&&n==="condensed"&&(c.push(`_tools used: ${m.tool_names}_`),c.push("")),T&&(c.push(b),c.push("")),u+=1}return c.push("---"),c.push(""),c.push(`_${u} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),c.join(`
1006
+ `)}H();Q();import{writeFileSync as G_}from"node:fs";import{join as z_}from"node:path";var K_=z_(U,"aliases.json");function kr(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Ae(e){return f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function V_(){return f().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:kr(t.previous_aliases)}))}function be(e,t){let s=t.trim();if(!s)throw new Error("alias must be non-empty");let n=f(),r=new Date().toISOString(),o=n.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=kr(o.previous_aliases),o.alias!==s&&a.push({alias:o.alias,replaced_at:r})),n.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
705
1007
  VALUES (?, ?, ?, ?)
706
1008
  ON CONFLICT(session_id) DO UPDATE SET
707
1009
  alias = excluded.alias,
708
1010
  updated_at = excluded.updated_at,
709
- previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(a)),ni(),{session_id:e,alias:s,updated_at:r,previous_aliases:a}}function ns(e){let t=f(),s=new Date().toISOString(),n=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!n)return;let r=Nn(n.previous_aliases);r.push({alias:n.alias,replaced_at:s}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
710
- WHERE session_id = ?`).run(s,JSON.stringify(r),e),ni()}function ni(){try{J();let e=Op(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};Ap(Np,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}function yt(e){if(!e.sessionStartedAt)return{allowed:!1,reason:"missing-session-started-at"};if(!e.terminalOpenedAt)return{allowed:!1,reason:"missing-terminal-opened-at"};let t=Date.parse(e.sessionStartedAt),s=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(s)?s-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}H();Z();import{writeFileSync as em,mkdirSync as tm,existsSync as sm}from"node:fs";import{join as ui}from"node:path";var jn=ui($,"notes"),di=200,nm=12e3,rm=800,om=8e3;function pi(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function mi(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(s=>!!s&&typeof s=="object"&&typeof s.synopsis=="string"&&typeof s.replaced_at=="string"):[]}catch{return[]}}function im(){J(),sm(jn)||tm(jn,{recursive:!0})}function am(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:pi(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:mi(e.auto_synopsis_history)}}var cm="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function is(e){let t=f().prepare(`SELECT ${cm} FROM session_notes WHERE session_id = ?`).get(e);return t?am(t):null}function gi(e,t){let s=f(),n=new Date().toISOString(),r=s.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=pi(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:n})),s.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1011
+ previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(a)),Xa(),{session_id:e,alias:s,updated_at:r,previous_aliases:a}}function Is(e){let t=f(),s=new Date().toISOString(),n=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!n)return;let r=kr(n.previous_aliases);r.push({alias:n.alias,replaced_at:s}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
1012
+ WHERE session_id = ?`).run(s,JSON.stringify(r),e),Xa()}function Xa(){try{J();let e=V_(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};G_(K_,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}function $t(e){if(!e.sessionStartedAt)return{allowed:!1,reason:"missing-session-started-at"};if(!e.terminalOpenedAt)return{allowed:!1,reason:"missing-terminal-opened-at"};let t=Date.parse(e.sessionStartedAt),s=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(s)?s-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}H();Q();import{writeFileSync as Z_,mkdirSync as Q_,existsSync as ef}from"node:fs";import{join as Ya}from"node:path";var xr=Ya(U,"notes"),Ja=200,tf=12e3,sf=800,nf=8e3;function Ga(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function za(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(s=>!!s&&typeof s=="object"&&typeof s.synopsis=="string"&&typeof s.replaced_at=="string"):[]}catch{return[]}}function rf(){J(),ef(xr)||Q_(xr,{recursive:!0})}function of(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:Ga(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:za(e.auto_synopsis_history)}}var af="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function js(e){let t=f().prepare(`SELECT ${af} FROM session_notes WHERE session_id = ?`).get(e);return t?of(t):null}function Ka(e,t){let s=f(),n=new Date().toISOString(),r=s.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=Ga(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:n})),s.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
711
1013
  VALUES (?, ?, ?, ?)
712
1014
  ON CONFLICT(session_id) DO UPDATE SET
713
1015
  content = excluded.content,
714
1016
  updated_at = excluded.updated_at,
715
- previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(o)),dm(e,t,n),is(e)??{session_id:e,content:t,updated_at:n,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function _i(e){let s=f().prepare(`SELECT rowid AS rid, role, content_text
1017
+ previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(o)),lf(e,t,n),js(e)??{session_id:e,content:t,updated_at:n,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function Va(e){let s=f().prepare(`SELECT rowid AS rid, role, content_text
716
1018
  FROM messages
717
1019
  WHERE session_id = ? AND is_sidechain = 0
718
1020
  AND content_text IS NOT NULL AND content_text != ''
719
1021
  AND role IN ('user', 'assistant')
720
1022
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
721
- LIMIT ?`).all(e,di);if(s.length===0)throw new Error("no messages available to summarise");let n=nm,r=[];for(let b of s){if(n<=0)break;let S=(b.content_text??"").slice(0,rm);r.push({rid:b.rid,role:b.role,content:S}),n-=S.length}r.reverse();let o=s.length===di||n<=0,a=r.map(b=>`**${b.role}**: ${b.content}`).join(`
1023
+ LIMIT ?`).all(e,Ja);if(s.length===0)throw new Error("no messages available to summarise");let n=tf,r=[];for(let b of s){if(n<=0)break;let T=(b.content_text??"").slice(0,sf);r.push({rid:b.rid,role:b.role,content:T}),n-=T.length}r.reverse();let o=s.length===Ja||n<=0,a=r.map(b=>`**${b.role}**: ${b.content}`).join(`
722
1024
 
723
1025
  `),c=["You will receive a sampled chronological transcript from a Claude Code session.",o?"The sample is the most recent slice that fits in the context budget; older messages were dropped.":"The full session is included.","","Write a clean markdown synopsis of the session. Use these sections \u2014 omit any that genuinely have nothing to say:","","## Goal","One sentence \u2014 what the user was trying to accomplish.","","## What was done","Bullet list \u2014 concrete actions, code changes, decisions. Skip pleasantries.","","## Key decisions","Bullet list \u2014 non-obvious choices and the reason behind them.","","## Files touched","Bullet list \u2014 file paths mentioned in the conversation. Omit the section if none.","","## Open follow-ups","Bullet list \u2014 anything left undone or flagged for later. Omit the section if none.","","Output ONLY the markdown \u2014 no surrounding prose, no code fences around the whole thing, no closing summary.","","---","",a].join(`
724
- `),{spawnClaudePrompt:d,isClaudeCliAvailable:u}=await Promise.resolve().then(()=>(ge(),Pe));if(!u())throw new Error("claude CLI not found on PATH");let g=await d(c,[],{});if(!g.success)throw new Error(`claude CLI exited ${g.exitCode}: ${g.stderr.slice(-500)}`);let h=lm(g.stdout);if(!h)throw new Error("claude CLI returned an empty synopsis");return h.slice(0,om)}function lm(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return n.trim()}}catch{}return t}function fi(e,t){let s=f(),n=new Date().toISOString(),r=Date.now(),o=s.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=mi(o?.auto_synopsis_history??null);return o?.auto_synopsis&&o.auto_synopsis!==t&&o.auto_synopsis.length>0&&a.push({synopsis:o.auto_synopsis,replaced_at:n}),o?s.prepare(`UPDATE session_notes
1026
+ `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(he(),We));if(!d())throw new Error("claude CLI not found on PATH");let m=await u(c,[],{});if(!m.success)throw new Error(`claude CLI exited ${m.exitCode}: ${m.stderr.slice(-500)}`);let h=cf(m.stdout);if(!h)throw new Error("claude CLI returned an empty synopsis");return h.slice(0,nf)}function cf(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return n.trim()}}catch{}return t}function Za(e,t){let s=f(),n=new Date().toISOString(),r=Date.now(),o=s.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=za(o?.auto_synopsis_history??null);return o?.auto_synopsis&&o.auto_synopsis!==t&&o.auto_synopsis.length>0&&a.push({synopsis:o.auto_synopsis,replaced_at:n}),o?s.prepare(`UPDATE session_notes
725
1027
  SET auto_synopsis = ?,
726
1028
  auto_synopsis_generated_at = ?,
727
1029
  auto_synopsis_history = ?
728
1030
  WHERE session_id = ?`).run(t,r,JSON.stringify(a),e):s.prepare(`INSERT INTO session_notes
729
1031
  (session_id, content, updated_at, previous_versions, auto_synopsis,
730
1032
  auto_synopsis_generated_at, auto_synopsis_history)
731
- VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,n,t,r,JSON.stringify(a)),is(e)}function dm(e,t,s){try{im();let n=ui(jn,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
732
- `;em(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}Ge();H();Z();import{randomUUID as um}from"node:crypto";import{writeFileSync as pm,readFileSync as pw,existsSync as mw}from"node:fs";import{join as mm}from"node:path";var gm=mm($,"collections.json"),as=8;function cs(e){return{...e}}function _e(e,t,s,n=null,r=new Date().toISOString()){f().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
733
- VALUES (?, ?, ?, ?, ?)`).run(e,n,t,s?JSON.stringify(s):null,r)}function ls(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function hi(e){if(!e)return 0;let t=0,s=e,n=new Set,r=f();for(;s;){if(n.has(s))throw new Error("collection cycle detected");n.add(s);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)break;t+=1,s=o.parent_id}return t}function _m(e,t){let s=f(),n=e,r=new Set;for(;n;){if(r.has(n))return!1;if(r.add(n),n===t)return!0;let o=s.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)return!1;n=o.parent_id}return!1}function Ei(e=!1){return f().prepare(`SELECT c.*,
1033
+ VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,n,t,r,JSON.stringify(a)),js(e)}function lf(e,t,s){try{rf();let n=Ya(xr,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
1034
+ `;Z_(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}Qe();H();Q();import{randomUUID as uf}from"node:crypto";import{writeFileSync as df,readFileSync as GR,existsSync as zR}from"node:fs";import{join as pf}from"node:path";var mf=pf(U,"collections.json"),Ms=8;function Ds(e){return{...e}}function Se(e,t,s,n=null,r=new Date().toISOString()){f().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1035
+ VALUES (?, ?, ?, ?, ?)`).run(e,n,t,s?JSON.stringify(s):null,r)}function Fs(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function Qa(e){if(!e)return 0;let t=0,s=e,n=new Set,r=f();for(;s;){if(n.has(s))throw new Error("collection cycle detected");n.add(s);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)break;t+=1,s=o.parent_id}return t}function gf(e,t){let s=f(),n=e,r=new Set;for(;n;){if(r.has(n))return!1;if(r.add(n),n===t)return!0;let o=s.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)return!1;n=o.parent_id}return!1}function ec(e=!1){return f().prepare(`SELECT c.*,
734
1036
  (SELECT COUNT(*) FROM collection_sessions cs WHERE cs.collection_id = c.id) AS session_count
735
1037
  FROM collections c
736
1038
  ${e?"":"WHERE c.archived_at IS NULL"}
737
- ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(n=>({...cs(n),session_count:n.session_count}))}function Ne(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?cs(t):null}function bi(e,t=!0){let s=f(),n=t?Mn(e):[e];if(n.length===0)return[];let r=n.map(()=>"?").join(",");return s.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1039
+ ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(n=>({...Ds(n),session_count:n.session_count}))}function Ie(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Ds(t):null}function tc(e,t=!0){let s=f(),n=t?Nr(e):[e];if(n.length===0)return[];let r=n.map(()=>"?").join(",");return s.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
738
1040
  FROM collection_sessions
739
1041
  WHERE collection_id IN (${r})
740
- ORDER BY added_at DESC`).all(...n)}function Mn(e){let t=f(),s=[e],n=[e],r=new Set([e]);for(;n.length>0;){let o=n.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...n),c=[];for(let d of a)r.has(d.id)||(r.add(d.id),s.push(d.id),c.push(d.id));n=c}return s}function Si(e){return f().prepare(`SELECT c.* FROM collections c
1042
+ ORDER BY added_at DESC`).all(...n)}function Nr(e){let t=f(),s=[e],n=[e],r=new Set([e]);for(;n.length>0;){let o=n.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...n),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),s.push(u.id),c.push(u.id));n=c}return s}function sc(e){return f().prepare(`SELECT c.* FROM collections c
741
1043
  JOIN collection_sessions cs ON cs.collection_id = c.id
742
1044
  WHERE cs.session_id = ? AND c.archived_at IS NULL
743
- ORDER BY LOWER(c.name)`).all(e)}function At(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let s=f(),n=new Date().toISOString(),r=um();if(e.parent_id){if(!Ne(e.parent_id))throw new Error("parent collection not found");if(hi(e.parent_id)>=as-1)throw new Error(`max collection depth is ${as}`)}return s.transaction(()=>{s.prepare(`INSERT INTO collections
1045
+ ORDER BY LOWER(c.name)`).all(e)}function Bt(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let s=f(),n=new Date().toISOString(),r=uf();if(e.parent_id){if(!Ie(e.parent_id))throw new Error("parent collection not found");if(Qa(e.parent_id)>=Ms-1)throw new Error(`max collection depth is ${Ms}`)}return s.transaction(()=>{s.prepare(`INSERT INTO collections
744
1046
  (id, name, description, icon, color, parent_id, sort_key, created_at, updated_at, archived_at)
745
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",n,n),_e(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),Ue(),Ne(r)}function Ti(e,t){let s=f(),n=ls(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():n.name,description:t.description!==void 0?t.description:n.description,icon:t.icon!==void 0?t.icon:n.icon,color:t.color!==void 0?t.color:n.color,parent_id:t.parent_id!==void 0?t.parent_id:n.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:n.sort_key};if(!o.name)throw new Error("name required");if(o.name.length>120)throw new Error("name too long (max 120 chars)");if(t.parent_id!==void 0&&t.parent_id!==n.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Ne(t.parent_id))throw new Error("parent collection not found");if(_m(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(hi(t.parent_id)>=as-1)throw new Error(`max collection depth is ${as}`)}return s.transaction(()=>{s.prepare(`UPDATE collections
1047
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",n,n),Se(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),Je(),Ie(r)}function nc(e,t){let s=f(),n=Fs(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():n.name,description:t.description!==void 0?t.description:n.description,icon:t.icon!==void 0?t.icon:n.icon,color:t.color!==void 0?t.color:n.color,parent_id:t.parent_id!==void 0?t.parent_id:n.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:n.sort_key};if(!o.name)throw new Error("name required");if(o.name.length>120)throw new Error("name too long (max 120 chars)");if(t.parent_id!==void 0&&t.parent_id!==n.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Ie(t.parent_id))throw new Error("parent collection not found");if(gf(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(Qa(t.parent_id)>=Ms-1)throw new Error(`max collection depth is ${Ms}`)}return s.transaction(()=>{s.prepare(`UPDATE collections
746
1048
  SET name = ?, description = ?, icon = ?, color = ?,
747
1049
  parent_id = ?, sort_key = ?, updated_at = ?
748
- WHERE id = ?`).run(o.name,o.description,o.icon,o.color,o.parent_id,o.sort_key,r,e),t.name!==void 0&&t.name!==n.name&&_e(e,"rename",{from:n.name,to:o.name},null,r),t.description!==void 0&&t.description!==n.description&&_e(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==n.icon||t.color!==void 0&&t.color!==n.color)&&_e(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==n.parent_id&&_e(e,"move",{from:n.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==n.sort_key&&_e(e,"reorder",{from:n.sort_key,to:o.sort_key},null,r)})(),Ue(),Ne(e)}function yi(e){let t=f(),s=ls(e);if(s.archived_at)return cs(s);let n=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(n,n,e),_e(e,"archive",{name:s.name},null,n)})(),Ue(),Ne(e)}function wi(e){let t=f(),s=ls(e);if(!s.archived_at)return cs(s);let n=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(n,e),_e(e,"restore",{name:s.name},null,n)})(),Ue(),Ne(e)}function xt(e,t,s=null,n={}){let r=f();if(ls(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let c=n.source??"manual",d=n.rule_id??null;if(c==="auto"&&!d)throw new Error("auto membership requires a rule_id");let u=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
749
- VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,u,s,c,d),_e(e,"add",{note:s,source:c,rule_id:d},t,u)})(),Ue(),{added:!0}}function Ri(e,t){let s=f();if(!s.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return s.transaction(()=>{s.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),_e(e,"remove",null,t,r)})(),Ue(),{removed:!0}}function ds(e){let t=f(),s=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
750
- WHERE rule_id = ?`).all(e);if(s.length===0)return{removed:0};let n=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of s)_e(o.collection_id,"remove",{rule_id:e},o.session_id,n)})(),Ue(),{removed:s.length}}function fm(){return f().prepare(`SELECT id, collection_id, session_id, action, payload, at
1050
+ WHERE id = ?`).run(o.name,o.description,o.icon,o.color,o.parent_id,o.sort_key,r,e),t.name!==void 0&&t.name!==n.name&&Se(e,"rename",{from:n.name,to:o.name},null,r),t.description!==void 0&&t.description!==n.description&&Se(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==n.icon||t.color!==void 0&&t.color!==n.color)&&Se(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==n.parent_id&&Se(e,"move",{from:n.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==n.sort_key&&Se(e,"reorder",{from:n.sort_key,to:o.sort_key},null,r)})(),Je(),Ie(e)}function rc(e){let t=f(),s=Fs(e);if(s.archived_at)return Ds(s);let n=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(n,n,e),Se(e,"archive",{name:s.name},null,n)})(),Je(),Ie(e)}function oc(e){let t=f(),s=Fs(e);if(!s.archived_at)return Ds(s);let n=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(n,e),Se(e,"restore",{name:s.name},null,n)})(),Je(),Ie(e)}function Ht(e,t,s=null,n={}){let r=f();if(Fs(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let c=n.source??"manual",u=n.rule_id??null;if(c==="auto"&&!u)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
1051
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,s,c,u),Se(e,"add",{note:s,source:c,rule_id:u},t,d)})(),Je(),{added:!0}}function ic(e,t){let s=f();if(!s.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return s.transaction(()=>{s.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),Se(e,"remove",null,t,r)})(),Je(),{removed:!0}}function Ps(e){let t=f(),s=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1052
+ WHERE rule_id = ?`).all(e);if(s.length===0)return{removed:0};let n=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of s)Se(o.collection_id,"remove",{rule_id:e},o.session_id,n)})(),Je(),{removed:s.length}}function _f(){return f().prepare(`SELECT id, collection_id, session_id, action, payload, at
751
1053
  FROM collection_events
752
- ORDER BY at ASC, id ASC`).all()}function Ue(){try{J();let e=f(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
1054
+ ORDER BY at ASC, id ASC`).all()}function Je(){try{J();let e=f(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
753
1055
  created_at, updated_at, archived_at
754
1056
  FROM collections
755
1057
  ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),s=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
756
1058
  FROM collection_sessions
757
- ORDER BY collection_id, added_at`).all(),n=fm(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};pm(gm,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}H();Z();import{randomUUID as Pn}from"node:crypto";import{existsSync as hm,mkdirSync as Em,writeFileSync as ki}from"node:fs";import{homedir as bm}from"node:os";import{basename as Sm,join as Un}from"node:path";var us=Un($,"auto-rules"),Tm=Un(us,"rules.json"),ym=Un(us,"suggestions.json"),Dn="Repositories",wm="Topics",Ai=3;var Rm=5,km=2,Am=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function $n(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 xm(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 Nm(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return Dn;case"tag":return wm;case"plan-file":return null}}function Om(e){let s=f().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(s)return s.id;let o=At({name:e,icon:e===Dn?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===Dn?"0000-repos":"0001-topics"});return f().prepare(`INSERT INTO auto_collection_rules
1059
+ ORDER BY collection_id, added_at`).all(),n=_f(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};df(mf,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}H();Q();import{randomUUID as Cr}from"node:crypto";import{existsSync as ff,mkdirSync as hf,writeFileSync as ac}from"node:fs";import{homedir as Ef}from"node:os";import{basename as bf,join as vr}from"node:path";var Us=vr(U,"auto-rules"),Sf=vr(Us,"rules.json"),Tf=vr(Us,"suggestions.json"),Or="Repositories",yf="Topics",cc=3;var wf=5,Rf=2,Af=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function Ir(e){return{id:e.id,name:e.name,type:e.type,pattern:e.pattern,collection_id:e.collection_id,priority:e.priority,enabled:e.enabled!==0,created_at:e.created_at,created_by:e.created_by}}function kf(e){return{id:e.id,type:e.type,pattern:e.pattern,suggested_name:e.suggested_name,suggested_parent_collection_id:e.suggested_parent_collection_id,session_count:e.session_count,detected_at:e.detected_at,dismissed:e.dismissed!==0}}function xf(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return Or;case"tag":return yf;case"plan-file":return null}}function Nf(e){let s=f().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(s)return s.id;let o=Bt({name:e,icon:e===Or?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===Or?"0000-repos":"0001-topics"});return f().prepare(`INSERT INTO auto_collection_rules
758
1060
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
759
- VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(Pn(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function Lm(e,t,s){let n;if(s!==void 0)n=s;else{let o=Nm(t);n=o?Om(o):null}return At({name:e,parent_id:n}).id}function ps(e){let t=f().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?$n(t):null}function Cm(e){let t=f();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(ms(e.pattern)+"%").map(n=>n.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(ms(e.pattern)+"%").map(n=>n.id);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(s).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(n=>n.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
760
- WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(n=>n.id)}}function xi(e,t,s=3){let n=f(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1061
+ VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(Cr(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function Of(e,t,s){let n;if(s!==void 0)n=s;else{let o=xf(t);n=o?Nf(o):null}return Bt({name:e,parent_id:n}).id}function $s(e){let t=f().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?Ir(t):null}function Lf(e){let t=f();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(Bs(e.pattern)+"%").map(n=>n.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(Bs(e.pattern)+"%").map(n=>n.id);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(s).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(n=>n.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1062
+ WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(n=>n.id)}}function lc(e,t,s=3){let n=f(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
761
1063
  sa.alias AS alias, s.auto_title AS auto_title, s.first_user_message AS first_user_message`,o="LEFT JOIN session_aliases sa ON sa.session_id = s.id",a=[];switch(e){case"cwd-prefix":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
762
1064
  WHERE s.cwd IS NOT NULL AND s.cwd LIKE ? ESCAPE '\\'
763
- ORDER BY s.started_at DESC LIMIT ?`).all(ms(t)+"%",s);break;case"git-branch-prefix":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
1065
+ ORDER BY s.started_at DESC LIMIT ?`).all(Bs(t)+"%",s);break;case"git-branch-prefix":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
764
1066
  WHERE s.git_branch IS NOT NULL AND s.git_branch LIKE ? ESCAPE '\\'
765
- ORDER BY s.started_at DESC LIMIT ?`).all(ms(t)+"%",s);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=n.prepare(`SELECT ${r} FROM sessions s ${o}
1067
+ ORDER BY s.started_at DESC LIMIT ?`).all(Bs(t)+"%",s);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=n.prepare(`SELECT ${r} FROM sessions s ${o}
766
1068
  WHERE s.project_id = ?
767
1069
  ORDER BY s.started_at DESC LIMIT ?`).all(c,s));break}case"tag":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
768
1070
  JOIN session_tags st ON st.session_id = s.id
@@ -770,19 +1072,19 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
770
1072
  ORDER BY s.started_at DESC LIMIT ?`).all(t,s);break;case"plan-file":a=n.prepare(`SELECT ${r} FROM sessions s ${o}
771
1073
  WHERE s.first_user_message IS NOT NULL
772
1074
  AND s.first_user_message LIKE ?
773
- ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",s);break}return a.map(c=>({id:c.id,title:Im(c),cwd:c.cwd,started_at:c.started_at}))}function Im(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return`session ${e.id.slice(0,8)}`;let s=t.split(`
774
- `)[0].trim();return s.length>80?s.slice(0,80)+"\u2026":s}function vm(e,t,s=f()){switch(e.type){case"cwd-prefix":return!!t.cwd&&t.cwd.startsWith(e.pattern);case"git-branch-prefix":return!!t.git_branch&&t.git_branch.startsWith(e.pattern);case"project-id":{let n=Number(e.pattern);return Number.isFinite(n)&&t.project_id===n}case"tag":return!!s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(t.id,e.pattern);case"plan-file":return!!t.first_user_message&&t.first_user_message.includes(e.pattern)}}function Ni(e){let t=f(),s=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!s)return{added:0};let n=t.prepare(`SELECT * FROM auto_collection_rules
1075
+ ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",s);break}return a.map(c=>({id:c.id,title:Cf(c),cwd:c.cwd,started_at:c.started_at}))}function Cf(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return`session ${e.id.slice(0,8)}`;let s=t.split(`
1076
+ `)[0].trim();return s.length>80?s.slice(0,80)+"\u2026":s}function vf(e,t,s=f()){switch(e.type){case"cwd-prefix":return!!t.cwd&&t.cwd.startsWith(e.pattern);case"git-branch-prefix":return!!t.git_branch&&t.git_branch.startsWith(e.pattern);case"project-id":{let n=Number(e.pattern);return Number.isFinite(n)&&t.project_id===n}case"tag":return!!s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(t.id,e.pattern);case"plan-file":return!!t.first_user_message&&t.first_user_message.includes(e.pattern)}}function uc(e){let t=f(),s=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!s)return{added:0};let n=t.prepare(`SELECT * FROM auto_collection_rules
775
1077
  WHERE enabled = 1 AND created_by != 'seed'
776
- ORDER BY priority, created_at`).all(),r=0;for(let o of n){let a=$n(o);if(vm(a,s,t))try{xt(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 Fn(e){if(!e.enabled)return{added:0};let t=0;for(let s of Cm(e))try{xt(e.collection_id,s,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(n){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${s}:`,n)}return{added:t}}function Bn(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let s=(e.pattern??"").trim();if(!s)throw new Error("pattern required");let n=f(),r=new Date().toISOString(),o=Pn(),a=e.collection_id;a||(a=Lm(t,e.type,e.parent_collection_id)),n.prepare(`INSERT INTO auto_collection_rules
1078
+ ORDER BY priority, created_at`).all(),r=0;for(let o of n){let a=Ir(o);if(vf(a,s,t))try{Ht(a.collection_id,e,null,{source:"auto",rule_id:a.id}).added&&(r+=1)}catch(c){console.error(`[auto-collections] failed to apply rule ${a.id} to session ${e}:`,c)}}return{added:r}}function Lr(e){if(!e.enabled)return{added:0};let t=0;for(let s of Lf(e))try{Ht(e.collection_id,s,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(n){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${s}:`,n)}return{added:t}}function jr(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let s=(e.pattern??"").trim();if(!s)throw new Error("pattern required");let n=f(),r=new Date().toISOString(),o=Cr(),a=e.collection_id;a||(a=Of(t,e.type,e.parent_collection_id)),n.prepare(`INSERT INTO auto_collection_rules
777
1079
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
778
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,s,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),n.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,s);let c=ps(o);return Fn(c),Ke(),c}function Oi(e={}){let t=e.includeSeed?"SELECT * FROM auto_collection_rules ORDER BY priority, created_at":"SELECT * FROM auto_collection_rules WHERE created_by != 'seed' ORDER BY priority, created_at";return f().prepare(t).all().map($n)}function Li(e,t){let s=f(),n=ps(e);if(!n)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():n.name,pattern:t.pattern!==void 0?t.pattern.trim():n.pattern,enabled:t.enabled!==void 0?t.enabled:n.enabled,priority:t.priority!==void 0?t.priority:n.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");s.prepare(`UPDATE auto_collection_rules
1080
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,s,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),n.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,s);let c=$s(o);return Lr(c),rt(),c}function dc(e={}){let t=e.includeSeed?"SELECT * FROM auto_collection_rules ORDER BY priority, created_at":"SELECT * FROM auto_collection_rules WHERE created_by != 'seed' ORDER BY priority, created_at";return f().prepare(t).all().map(Ir)}function pc(e,t){let s=f(),n=$s(e);if(!n)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():n.name,pattern:t.pattern!==void 0?t.pattern.trim():n.pattern,enabled:t.enabled!==void 0?t.enabled:n.enabled,priority:t.priority!==void 0?t.priority:n.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");s.prepare(`UPDATE auto_collection_rules
779
1081
  SET name = ?, pattern = ?, enabled = ?, priority = ?
780
- WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=ps(e);return t.pattern!==void 0&&t.pattern!==n.pattern?(ds(e),o.enabled&&Fn(o)):t.enabled!==void 0&&t.enabled!==n.enabled&&(o.enabled?Fn(o):ds(e)),Ke(),o}function Ci(e){let t=f();if(!ps(e))return{removed:0};let n=ds(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),Ke(),n}function gs(e={}){let t=e.includeDismissed?"SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC":"SELECT * FROM auto_collection_suggestions WHERE dismissed = 0 ORDER BY detected_at DESC";return f().prepare(t).all().map(xm)}function Ii(e){f().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),Ke()}function vi(e){let t=f(),s=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!s)throw new Error(`suggestion not found: ${e}`);if(s.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let n=Bn({name:s.suggested_name,type:s.type,pattern:s.pattern,parent_collection_id:s.suggested_parent_collection_id===null?void 0:s.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),Ke(),n}function _s(){let e=f(),t=new Date().toISOString(),s=bm(),n=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...jm(s,t).filter(c=>!n.has(c.pattern)),...Mm(t),...Dm(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),n.size>0){let c=Array.from(n).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(n))}let o=e.prepare("SELECT type, pattern FROM auto_collection_rules").all(),a=new Set(o.map(c=>`${c.type}:${c.pattern}`));for(let c of r){let d=`${c.type}:${c.pattern}`;if(a.has(d))continue;let u=e.prepare("SELECT id FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").get(c.type,c.pattern);u?e.prepare(`UPDATE auto_collection_suggestions
1082
+ WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=$s(e);return t.pattern!==void 0&&t.pattern!==n.pattern?(Ps(e),o.enabled&&Lr(o)):t.enabled!==void 0&&t.enabled!==n.enabled&&(o.enabled?Lr(o):Ps(e)),rt(),o}function mc(e){let t=f();if(!$s(e))return{removed:0};let n=Ps(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),rt(),n}function Hs(e={}){let t=e.includeDismissed?"SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC":"SELECT * FROM auto_collection_suggestions WHERE dismissed = 0 ORDER BY detected_at DESC";return f().prepare(t).all().map(kf)}function gc(e){f().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),rt()}function _c(e){let t=f(),s=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!s)throw new Error(`suggestion not found: ${e}`);if(s.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let n=jr({name:s.suggested_name,type:s.type,pattern:s.pattern,parent_collection_id:s.suggested_parent_collection_id===null?void 0:s.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),rt(),n}function Ws(){let e=f(),t=new Date().toISOString(),s=Ef(),n=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...If(s,t).filter(c=>!n.has(c.pattern)),...jf(t),...Mf(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),n.size>0){let c=Array.from(n).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(n))}let o=e.prepare("SELECT type, pattern FROM auto_collection_rules").all(),a=new Set(o.map(c=>`${c.type}:${c.pattern}`));for(let c of r){let u=`${c.type}:${c.pattern}`;if(a.has(u))continue;let d=e.prepare("SELECT id FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").get(c.type,c.pattern);d?e.prepare(`UPDATE auto_collection_suggestions
781
1083
  SET session_count = ?, detected_at = ?, suggested_name = ?, suggested_parent_collection_id = ?
782
- WHERE id = ?`).run(c.session_count,t,c.suggested_name,c.suggested_parent_collection_id,u.id):e.prepare(`INSERT INTO auto_collection_suggestions
1084
+ WHERE id = ?`).run(c.session_count,t,c.suggested_name,c.suggested_parent_collection_id,d.id):e.prepare(`INSERT INTO auto_collection_suggestions
783
1085
  (id, type, pattern, suggested_name, suggested_parent_collection_id, session_count, detected_at, dismissed)
784
- VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(Pn(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return Ke(),gs()}function jm(e,t){let n=f().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of n){let c=a.cwd.split("/").filter(Boolean),d="";for(let u of c){if(d=`${d}/${u}`,d===e||d==="/")continue;let g=r.get(d);g||(g=new Set,r.set(d,g)),g.add(a.id)}}let o=[];for(let[a,c]of r.entries()){if(c.size<Ai)continue;let d=!1;for(let[u,g]of r.entries())if(u!==a&&u.startsWith(a+"/")&&g.size>=Ai){d=!0;break}d||o.push({type:"cwd-prefix",pattern:a,suggested_name:Sm(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function Mm(e){return f().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(Rm).map(s=>({type:"tag",pattern:s.tag,suggested_name:s.tag,suggested_parent_collection_id:null,session_count:s.n,detected_at:e,dismissed:!1}))}function Dm(e){let t=f().prepare(`SELECT id, first_user_message FROM sessions
785
- WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),s=new Map;for(let r of t)for(let o of Am){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let d=s.get(c);d||(d=new Set,s.set(c,d)),d.add(r.id)}}let n=[];for(let[r,o]of s.entries())o.size<km||n.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return n}function ms(e){return e.replace(/[\\%_]/g,"\\$&")}function Ke(){try{J(),hm(us)||Em(us,{recursive:!0});let e=f(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),s=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();ki(Tm,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),ki(ym,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:s},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function ji(){let e=f().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}H();Z();import{randomUUID as Mi}from"node:crypto";import{writeFileSync as Di,readFileSync as Nw,existsSync as Fm,mkdirSync as Pm}from"node:fs";import{join as Hn}from"node:path";var fs=Hn($,"threads"),Um=Hn(fs,"index.json");function Fi(){J(),Fm(fs)||Pm(fs,{recursive:!0})}function Wn(e,t,s){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:s?.project??null,project_count:s?.project_count??0,folder_id:e.folder_id??null}}function Pi(e){let t=new Map;if(e.length===0)return t;let s=f(),n=e.map(()=>"?").join(","),r=s.prepare(`SELECT te.thread_id AS thread_id,
1086
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(Cr(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return rt(),Hs()}function If(e,t){let n=f().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of n){let c=a.cwd.split("/").filter(Boolean),u="";for(let d of c){if(u=`${u}/${d}`,u===e||u==="/")continue;let m=r.get(u);m||(m=new Set,r.set(u,m)),m.add(a.id)}}let o=[];for(let[a,c]of r.entries()){if(c.size<cc)continue;let u=!1;for(let[d,m]of r.entries())if(d!==a&&d.startsWith(a+"/")&&m.size>=cc){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:bf(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function jf(e){return f().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(wf).map(s=>({type:"tag",pattern:s.tag,suggested_name:s.tag,suggested_parent_collection_id:null,session_count:s.n,detected_at:e,dismissed:!1}))}function Mf(e){let t=f().prepare(`SELECT id, first_user_message FROM sessions
1087
+ WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),s=new Map;for(let r of t)for(let o of Af){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=s.get(c);u||(u=new Set,s.set(c,u)),u.add(r.id)}}let n=[];for(let[r,o]of s.entries())o.size<Rf||n.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return n}function Bs(e){return e.replace(/[\\%_]/g,"\\$&")}function rt(){try{J(),ff(Us)||hf(Us,{recursive:!0});let e=f(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),s=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();ac(Sf,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),ac(Tf,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:s},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function fc(){let e=f().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}H();Q();import{randomUUID as hc}from"node:crypto";import{writeFileSync as Ec,readFileSync as uA,existsSync as Df,mkdirSync as Ff}from"node:fs";import{join as Mr}from"node:path";var qs=Mr(U,"threads"),Pf=Mr(qs,"index.json");function bc(){J(),Df(qs)||Ff(qs,{recursive:!0})}function Dr(e,t,s){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:s?.project??null,project_count:s?.project_count??0,folder_id:e.folder_id??null}}function Sc(e){let t=new Map;if(e.length===0)return t;let s=f(),n=e.map(()=>"?").join(","),r=s.prepare(`SELECT te.thread_id AS thread_id,
786
1088
  p.name AS project,
787
1089
  COUNT(*) AS n,
788
1090
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
@@ -790,7 +1092,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
790
1092
  LEFT JOIN sessions s ON s.id = te.session_id
791
1093
  LEFT JOIN projects p ON p.id = s.project_id
792
1094
  WHERE te.thread_id IN (${n})
793
- 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 d=c.filter(h=>h.project!==null),u=d.length,g=null;d.length>0&&(g=[...d].sort((b,S)=>S.n-b.n||S.origin_n-b.origin_n||(b.project??"").localeCompare(S.project??""))[0].project),t.set(a,{project:g,project_count:u})}return t}function Ui(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 $i(e){let s=f().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1095
+ GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let a of r){let c=o.get(a.thread_id);c||(c=[],o.set(a.thread_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(h=>h.project!==null),d=u.length,m=null;u.length>0&&(m=[...u].sort((b,T)=>T.n-b.n||T.origin_n-b.origin_n||(b.project??"").localeCompare(T.project??""))[0].project),t.set(a,{project:m,project_count:d})}return t}function Tc(e){let t=e.auto_title_source;return{thread_id:e.thread_id,session_id:e.session_id,parent_session_id:e.parent_session_id,role:e.role,confidence:e.confidence,source:e.source,added_at:e.added_at,alias:e.alias,auto_title:e.auto_title,auto_title_source:t==="agent"||t==="heuristic"?t:null,alias_source:e.alias?"manual":null,first_user_message:e.first_user_message,project:e.project}}function yc(e){let s=f().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
794
1096
  s.auto_title AS auto_title,
795
1097
  s.auto_title_source AS auto_title_source,
796
1098
  s.first_user_message AS first_user_message,
@@ -798,11 +1100,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
798
1100
  FROM (SELECT ? AS sid) q
799
1101
  LEFT JOIN sessions s ON s.id = q.sid
800
1102
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
801
- LEFT JOIN projects p ON p.id = s.project_id`).get(e),n=s?.auto_title_source??null;return{alias:s?.alias??null,auto_title:s?.auto_title??null,auto_title_source:n==="agent"||n==="heuristic"?n:null,first_user_message:s?.first_user_message??null,project:s?.project??null}}function qn(e){let s=f().prepare(`SELECT
1103
+ LEFT JOIN projects p ON p.id = s.project_id`).get(e),n=s?.auto_title_source??null;return{alias:s?.alias??null,auto_title:s?.auto_title??null,auto_title_source:n==="agent"||n==="heuristic"?n:null,first_user_message:s?.first_user_message??null,project:s?.project??null}}function Fr(e){let s=f().prepare(`SELECT
802
1104
  COUNT(*) AS session_count,
803
1105
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
804
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function fe(e){let t=te(e);t&&(Fi(),Di(Hn(fs,`${e}.json`),JSON.stringify(t,null,2)),Bi())}function Bi(){Fi();let e=Jn({includeArchived:!0});Di(Um,JSON.stringify({threads:e},null,2))}function hs(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=f(),n=Mi(),r=new Date().toISOString();s.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(n,t,e.summary?.trim()||null,r),e.originSessionId&&s.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
805
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),fe(n);let o=te(n);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Jn(e={}){let t=f(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=Pi(n.map(o=>o.id));return n.map(o=>Wn(o,qn(o.id),r.get(o.id)))}function te(e){let t=f(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
1106
+ FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function Te(e){let t=re(e);t&&(bc(),Ec(Mr(qs,`${e}.json`),JSON.stringify(t,null,2)),wc())}function wc(){bc();let e=Pr({includeArchived:!0});Ec(Pf,JSON.stringify({threads:e},null,2))}function Xs(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=f(),n=hc(),r=new Date().toISOString();s.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(n,t,e.summary?.trim()||null,r),e.originSessionId&&s.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1107
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),Te(n);let o=re(n);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Pr(e={}){let t=f(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=Sc(n.map(o=>o.id));return n.map(o=>Dr(o,Fr(o.id),r.get(o.id)))}function re(e){let t=f(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
806
1108
  NULLIF(sa.alias, '') AS alias,
807
1109
  s.auto_title AS auto_title,
808
1110
  s.auto_title_source AS auto_title_source,
@@ -813,10 +1115,10 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
813
1115
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
814
1116
  LEFT JOIN projects p ON p.id = s.project_id
815
1117
  WHERE e.thread_id = ?
816
- ORDER BY e.added_at ASC`).all(e).map(Ui),r=Pi([e]).get(e);return{...Wn(s,qn(s.id),r),edges:n}}function Hi(e){return f().prepare(`SELECT t.* FROM threads t
1118
+ ORDER BY e.added_at ASC`).all(e).map(Tc),r=Sc([e]).get(e);return{...Dr(s,Fr(s.id),r),edges:n}}function Rc(e){return f().prepare(`SELECT t.* FROM threads t
817
1119
  JOIN thread_edges e ON e.thread_id = t.id
818
1120
  WHERE e.session_id = ? AND t.archived = 0
819
- ORDER BY t.created_at DESC`).all(e).map(n=>Wn(n,qn(n.id)))}function Es(e){let t=f();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let n=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),a=e.confidence??1,c=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
1121
+ ORDER BY t.created_at DESC`).all(e).map(n=>Dr(n,Fr(n.id)))}function Js(e){let t=f();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let n=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),a=e.confidence??1,c=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
820
1122
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
821
1123
  VALUES (?, ?, ?, ?, ?, ?, ?)
822
1124
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -824,33 +1126,33 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
824
1126
  role = excluded.role,
825
1127
  confidence = excluded.confidence,
826
1128
  source = excluded.source,
827
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,n),fe(e.threadId);let d=$i(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:n,alias:d.alias,auto_title:d.auto_title,auto_title_source:d.auto_title_source,alias_source:d.alias?"manual":null,first_user_message:d.first_user_message,project:d.project}}function Wi(e,t){let n=f().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&fe(e),{removed:n.changes}}function Nt(e,t,s){let n=f(),r=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(s!==null){if(s===t)throw new Error("cycle detected: session cannot be its own parent");let c=n.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),d=s,u=new Set;for(;d!==null;){if(d===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(u.has(d))break;u.add(d),d=c.get(e,d)?.parent_session_id??null}}let o=s?"child":"origin";n.prepare(`UPDATE thread_edges
1129
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,n),Te(e.threadId);let u=yc(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:n,alias:u.alias,auto_title:u.auto_title,auto_title_source:u.auto_title_source,alias_source:u.alias?"manual":null,first_user_message:u.first_user_message,project:u.project}}function Ac(e,t){let n=f().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&Te(e),{removed:n.changes}}function Wt(e,t,s){let n=f(),r=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(s!==null){if(s===t)throw new Error("cycle detected: session cannot be its own parent");let c=n.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=s,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=s?"child":"origin";n.prepare(`UPDATE thread_edges
828
1130
  SET parent_session_id = ?, role = ?, added_at = ?
829
- WHERE thread_id = ? AND session_id = ?`).run(s,o,new Date().toISOString(),e,t),fe(e);let a=$i(t);return Ui({...r,parent_session_id:s,role:o,added_at:new Date().toISOString(),alias:a.alias,auto_title:a.auto_title,auto_title_source:a.auto_title_source,first_user_message:a.first_user_message,project:a.project})}function qi(e,t){let s=t.trim();if(!s)throw new Error("name cannot be empty");f().prepare("UPDATE threads SET name = ? WHERE id = ?").run(s,e),fe(e);let r=te(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Ji(e){f().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),fe(e);let s=te(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Xi(e){f().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),fe(e);let s=te(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Gi(e){f().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),fe(e);let s=te(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Yi(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let s=f(),n=new Date().toISOString();s.transaction(()=>{let o=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)s.prepare(`INSERT INTO thread_edges
1131
+ WHERE thread_id = ? AND session_id = ?`).run(s,o,new Date().toISOString(),e,t),Te(e);let a=yc(t);return Tc({...r,parent_session_id:s,role:o,added_at:new Date().toISOString(),alias:a.alias,auto_title:a.auto_title,auto_title_source:a.auto_title_source,first_user_message:a.first_user_message,project:a.project})}function kc(e,t){let s=t.trim();if(!s)throw new Error("name cannot be empty");f().prepare("UPDATE threads SET name = ? WHERE id = ?").run(s,e),Te(e);let r=re(e);if(!r)throw new Error(`thread ${e} not found`);return r}function xc(e){f().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Te(e);let s=re(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Nc(e){f().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Te(e);let s=re(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Oc(e){f().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Te(e);let s=re(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Lc(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let s=f(),n=new Date().toISOString();s.transaction(()=>{let o=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)s.prepare(`INSERT INTO thread_edges
830
1132
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
831
1133
  VALUES (?, ?, ?, ?, ?, ?, ?)
832
1134
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
833
1135
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
834
1136
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
835
1137
  confidence = MAX(thread_edges.confidence, excluded.confidence),
836
- source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,n);s.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),fe(t),Bi();let r=te(t);if(!r)throw new Error("merge destination disappeared");return r}function Ki(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=f(),s=new Date().toISOString(),n=Mi();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(n,e.newThreadName.trim(),s);for(let o of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);a&&(t.prepare(`INSERT INTO thread_edges
1138
+ source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,n);s.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Te(t),wc();let r=re(t);if(!r)throw new Error("merge destination disappeared");return r}function Cc(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=f(),s=new Date().toISOString(),n=hc();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(n,e.newThreadName.trim(),s);for(let o of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);a&&(t.prepare(`INSERT INTO thread_edges
837
1139
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
838
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(n,o,a.parent_session_id,a.role,a.confidence,a.source,s),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),fe(e.threadId),fe(n);let r=te(n);if(!r)throw new Error("split destination disappeared");return r}H();import{execFile as eg}from"node:child_process";import{promisify as tg}from"node:util";import{readlink as sg,readFile as ea}from"node:fs/promises";import{platform as ws}from"node:os";import{readFileSync as $m,statSync as Bm}from"node:fs";var Hm=200*1024*1024,Ss=.7,Ts=.5,Zi=Ts,Wm=[{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"}],qm=["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 zi(e){if(!e)return null;if(e.startsWith("/")){let s=e.split(" \xB7 ");if(s.length>1)return s[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function Jm(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of Wm)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Xm(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],s=Math.min(e.length,t.length);for(let n=0;n<s;n++){let r=e[n];if(!r)continue;let o=r.toLowerCase();for(let a of qm)if(o.includes(a))return{weight:t[n],matched:a,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function Xn(e,t){if(!e||!t||e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function Gm(e,t){if(e.length===0||t.length===0)return 0;let s=0;for(let n of e)for(let r of t){let o=Xn(n,r);o>s&&(s=o)}return s}function Ym(e,t){let s=Xn(e.mean_embedding,t.mean_embedding),n=Xn(e.tail_pool,t.head_pool),r=Gm(e.sample_chunks,t.sample_chunks),o=0,a=null;if(s>o&&(o=s,a="mean"),n>o&&(o=n,a="asymmetric"),r>o&&(o=r,a="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:a};let c=(o-.65)/.2*.3;return{weight:Math.round(c*100)/100,cosine:o,mode:a}}function Km(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 zm(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let s=0;for(let r of t)e.has(r)&&s++;return s===0?{weight:0,count:0}:{weight:Math.min(.4,s*.1),count:s}}function Vm(e,t){let s=zi(e),n=zi(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function Vi(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Zm(e,t){let s=0,n=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
839
- `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){s+=.5,n=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Vi(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Vi(a);if(c.length<200)continue;let d=Math.min(c.length,240),u=c.slice(0,d);if(o.includes(u)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function Qm(e,t,s=Zi){if(t.started_at_ms<=e.started_at_ms)return null;let n=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<n)return null;let r=Jm(n,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=Xm(o),c=zm(e.touched_files,t.touched_files),d=Vm(e.auto_title,t.auto_title),u=Ym(e,t),g=Km(e,t),h=Zm(e,t),b=r.weight+a.weight+c.weight+d.weight+u.weight+g.weight+h.weight;if(b<s)return null;let S=[];if(r.label&&S.push(`temporal ${r.label} (+${r.weight})`),a.matched){let y=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;S.push(`continuation phrase "${a.matched}" in ${y} (+${a.weight})`)}if(c.count>0&&S.push(`${c.count} file${c.count===1?"":"s"} overlap (+${c.weight.toFixed(1)})`),d.brand&&S.push(`shared brand "${d.brand}" (+${d.weight})`),u.weight>0&&u.mode&&S.push(`semantic ${u.mode==="asymmetric"?"tail\u2192head":u.mode==="max_pool"?"best-chunk":"mean"} ${u.cosine.toFixed(2)} (+${u.weight.toFixed(2)})`),g.same&&S.push(`same cluster (+${g.weight})`),h.weight>0){let y=[];h.pathMatch&&y.push(`opened authored path "${h.pathMatch.split("/").pop()}"`),h.contentMatch&&y.push("verbatim-paste of authored content"),S.push(`doc-authorship: ${y.join(" + ")} (+${h.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,b),signals:{temporal:r.weight,continuation:a.weight,file_overlap:c.weight,same_brand:d.weight,semantic:u.weight,cluster:g.weight,doc_authorship:h.weight},reasons:S}}function ze(e,t=Zi){let s=[];for(let n=0;n<e.length;n++){let r=e[n],o=null;for(let a=0;a<n;a++){let c=e[a],d=Qm(c,r,t);d&&(!o||d.confidence>o.confidence)&&(o=d)}o&&s.push(o)}return s}function Qi(e,t){let s=new Map,n=c=>{let d=c;for(;s.get(d)!==d;)d=s.get(d);let u=c;for(;s.get(u)!==d;){let g=s.get(u);s.set(u,d),u=g}return d},r=(c,d)=>{let u=n(c),g=n(d);u!==g&&s.set(u,g)};for(let c of e)s.has(c.parent_id)||s.set(c.parent_id,c.parent_id),s.has(c.child_id)||s.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of s.keys()){let d=n(c),u=o.get(d);u||(u=[],o.set(d,u)),u.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((d,u)=>(a.get(d)??0)-(a.get(u)??0)),{rootId:c[0],sessionIds:c}))}function ys(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],d;try{if(Bm(e).size>Hm)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};d=$m(e,"utf8")}catch{return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}let u=0;for(;u<d.length;){let g=d.indexOf(`
840
- `,u),h=g===-1?d.length:g,b=d.slice(u,h);if(u=g===-1?d.length:g+1,!b.trim())continue;let S;try{S=JSON.parse(b)}catch{continue}let y=S;if(y.type==="user"&&y.message?.role==="user"&&typeof y.message.content=="string"&&o.length<s){let w=y.message.content.trim();w&&o.push(w.length>n?w.slice(0,n):w)}let k=y.message?.content;if(Array.isArray(k))for(let w of k){if(!w||typeof w!="object")continue;let D=w;if(D.type!=="tool_use")continue;let L=D.input??{},X=typeof L.file_path=="string"?L.file_path:null;if(X){let M=bs(X);M&&r.add(M)}if((D.name==="Write"||D.name==="Edit"||D.name==="MultiEdit")&&X){let M=bs(X);M&&a.add(M);let B=typeof L.content=="string"?L.content:typeof L.new_string=="string"?L.new_string:null;B&&B.length>=200&&c.push(B.length>4096?B.slice(0,4096):B)}if(D.name==="Bash"&&typeof L.command=="string")for(let M of L.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let B=bs(M[1]);B&&r.add(B)}if((D.name==="Glob"||D.name==="Grep")&&typeof L.pattern=="string"){let M=bs(L.pattern);M&&!M.includes("*")&&r.add(M)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function bs(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Yn=tg(eg),ng=6,ta="Active ",sa=" sessions \u2014 ",rg=6e4;async function og(){if(ws()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Yn(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
841
- `)){let o=r.trim().match(/^(\d+)\s+(.+)$/);if(!o)continue;let a=Number(o[1]),c=o[2].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&Number.isFinite(a)&&n.push(a)}return n}catch{continue}return[]}async function ig(e){let t=ws();if(t==="linux")try{return(await sg(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:n}=await Yn(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
842
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function ag(e){let t=ws();if(t==="linux")try{let s=await ea(`/proc/${e}/stat`,"utf8"),n=s.lastIndexOf(")");if(n===-1)return null;let r=s.slice(n+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await ea("/proc/uptime","utf8"),c=Number(a.split(/\s+/)[0]);return Number.isFinite(c)?Date.now()-c*1e3+o/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Yn(s,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(n.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function cg(e,t){let s=await og();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let a of s){let c=await ig(a);if(c&&(c===n||c.startsWith(n+"/"))){let d=await ag(a);r.push({pid:a,startMs:d})}}if(r.length===0)return new Set;let o=new Set;for(let{startMs:a}of r){if(a==null)continue;let c=null,d=rg;for(let u of t){if(o.has(u.session_id)||!u.started_at)continue;let g=Date.parse(u.started_at);if(!Number.isFinite(g))continue;let h=Math.abs(g-a);h<d&&(d=h,c=u)}c&&o.add(c.session_id)}return o}function lg(e){let s=f().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!s)throw new Error(`project ${e} not found`);return s}function dg(e,t){let s=f(),n=`${ta}${t}${sa}%`,r=s.prepare(`SELECT t.id
1140
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(n,o,a.parent_session_id,a.role,a.confidence,a.source,s),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),Te(e.threadId),Te(n);let r=re(n);if(!r)throw new Error("split destination disappeared");return r}H();import{execFile as Qf}from"node:child_process";import{promisify as eh}from"node:util";import{readlink as th,readFile as Dc}from"node:fs/promises";import{platform as Vs}from"node:os";import{readFileSync as Uf,statSync as $f}from"node:fs";var Bf=200*1024*1024,Gs=.7,zs=.5,jc=zs,Hf=[{maxGapMs:3600*1e3,weight:.7,label:"<1h gap"},{maxGapMs:14400*1e3,weight:.4,label:"<4h gap"},{maxGapMs:1440*60*1e3,weight:.2,label:"<24h gap"}],Wf=["let's commit","lets commit","now commit","inspect the diff","inspect this diff","review the diff","review this diff","diff of all changes","diff of changes","based on what we just did","based on the things done","based on all the things","continue from","continuing from","pick up where","next step","now fix","now lets","now let's","from the previous session","from our last session","from the last session"];function vc(e){if(!e)return null;if(e.startsWith("/")){let s=e.split(" \xB7 ");if(s.length>1)return s[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function qf(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of Hf)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Xf(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],s=Math.min(e.length,t.length);for(let n=0;n<s;n++){let r=e[n];if(!r)continue;let o=r.toLowerCase();for(let a of Wf)if(o.includes(a))return{weight:t[n],matched:a,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function Ur(e,t){if(!e||!t||e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function Jf(e,t){if(e.length===0||t.length===0)return 0;let s=0;for(let n of e)for(let r of t){let o=Ur(n,r);o>s&&(s=o)}return s}function Yf(e,t){let s=Ur(e.mean_embedding,t.mean_embedding),n=Ur(e.tail_pool,t.head_pool),r=Jf(e.sample_chunks,t.sample_chunks),o=0,a=null;if(s>o&&(o=s,a="mean"),n>o&&(o=n,a="asymmetric"),r>o&&(o=r,a="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:a};let c=(o-.65)/.2*.3;return{weight:Math.round(c*100)/100,cosine:o,mode:a}}function Gf(e,t){return e.cluster_id===null||t.cluster_id===null?{weight:0,same:!1}:e.cluster_id!==t.cluster_id?{weight:0,same:!1}:{weight:.05,same:!0}}function zf(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let s=0;for(let r of t)e.has(r)&&s++;return s===0?{weight:0,count:0}:{weight:Math.min(.4,s*.1),count:s}}function Kf(e,t){let s=vc(e),n=vc(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function Ic(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Vf(e,t){let s=0,n=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1141
+ `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){s+=.5,n=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Ic(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Ic(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function Zf(e,t,s=jc){if(t.started_at_ms<=e.started_at_ms)return null;let n=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<n)return null;let r=qf(n,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=Xf(o),c=zf(e.touched_files,t.touched_files),u=Kf(e.auto_title,t.auto_title),d=Yf(e,t),m=Gf(e,t),h=Vf(e,t),b=r.weight+a.weight+c.weight+u.weight+d.weight+m.weight+h.weight;if(b<s)return null;let T=[];if(r.label&&T.push(`temporal ${r.label} (+${r.weight})`),a.matched){let S=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;T.push(`continuation phrase "${a.matched}" in ${S} (+${a.weight})`)}if(c.count>0&&T.push(`${c.count} file${c.count===1?"":"s"} overlap (+${c.weight.toFixed(1)})`),u.brand&&T.push(`shared brand "${u.brand}" (+${u.weight})`),d.weight>0&&d.mode&&T.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),m.same&&T.push(`same cluster (+${m.weight})`),h.weight>0){let S=[];h.pathMatch&&S.push(`opened authored path "${h.pathMatch.split("/").pop()}"`),h.contentMatch&&S.push("verbatim-paste of authored content"),T.push(`doc-authorship: ${S.join(" + ")} (+${h.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,b),signals:{temporal:r.weight,continuation:a.weight,file_overlap:c.weight,same_brand:u.weight,semantic:d.weight,cluster:m.weight,doc_authorship:h.weight},reasons:T}}function ot(e,t=jc){let s=[];for(let n=0;n<e.length;n++){let r=e[n],o=null;for(let a=0;a<n;a++){let c=e[a],u=Zf(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&s.push(o)}return s}function Mc(e,t){let s=new Map,n=c=>{let u=c;for(;s.get(u)!==u;)u=s.get(u);let d=c;for(;s.get(d)!==u;){let m=s.get(d);s.set(d,u),d=m}return u},r=(c,u)=>{let d=n(c),m=n(u);d!==m&&s.set(d,m)};for(let c of e)s.has(c.parent_id)||s.set(c.parent_id,c.parent_id),s.has(c.child_id)||s.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of s.keys()){let u=n(c),d=o.get(u);d||(d=[],o.set(u,d)),d.push(c)}let a=new Map;for(let c of t)a.set(c.id,c.started_at_ms);return Array.from(o.values()).map(c=>(c.sort((u,d)=>(a.get(u)??0)-(a.get(d)??0)),{rootId:c[0],sessionIds:c}))}function Ks(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if($f(e).size>Bf)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=Uf(e,"utf8")}catch{return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}let d=0;for(;d<u.length;){let m=u.indexOf(`
1142
+ `,d),h=m===-1?u.length:m,b=u.slice(d,h);if(d=m===-1?u.length:m+1,!b.trim())continue;let T;try{T=JSON.parse(b)}catch{continue}let S=T;if(S.type==="user"&&S.message?.role==="user"&&typeof S.message.content=="string"&&o.length<s){let R=S.message.content.trim();R&&o.push(R.length>n?R.slice(0,n):R)}let w=S.message?.content;if(Array.isArray(w))for(let R of w){if(!R||typeof R!="object")continue;let D=R;if(D.type!=="tool_use")continue;let O=D.input??{},Y=typeof O.file_path=="string"?O.file_path:null;if(Y){let M=Ys(Y);M&&r.add(M)}if((D.name==="Write"||D.name==="Edit"||D.name==="MultiEdit")&&Y){let M=Ys(Y);M&&a.add(M);let W=typeof O.content=="string"?O.content:typeof O.new_string=="string"?O.new_string:null;W&&W.length>=200&&c.push(W.length>4096?W.slice(0,4096):W)}if(D.name==="Bash"&&typeof O.command=="string")for(let M of O.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let W=Ys(M[1]);W&&r.add(W)}if((D.name==="Glob"||D.name==="Grep")&&typeof O.pattern=="string"){let M=Ys(O.pattern);M&&!M.includes("*")&&r.add(M)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function Ys(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Br=eh(Qf),sh=6,Fc="Active ",Pc=" sessions \u2014 ",nh=6e4;async function rh(){if(Vs()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Br(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
1143
+ `)){let o=r.trim().match(/^(\d+)\s+(.+)$/);if(!o)continue;let a=Number(o[1]),c=o[2].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&Number.isFinite(a)&&n.push(a)}return n}catch{continue}return[]}async function oh(e){let t=Vs();if(t==="linux")try{return(await th(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:n}=await Br(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
1144
+ `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function ih(e){let t=Vs();if(t==="linux")try{let s=await Dc(`/proc/${e}/stat`,"utf8"),n=s.lastIndexOf(")");if(n===-1)return null;let r=s.slice(n+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await Dc("/proc/uptime","utf8"),c=Number(a.split(/\s+/)[0]);return Number.isFinite(c)?Date.now()-c*1e3+o/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Br(s,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(n.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function ah(e,t){let s=await rh();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let a of s){let c=await oh(a);if(c&&(c===n||c.startsWith(n+"/"))){let u=await ih(a);r.push({pid:a,startMs:u})}}if(r.length===0)return new Set;let o=new Set;for(let{startMs:a}of r){if(a==null)continue;let c=null,u=nh;for(let d of t){if(o.has(d.session_id)||!d.started_at)continue;let m=Date.parse(d.started_at);if(!Number.isFinite(m))continue;let h=Math.abs(m-a);h<u&&(u=h,c=d)}c&&o.add(c.session_id)}return o}function ch(e){let s=f().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!s)throw new Error(`project ${e} not found`);return s}function lh(e,t){let s=f(),n=`${Fc}${t}${Pc}%`,r=s.prepare(`SELECT t.id
843
1145
  FROM threads t
844
1146
  WHERE t.archived = 0
845
1147
  AND t.name LIKE ?
846
- ORDER BY t.created_at DESC`).all(n);for(let a of r){let c=te(a.id);if(c&&c.project===t)return c}let o=s.prepare(`SELECT DISTINCT te.thread_id AS id
1148
+ ORDER BY t.created_at DESC`).all(n);for(let a of r){let c=re(a.id);if(c&&c.project===t)return c}let o=s.prepare(`SELECT DISTINCT te.thread_id AS id
847
1149
  FROM thread_edges te
848
1150
  JOIN sessions s ON s.id = te.session_id
849
1151
  JOIN threads t ON t.id = te.thread_id
850
1152
  WHERE s.project_id = ?
851
1153
  AND t.archived = 0
852
1154
  AND t.name LIKE ?
853
- LIMIT 1`).get(e,n);return o?te(o.id):null}function na(e){let t=e?new Date(e):new Date,s=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${s}-${n}-${r}`}function Gn(e,t){let s=f(),n=t>0,r=n?Date.now()-t*60*60*1e3:0;return n?s.prepare(`SELECT s.id AS session_id,
1155
+ LIMIT 1`).get(e,n);return o?re(o.id):null}function Uc(e){let t=e?new Date(e):new Date,s=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${s}-${n}-${r}`}function $r(e,t){let s=f(),n=t>0,r=n?Date.now()-t*60*60*1e3:0;return n?s.prepare(`SELECT s.id AS session_id,
854
1156
  sa.alias AS alias,
855
1157
  s.auto_title AS auto_title,
856
1158
  s.first_user_message AS first_user_message,
@@ -870,86 +1172,19 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
870
1172
  FROM sessions s
871
1173
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
872
1174
  WHERE s.project_id = ?
873
- ORDER BY s.started_at ASC`).all(e)}function ug(e){let t=f(),s=[];for(let n of e){if(!n.started_at)continue;let r=Date.parse(n.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(n.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=ys(o.file_path,{maxUserMessages:7});s.push({id:n.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:n.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:n.auto_title,touched_files:c.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:c.authored_paths,authored_content:c.authored_content})}return s}function pg(e){let s=f().prepare(`SELECT session_id, parent_session_id, source
1175
+ ORDER BY s.started_at ASC`).all(e)}function uh(e){let t=f(),s=[];for(let n of e){if(!n.started_at)continue;let r=Date.parse(n.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(n.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=Ks(o.file_path,{maxUserMessages:7});s.push({id:n.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:n.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:n.auto_title,touched_files:c.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:c.authored_paths,authored_content:c.authored_content})}return s}function dh(e){let s=f().prepare(`SELECT session_id, parent_session_id, source
874
1176
  FROM thread_edges
875
- WHERE thread_id = ?`).all(e),n=new Map;for(let r of s)n.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return n}async function ra(e,t={}){let s=lg(e),n=t.windowHours??ng,r=t.scoreThreshold??Ts,o=t.useLivePids??!0,a=[],c=[];if(o&&s.decoded_path){let k=Gn(e,0),w=await cg(s.decoded_path,k);if(w===null){let L=ws()==="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(L),c=Gn(e,n)}else w.size===0?(a.push(`No active terminals open in ${s.name} (cwd=${s.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=k.filter(D=>w.has(D.session_id))}else c=Gn(e,n);c.length===0&&!a.length&&a.push(`No active sessions in ${s.name} within the last ${n}h.`);let d=dg(e,s.name),u=new Set(d?d.edges.map(k=>k.session_id):[]),g=c.filter(k=>!u.has(k.session_id)),h=ug(c);h.sort((k,w)=>k.started_at_ms-w.started_at_ms);let b=ze(h,r),S=d?d.edges.filter(k=>k.source!=="auto-active"&&(k.parent_session_id||k.role==="origin")).map(k=>({session_id:k.session_id,parent_session_id:k.parent_session_id})):[],y=d?d.name:`${ta}${s.name}${sa}${na(t.todayIso)}`;return{project:s,thread:{id:d?.id??null,name:y,exists:!!d,existing_session_count:d?.edges.length??0},candidates:c,proposed_additions:g,proposed_edges:b,preserved_manual_edges:S,warnings:a}}function oa(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},s;e.thread.exists&&e.thread.id?s=e.thread.id:s=hs({name:e.thread.name,summary:`Auto-captured by sync-active on ${na()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=s;let n=pg(s);for(let a of e.candidates)n.has(a.session_id)||(Es({threadId:s,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,n.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=n.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&n.has(a.parent_id))try{Nt(s,a.child_id,a.parent_id),t.edges_set++,n.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=f().prepare(`SELECT session_id FROM thread_edges
1177
+ WHERE thread_id = ?`).all(e),n=new Map;for(let r of s)n.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return n}async function $c(e,t={}){let s=ch(e),n=t.windowHours??sh,r=t.scoreThreshold??zs,o=t.useLivePids??!0,a=[],c=[];if(o&&s.decoded_path){let w=$r(e,0),R=await ah(s.decoded_path,w);if(R===null){let O=Vs()==="win32"?"Windows live-PID detection is not yet supported \u2014 falling back to the rolling mtime window.":"No live `claude` processes detected \u2014 falling back to the rolling mtime window. Output may include sessions that are no longer open.";a.push(O),c=$r(e,n)}else R.size===0?(a.push(`No active terminals open in ${s.name} (cwd=${s.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=w.filter(D=>R.has(D.session_id))}else c=$r(e,n);c.length===0&&!a.length&&a.push(`No active sessions in ${s.name} within the last ${n}h.`);let u=lh(e,s.name),d=new Set(u?u.edges.map(w=>w.session_id):[]),m=c.filter(w=>!d.has(w.session_id)),h=uh(c);h.sort((w,R)=>w.started_at_ms-R.started_at_ms);let b=ot(h,r),T=u?u.edges.filter(w=>w.source!=="auto-active"&&(w.parent_session_id||w.role==="origin")).map(w=>({session_id:w.session_id,parent_session_id:w.parent_session_id})):[],S=u?u.name:`${Fc}${s.name}${Pc}${Uc(t.todayIso)}`;return{project:s,thread:{id:u?.id??null,name:S,exists:!!u,existing_session_count:u?.edges.length??0},candidates:c,proposed_additions:m,proposed_edges:b,preserved_manual_edges:T,warnings:a}}function Bc(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},s;e.thread.exists&&e.thread.id?s=e.thread.id:s=Xs({name:e.thread.name,summary:`Auto-captured by sync-active on ${Uc()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=s;let n=dh(s);for(let a of e.candidates)n.has(a.session_id)||(Js({threadId:s,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,n.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=n.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&n.has(a.parent_id))try{Wt(s,a.child_id,a.parent_id),t.edges_set++,n.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=f().prepare(`SELECT session_id FROM thread_edges
876
1178
  WHERE thread_id = ?
877
1179
  AND source = 'auto-active'
878
1180
  AND parent_session_id IS NULL
879
- AND role = 'child'`).all(s);for(let a of o)try{Nt(s,a.session_id,null)}catch{}return t}H();Z();import{randomUUID as mg}from"node:crypto";import{writeFileSync as gg}from"node:fs";import{join as _g}from"node:path";var fg=_g($,"thread-folders.json");function ia(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 Ot(){try{J();let e=Kn({includeArchived:!0});gg(fg,JSON.stringify({folders:e},null,2))}catch{}}function Kn(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return f().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(ia)}function $e(e){let t=f().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?ia(t):null}function aa(e){let t=e.name.trim();if(!t)throw new Error("folder name cannot be empty");if(t.length>200)throw new Error("folder name too long (200 char max)");let s=e.parentFolderId??null,n=e.projectScope??null;if(s){let c=$e(s);if(!c)throw new Error(`parent folder ${s} not found`);n=c.project_scope}let r=mg(),o=new Date().toISOString(),a=ca(s,n);return f().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
880
- VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,s,n,o,a),Ot(),{id:r,name:t,parent_folder_id:s,project_scope:n,created_at:o,archived:!1,sort_order:a}}function ca(e,t){return f().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1181
+ AND role = 'child'`).all(s);for(let a of o)try{Wt(s,a.session_id,null)}catch{}return t}H();Q();import{randomUUID as ph}from"node:crypto";import{writeFileSync as mh}from"node:fs";import{join as gh}from"node:path";var _h=gh(U,"thread-folders.json");function Hc(e){return{id:e.id,name:e.name,parent_folder_id:e.parent_folder_id,project_scope:e.project_scope,created_at:e.created_at,archived:e.archived===1,sort_order:e.sort_order}}function qt(){try{J();let e=Hr({includeArchived:!0});mh(_h,JSON.stringify({folders:e},null,2))}catch{}}function Hr(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return f().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(Hc)}function Ye(e){let t=f().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?Hc(t):null}function Wc(e){let t=e.name.trim();if(!t)throw new Error("folder name cannot be empty");if(t.length>200)throw new Error("folder name too long (200 char max)");let s=e.parentFolderId??null,n=e.projectScope??null;if(s){let c=Ye(s);if(!c)throw new Error(`parent folder ${s} not found`);n=c.project_scope}let r=ph(),o=new Date().toISOString(),a=qc(s,n);return f().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1182
+ VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,s,n,o,a),qt(),{id:r,name:t,parent_folder_id:s,project_scope:n,created_at:o,archived:!1,sort_order:a}}function qc(e,t){return f().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
881
1183
  FROM thread_folders
882
1184
  WHERE parent_folder_id IS ?
883
- AND project_scope IS ?`).get(e,t)?.next??0}function la(e,t){let s=t.trim();if(!s)throw new Error("folder name cannot be empty");if(s.length>200)throw new Error("folder name too long (200 char max)");let n=$e(e);if(!n)throw new Error(`folder ${e} not found`);return f().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(s,e),Ot(),{...n,name:s}}function da(e,t){let s=$e(e);if(!s)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let n=s.project_scope;if(t!==null){let o=$e(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 d=$e(a);if(!d)break;a=d.parent_folder_id,c++}n=o.project_scope}let r=ca(t,n);return f().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,n,r,e),Ot(),{...s,parent_folder_id:t,project_scope:n,sort_order:r}}function ua(e,t,s){if(!Array.isArray(s)||s.length===0)throw new Error("ordered_ids must be a non-empty array");let n=new Set;for(let u of s){if(typeof u!="string"||!u)throw new Error("ordered_ids must contain non-empty strings");if(n.has(u))throw new Error(`duplicate id in ordered_ids: ${u}`);n.add(u)}let r=f(),o=r.prepare(`SELECT id FROM thread_folders
1185
+ AND project_scope IS ?`).get(e,t)?.next??0}function Xc(e,t){let s=t.trim();if(!s)throw new Error("folder name cannot be empty");if(s.length>200)throw new Error("folder name too long (200 char max)");let n=Ye(e);if(!n)throw new Error(`folder ${e} not found`);return f().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(s,e),qt(),{...n,name:s}}function Jc(e,t){let s=Ye(e);if(!s)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let n=s.project_scope;if(t!==null){let o=Ye(t);if(!o)throw new Error(`parent folder ${t} not found`);let a=o.parent_folder_id,c=0;for(;a!==null&&c<1024;){if(a===e)throw new Error("cannot move a folder into one of its own descendants (cycle)");let u=Ye(a);if(!u)break;a=u.parent_folder_id,c++}n=o.project_scope}let r=qc(t,n);return f().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,n,r,e),qt(),{...s,parent_folder_id:t,project_scope:n,sort_order:r}}function Yc(e,t,s){if(!Array.isArray(s)||s.length===0)throw new Error("ordered_ids must be a non-empty array");let n=new Set;for(let d of s){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(n.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);n.add(d)}let r=f(),o=r.prepare(`SELECT id FROM thread_folders
884
1186
  WHERE parent_folder_id IS ?
885
- AND project_scope IS ?`).all(e,t),a=new Set(o.map(u=>u.id));if(a.size!==s.length)throw new Error(`ordered_ids length ${s.length} does not match sibling count ${a.size}`);for(let u of s)if(!a.has(u))throw new Error(`folder ${u} is not in the named sibling bucket`);let c=r.prepare("UPDATE thread_folders SET sort_order = ? WHERE id = ?");r.transaction(u=>{u.forEach((g,h)=>c.run(h*100,g))})(s),Ot()}function pa(e){if(!$e(e))throw new Error(`folder ${e} not found`);f().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),Ot()}function ma(e,t){if(t!==null&&!$e(t))throw new Error(`folder ${t} not found`);if(f().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}H();Z();import{writeFileSync as ga,readFileSync as zw,existsSync as _a,mkdirSync as fa,readdirSync as Vw}from"node:fs";import{join as Rs}from"node:path";var hg=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),ha=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),Eg=new Set(["pending","approved","rejected"]),bg=new Set(["L1","L2","L3","L4","user"]),zn=Rs($,"links"),Vn=Rs($,"suggestions"),Sg=Rs(Vn,"index.json");function Tg(){J(),_a(zn)||fa(zn,{recursive:!0})}function yg(){J(),_a(Vn)||fa(Vn,{recursive:!0})}function Ea(e){try{return JSON.parse(e)}catch{return e}}function ks(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:Ea(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Zn(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:Ea(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function ba(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function Qn(e){if(!hg.has(e))throw new Error(`invalid link_type: ${e}`)}function wg(e){if(!ha.has(e))throw new Error(`invalid source: ${e}`)}function Sa(e){if(!bg.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Ta(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 ya(e){Ta(e.source_session_id,e.target_session_id),Qn(e.link_type),wg(e.source),ba(e.confidence);let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
886
- (source_session_id, target_session_id, link_type,
887
- confidence, source, evidence, approved,
888
- created_at, updated_at)
889
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
890
- ON CONFLICT(source_session_id, target_session_id, link_type) DO UPDATE SET
891
- confidence = excluded.confidence,
892
- source = excluded.source,
893
- evidence = excluded.evidence,
894
- approved = excluded.approved,
895
- updated_at = excluded.updated_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,e.source,n,r,s,s);let o=t.prepare(`SELECT * FROM session_links
896
- WHERE source_session_id = ?
897
- AND target_session_id = ?
898
- 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 tr(e.source_session_id),ks(o)}function As(e={}){let t=f(),s=[],n=[];e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.linkType&&(Qn(e.linkType),s.push("link_type = ?"),n.push(e.linkType)),e.approvedOnly&&s.push("approved = 1");let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
899
- ORDER BY confidence DESC, updated_at DESC
900
- LIMIT ?`).all(...n,o).map(ks)}function Lt(e){return f().prepare(`SELECT * FROM session_links
901
- WHERE source_session_id = ? OR target_session_id = ?
902
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(ks)}function wa(e){let t=f(),s=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!s)return{removed:0,sourceSessionId:null};let n=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return n.changes>0&&tr(s.source_session_id),{removed:n.changes,sourceSessionId:s.source_session_id}}function Ct(e){Ta(e.source_session_id,e.target_session_id),Qn(e.link_type),ba(e.confidence),Sa(e.inferred_by);let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
903
- (source_session_id, target_session_id, link_type,
904
- confidence, evidence, status, inferred_by,
905
- created_at, decided_at)
906
- VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, NULL)
907
- ON CONFLICT(source_session_id, target_session_id, link_type, inferred_by) DO UPDATE SET
908
- confidence = CASE
909
- WHEN session_link_suggestions.status = 'pending'
910
- THEN excluded.confidence
911
- ELSE session_link_suggestions.confidence
912
- END,
913
- evidence = CASE
914
- WHEN session_link_suggestions.status = 'pending'
915
- THEN excluded.evidence
916
- ELSE session_link_suggestions.evidence
917
- END,
918
- created_at = session_link_suggestions.created_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,n,e.inferred_by,s);let r=t.prepare(`SELECT * FROM session_link_suggestions
919
- WHERE source_session_id = ?
920
- AND target_session_id = ?
921
- AND link_type = ?
922
- 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 Ra(),Zn(r)}function Ve(e={}){let t=f(),s=[],n=[];if(e.status){if(!Eg.has(e.status))throw new Error(`invalid status: ${e.status}`);s.push("status = ?"),n.push(e.status)}e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.inferredBy&&(Sa(e.inferredBy),s.push("inferred_by = ?"),n.push(e.inferredBy));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
923
- ORDER BY confidence DESC, created_at DESC
924
- LIMIT ?`).all(...n,o).map(Zn)}function er(e,t,s={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let n=s.source??"manual";if(!ha.has(n))throw new Error(`invalid source: ${n}`);let r=f(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let a=new Date().toISOString(),c;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
925
- SET status = ?, decided_at = ?
926
- WHERE id = ?`).run(t,a,e),t==="approved"&&(r.prepare(`INSERT INTO session_links
927
- (source_session_id, target_session_id, link_type,
928
- confidence, source, evidence, approved,
929
- created_at, updated_at)
930
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)
931
- ON CONFLICT(source_session_id, target_session_id, link_type) DO UPDATE SET
932
- confidence = excluded.confidence,
933
- source = excluded.source,
934
- evidence = excluded.evidence,
935
- approved = 1,
936
- updated_at = excluded.updated_at`).run(o.source_session_id,o.target_session_id,o.link_type,o.confidence,n,o.evidence,a,a),c=r.prepare(`SELECT * FROM session_links
937
- WHERE source_session_id = ?
938
- AND target_session_id = ?
939
- AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),Ra(),t==="approved"&&tr(o.source_session_id);let d=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:Zn(d),link:c?ks(c):null}}function tr(e){try{Tg();let t=As({sourceSessionId:e}),s=Rs(zn,`${e}.json`);if(t.length===0)return;let n={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};ga(s,JSON.stringify(n,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function Ra(){try{yg();let e=Ve({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};ga(Sg,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}function ka(e,t){let s=new Set,n=[];for(let r of t){if(s.has(r.id))continue;let o=null,a="";if(r.source_session_id===e)o="outbound",a=r.target_session_id;else if(r.target_session_id===e)o="inbound",a=r.source_session_id;else continue;s.add(r.id),n.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return n.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),n}H();Z();import{writeFileSync as Rg,readFileSync as r0,existsSync as kg,mkdirSync as Ag,readdirSync as o0}from"node:fs";import{join as Aa}from"node:path";var sr=Aa($,"output-index");function xg(){J(),kg(sr)||Ag(sr,{recursive:!0})}function It(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Ng(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function xa(e){return{session_id:e.session_id,files_written:It(e.files_written),brands_mentioned:It(e.brands_mentioned),terms_introduced:It(e.terms_introduced),plan_ids_referenced:It(e.plan_ids_referenced),bug_signatures:It(e.bug_signatures),raw_extraction:Ng(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function Na(e){if(!e.session_id)throw new Error("session_id is required");let t=f(),s=new Date().toISOString(),n=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),a=JSON.stringify(e.plan_ids_referenced??[]),c=JSON.stringify(e.bug_signatures??[]),d=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),u=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
940
- (session_id, files_written, brands_mentioned, terms_introduced,
941
- plan_ids_referenced, bug_signatures, raw_extraction,
942
- extracted_at, extractor_version)
943
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
944
- ON CONFLICT(session_id) DO UPDATE SET
945
- files_written = excluded.files_written,
946
- brands_mentioned = excluded.brands_mentioned,
947
- terms_introduced = excluded.terms_introduced,
948
- plan_ids_referenced = excluded.plan_ids_referenced,
949
- bug_signatures = excluded.bug_signatures,
950
- raw_extraction = excluded.raw_extraction,
951
- extracted_at = excluded.extracted_at,
952
- extractor_version = excluded.extractor_version`).run(e.session_id,n,r,o,a,c,d,s,u);let g=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!g)throw new Error("setOutputIndex succeeded but read-back failed");let h=xa(g);return Og(e.session_id),h}function Be(e){let s=f().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return s?xa(s):null}function Og(e){try{xg();let t=Be(e);if(!t)return;let s=Aa(sr,`${e}.json`),n={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};Rg(s,JSON.stringify(n,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}H();var Lg=4e3,Cg=2,Ig=30,vg=.2,jg={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function xs(e){return e?Math.ceil(e.length/4):0}function Oa(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Ig);return Math.max(vg,t)}function La(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function Ca(e){return f().prepare(`SELECT s.id,
1187
+ AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==s.length)throw new Error(`ordered_ids length ${s.length} does not match sibling count ${a.size}`);for(let d of s)if(!a.has(d))throw new Error(`folder ${d} is not in the named sibling bucket`);let c=r.prepare("UPDATE thread_folders SET sort_order = ? WHERE id = ?");r.transaction(d=>{d.forEach((m,h)=>c.run(h*100,m))})(s),qt()}function Gc(e){if(!Ye(e))throw new Error(`folder ${e} not found`);f().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),qt()}function zc(e,t){if(t!==null&&!Ye(t))throw new Error(`folder ${t} not found`);if(f().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function Kc(e,t){let s=new Set,n=[];for(let r of t){if(s.has(r.id))continue;let o=null,a="";if(r.source_session_id===e)o="outbound",a=r.target_session_id;else if(r.target_session_id===e)o="inbound",a=r.source_session_id;else continue;s.add(r.id),n.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return n.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),n}H();var fh=4e3,hh=2,Eh=30,bh=.2,Sh={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function Zs(e){return e?Math.ceil(e.length/4):0}function Vc(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Eh);return Math.max(bh,t)}function Zc(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function Qc(e){return f().prepare(`SELECT s.id,
953
1188
  NULLIF(sa.alias, '') AS alias,
954
1189
  s.auto_title,
955
1190
  s.auto_title_source,
@@ -960,64 +1195,25 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
960
1195
  FROM sessions s
961
1196
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
962
1197
  LEFT JOIN projects p ON p.id = s.project_id
963
- WHERE s.id = ?`).get(e)??null}function Ia(e){let s=f().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!s||!s.summary)return null;let n=s.summary.trim();return n.length>0?n:null}function va(e){let t=e.alias?.trim(),s=e.auto_title?.trim(),n=e.first_user_message?.trim();return s&&e.auto_title_source==="agent"?s:t||s||(n?n.slice(0,80):e.id.slice(0,8))}function Mg(e){let s=f().prepare(`SELECT id, auto_title, started_at
1198
+ WHERE s.id = ?`).get(e)??null}function el(e){let s=f().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!s||!s.summary)return null;let n=s.summary.trim();return n.length>0?n:null}function tl(e){let t=e.alias?.trim(),s=e.auto_title?.trim(),n=e.first_user_message?.trim();return s&&e.auto_title_source==="agent"?s:t||s||(n?n.slice(0,80):e.id.slice(0,8))}function Th(e){let s=f().prepare(`SELECT id, auto_title, started_at
964
1199
  FROM sessions
965
1200
  WHERE project_id = ?
966
- ORDER BY COALESCE(started_at, ''), id`).all(e),n=new Set,r=new Set,o=[];for(let b of s){if(!b.auto_title||!b.auto_title.startsWith("/")){o.push({id:b.id,brand:null,skill:null});continue}let S=b.auto_title.split(" \xB7 "),y=S[0].trim(),k=S.length>1?S.slice(1).join(" \xB7 ").trim():null;o.push({id:b.id,brand:k||null,skill:y||null}),k&&n.add(k),y&&r.add(y)}let a=[...n].sort(),c=new Map;a.forEach((b,S)=>c.set(b,S));let d=[...r].sort(),u=new Map;d.forEach((b,S)=>u.set(b,S));let g=new Map,h=new Map;for(let b of o){if(!b.brand||!b.skill)continue;let S=c.get(b.brand),y=u.get(b.skill);if(S===void 0||y===void 0)continue;let k=`${S}.${y}`,w=(g.get(k)??0)+1;g.set(k,w),h.set(b.id,`${S}.${y}.${w}`)}return{byId:h}}function Dg(e){return{table:e!==null?Mg(e):null,originProjectId:e,cache:new Map}}function Ns(e,t){let s=e.cache.get(t);if(s)return s;let n=Ca(t);if(!n)return null;let r=e.table&&n.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:n.id,title:va(n),decimal:r,summary:Ia(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,o),o}function Fg(e,t){let n=f().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1201
+ ORDER BY COALESCE(started_at, ''), id`).all(e),n=new Set,r=new Set,o=[];for(let b of s){if(!b.auto_title||!b.auto_title.startsWith("/")){o.push({id:b.id,brand:null,skill:null});continue}let T=b.auto_title.split(" \xB7 "),S=T[0].trim(),w=T.length>1?T.slice(1).join(" \xB7 ").trim():null;o.push({id:b.id,brand:w||null,skill:S||null}),w&&n.add(w),S&&r.add(S)}let a=[...n].sort(),c=new Map;a.forEach((b,T)=>c.set(b,T));let u=[...r].sort(),d=new Map;u.forEach((b,T)=>d.set(b,T));let m=new Map,h=new Map;for(let b of o){if(!b.brand||!b.skill)continue;let T=c.get(b.brand),S=d.get(b.skill);if(T===void 0||S===void 0)continue;let w=`${T}.${S}`,R=(m.get(w)??0)+1;m.set(w,R),h.set(b.id,`${T}.${S}.${R}`)}return{byId:h}}function yh(e){return{table:e!==null?Th(e):null,originProjectId:e,cache:new Map}}function Qs(e,t){let s=e.cache.get(t);if(s)return s;let n=Qc(t);if(!n)return null;let r=e.table&&n.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:n.id,title:tl(n),decimal:r,summary:el(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,o),o}function wh(e,t){let n=f().prepare(`SELECT DISTINCT te.parent_session_id AS pid
967
1202
  FROM thread_edges te
968
1203
  WHERE te.session_id = ?
969
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of n){if(!o.pid)continue;let a=Ns(e,o.pid);a&&r.push(a)}return r}function Pg(e,t){let n=f().prepare(`SELECT DISTINCT te.session_id AS sid
1204
+ AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of n){if(!o.pid)continue;let a=Qs(e,o.pid);a&&r.push(a)}return r}function Rh(e,t){let n=f().prepare(`SELECT DISTINCT te.session_id AS sid
970
1205
  FROM thread_edges te
971
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of n){if(!o.sid)continue;let a=Ns(e,o.sid);a&&r.push(a)}return r}function ja(e){let t=jg[e.linkType]??.5,s=Ze(e.confidence),n=t*s,r=Oa(e.daysApart),o=e.embeddingCosine??.5,a=Ze(e.pagerank);if(e.scoring==="pagerank")return Ze(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Ze(n):Ze(o);let c=.35*n+.2*r+.2*o+.25*a;return Ze(c)}function Ze(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function Ug(e,t,s,n,r){let o=new Map;function a(c,d){if(c===d)return;let u=o.get(c);u||(u=new Set,o.set(c,u)),u.add(d)}for(let c of t)a(c.source_session_id,c.target_session_id),a(c.target_session_id,c.source_session_id);for(let c of s)a(e,c.session_id);for(let c of s)a(c.session_id,e);for(let c of n)a(e,c.session_id);for(let c of n)a(c.session_id,e);if(r>1){let c=new Set([e]),d=new Set([e]);for(let u=1;u<r;u++){let g=new Set;for(let h of c){let b=o.get(h);if(b)for(let S of b){if(d.has(S))continue;let y=Lt(S).filter(k=>k.approved);for(let k of y)a(k.source_session_id,k.target_session_id),a(k.target_session_id,k.source_session_id);d.add(S),g.add(S)}}if(g.size===0)break;for(let h of g)c.add(h)}}return{edges:o}}function $g(e,t={}){let s=t.iterations??12,n=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,a=new Map(r.map(u=>[u,o]));for(let u=0;u<s;u++){let g=new Map(r.map(h=>[h,(1-n)/r.length]));for(let h of r){let b=e.edges.get(h);if(!b||b.size===0)continue;let S=(a.get(h)??0)/b.size;for(let y of b)g.set(y,(g.get(y)??0)+n*S)}a=g}let c=0;for(let u of a.values())u>c&&(c=u);if(c<=0)return a;let d=new Map;for(let[u,g]of a)d.set(u,g/c);return d}var Ma=240;function Da(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function Bg(e){let t=e.decimal?`${e.decimal} `:"",s=e.session_id.slice(0,8),n="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${s})${n}`;if(e.summary){let o=Da(e.summary,Ma);return`${r}
972
- ${o}`}return r}function Hg(e,t,s){let n=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(n.push(c),o+=xs(c),e.summary){let d=Da(e.summary,Ma*4);n.push(d),o+=xs(d)}n.push("");for(let d of t){if(d.refs.length===0)continue;let u=`## ${d.heading}`,g=xs(u),h=[],b=0;for(let S of d.refs){let y=Bg(S),k=xs(y);if(o+g+b+k>s){r.push({session_id:S.session_id,title:S.title,decimal:S.decimal,summary:S.summary,project:S.project,started_at:S.started_at});continue}h.push(y),b+=k}if(h.length>0){n.push(u);for(let S of h)n.push(S);n.push(""),o+=g+b}}for(;n.length>0&&n[n.length-1]==="";)n.pop();return{bundle:n.join(`
1206
+ WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of n){if(!o.sid)continue;let a=Qs(e,o.sid);a&&r.push(a)}return r}function sl(e){let t=Sh[e.linkType]??.5,s=it(e.confidence),n=t*s,r=Vc(e.daysApart),o=e.embeddingCosine??.5,a=it(e.pagerank);if(e.scoring==="pagerank")return it(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?it(n):it(o);let c=.35*n+.2*r+.2*o+.25*a;return it(c)}function it(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function Ah(e,t,s,n,r){let o=new Map;function a(c,u){if(c===u)return;let d=o.get(c);d||(d=new Set,o.set(c,d)),d.add(u)}for(let c of t)a(c.source_session_id,c.target_session_id),a(c.target_session_id,c.source_session_id);for(let c of s)a(e,c.session_id);for(let c of s)a(c.session_id,e);for(let c of n)a(e,c.session_id);for(let c of n)a(c.session_id,e);if(r>1){let c=new Set([e]),u=new Set([e]);for(let d=1;d<r;d++){let m=new Set;for(let h of c){let b=o.get(h);if(b)for(let T of b){if(u.has(T))continue;let S=vt(T).filter(w=>w.approved);for(let w of S)a(w.source_session_id,w.target_session_id),a(w.target_session_id,w.source_session_id);u.add(T),m.add(T)}}if(m.size===0)break;for(let h of m)c.add(h)}}return{edges:o}}function kh(e,t={}){let s=t.iterations??12,n=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,a=new Map(r.map(d=>[d,o]));for(let d=0;d<s;d++){let m=new Map(r.map(h=>[h,(1-n)/r.length]));for(let h of r){let b=e.edges.get(h);if(!b||b.size===0)continue;let T=(a.get(h)??0)/b.size;for(let S of b)m.set(S,(m.get(S)??0)+n*T)}a=m}let c=0;for(let d of a.values())d>c&&(c=d);if(c<=0)return a;let u=new Map;for(let[d,m]of a)u.set(d,m/c);return u}var nl=240;function rl(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function xh(e){let t=e.decimal?`${e.decimal} `:"",s=e.session_id.slice(0,8),n="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${s})${n}`;if(e.summary){let o=rl(e.summary,nl);return`${r}
1207
+ ${o}`}return r}function Nh(e,t,s){let n=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(n.push(c),o+=Zs(c),e.summary){let u=rl(e.summary,nl*4);n.push(u),o+=Zs(u)}n.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,m=Zs(d),h=[],b=0;for(let T of u.refs){let S=xh(T),w=Zs(S);if(o+m+b+w>s){r.push({session_id:T.session_id,title:T.title,decimal:T.decimal,summary:T.summary,project:T.project,started_at:T.started_at});continue}h.push(S),b+=w}if(h.length>0){n.push(d);for(let T of h)n.push(T);n.push(""),o+=m+b}}for(;n.length>0&&n[n.length-1]==="";)n.pop();return{bundle:n.join(`
973
1208
  `)+`
974
- `,budgetUsed:o,truncated:r}}function Wg(e,t,s,n,r,o){let a=[];for(let c of s){if(n&&!n.has(c.link_type))continue;let d=null;if(c.source_session_id===t.session_id?d=c.target_session_id:c.target_session_id===t.session_id&&(d=c.source_session_id),!d)continue;let u=Ns(e,d);if(!u)continue;let g=La(t.started_at,u.started_at),h=ja({confidence:c.confidence,linkType:c.link_type,daysApart:g,embeddingCosine:null,pagerank:o.get(d)??0,scoring:r});a.push({...u,score:h,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(g)}d apart`,link_type:c.link_type})}return a}function Os(e,t={}){let s=Math.max(100,Math.floor(t.budget??Lg)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Cg)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,d=Ca(e);if(!d)throw new Error(`session not found: ${e}`);let u=Dg(d.project_id),g={session_id:d.id,title:va(d),decimal:u.table?.byId.get(d.id)??null,summary:Ia(d.id),project:d.project,started_at:d.started_at};u.cache.set(d.id,g);let h=Fg(u,e),b=Pg(u,e),S=Lt(e).filter(i=>i.approved).filter(i=>!c||c.has(i.link_type)).filter(i=>o||i.link_type!=="wiki_link"),y=Ug(e,S,h,b,r),k=$g(y),w=[],D=[],L=[],X=[];for(let i of S){let l=i.source_session_id===e?i.target_session_id:i.source_session_id,p=Ns(u,l);if(!p)continue;let m=La(g.started_at,p.started_at),_=ja({confidence:i.confidence,linkType:i.link_type,daysApart:m,embeddingCosine:null,pagerank:k.get(l)??0,scoring:n}),E=Oa(m),T=`${i.link_type} confidence=${i.confidence.toFixed(2)} recency=${E.toFixed(2)} (${Math.round(m)}d apart)`,R={...p,score:_,evidence:T,link_type:i.link_type};i.link_type==="citation"?w.push(R):i.link_type==="similar"?D.push(R):i.link_type==="wiki_link"?X.push(R):L.push(R)}if(a){let i=Ve({sourceSessionId:e,status:"pending",limit:100}),l=Ve({targetSessionId:e,status:"pending",limit:100}),p=[...i,...l],m=new Set,_=p.filter(T=>m.has(T.id)?!1:(m.add(T.id),!0)),E=Wg(u,g,_,c,n,k);for(let T of E)T.link_type==="citation"?w.push(T):T.link_type==="similar"?D.push(T):T.link_type==="wiki_link"?X.push(T):L.push(T)}let M=(i,l)=>l.score-i.score;w.sort(M),D.sort(M),L.sort(M),X.sort(M);let se=Hg(g,[{heading:"Parents",refs:h},{heading:"Children",refs:b},{heading:"Citations (approved)",refs:w},{heading:"Similar sessions",refs:D},{heading:"Cousins (skill track + temporal)",refs:L},{heading:"Wiki links (manual)",refs:X}],s);return{origin:g,parents:h,children:b,citations:w,similar:D,cousins:L,wikiLinks:X,bundle:se.bundle,budgetUsed:se.budgetUsed,budgetRemaining:Math.max(0,s-se.budgetUsed),truncated:se.truncated}}H();Z();import{writeFileSync as qg,readFileSync as g0,existsSync as Jg,mkdirSync as Xg,readdirSync as _0,unlinkSync as f0}from"node:fs";import{join as Fa}from"node:path";import{randomUUID as Pa}from"node:crypto";var nr=Fa($,"bug-patterns");function Gg(){J(),Jg(nr)||Xg(nr,{recursive:!0})}function Oe(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 Ua(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function $a(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=f(),s=new Date().toISOString(),n=e.id??Pa(),r=e.first_seen_at??s,o=e.last_seen_at??s,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
975
- (id, signature_hash, example_message, occurrence_count,
976
- first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
977
- VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(n,e.signature_hash,e.example_message,a.length,r,o);let d=t.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
978
- VALUES (?, ?, ?)
979
- ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let u of a)d.run(n,u,s)})();let c=Ba(n);if(!c)throw new Error("createCluster succeeded but read-back failed");return vt(n),c}function Ba(e){let t=f(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:Oe(s),members:n.map(Ua)}}function Ha(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let s=f();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;s.transaction(()=>{let c=s.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
980
- VALUES (?, ?, ?)
981
- ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of Array.from(new Set(t)))c.run(e,d,r).changes>0&&(o+=1);if(o>0){let d=s.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;s.prepare(`UPDATE bug_pattern_clusters
982
- SET occurrence_count = ?, last_seen_at = ?
983
- WHERE id = ?`).run(d,r,e)}})(),o>0&&vt(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Oe(a),added:o}}function Wa(e={}){let t=f(),s=[],n=[];typeof e.minOccurrenceCount=="number"&&(s.push("c.occurrence_count >= ?"),n.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&s.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(s.push(`EXISTS (
984
- SELECT 1 FROM bug_pattern_members m
985
- JOIN sessions s ON s.id = m.session_id
986
- JOIN projects p ON p.id = s.project_id
987
- WHERE m.cluster_id = c.id AND p.name = ?
988
- )`),n.push(e.project));let r=s.length>0?`WHERE ${s.join(" AND ")}`:"",o=t.prepare(`SELECT COUNT(*) AS n FROM bug_pattern_clusters c ${r}`).get(...n),a=Math.max(1,Math.min(5e3,e.limit??100)),c=Math.max(0,Math.floor(e.offset??0));return{clusters:t.prepare(`SELECT c.* FROM bug_pattern_clusters c ${r}
989
- ORDER BY c.occurrence_count DESC, c.last_seen_at DESC
990
- LIMIT ? OFFSET ?`).all(...n,a,c).map(Oe),total:o.n}}function Yg(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,s=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:s,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function qa(e){let t=f(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
991
- NULLIF(sa.alias, '') AS alias,
992
- s.auto_title,
993
- s.first_user_message,
994
- p.name AS project,
995
- s.started_at
996
- FROM bug_pattern_members m
997
- LEFT JOIN sessions s ON s.id = m.session_id
998
- LEFT JOIN session_aliases sa ON sa.session_id = m.session_id
999
- LEFT JOIN projects p ON p.id = s.project_id
1000
- WHERE m.cluster_id = ?
1001
- ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:Oe(s),members:n.map(Yg)}}function Ja(e,t,s){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof s!="string"||!s.trim())throw new Error("fixSummary is required");let n=f();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!n.prepare("SELECT 1 FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?").get(e,t))throw new Error(`session ${t} is not a member of cluster ${e}`);n.prepare(`UPDATE bug_pattern_clusters
1002
- SET resolved_in_session_id = ?, fix_summary = ?
1003
- WHERE id = ?`).run(t,s.trim(),e),vt(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Oe(a)}}function Xa(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("memberSessionIds must be a non-empty array");let s=f(),n=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=s.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
1004
- WHERE cluster_id = ? AND session_id IN (${o})`).all(e,...r);if(a.length===0)throw new Error(`none of the supplied session_ids are members of cluster ${e}`);let c=s.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;if(a.length>=c)throw new Error(`cannot split: would leave the original cluster empty (move ${a.length} of ${c})`);let d=Pa(),u=new Date().toISOString(),g=[];s.transaction(()=>{s.prepare(`INSERT INTO bug_pattern_clusters
1005
- (id, signature_hash, example_message, occurrence_count,
1006
- first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
1007
- VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(d,n.signature_hash,n.example_message,a.length,n.first_seen_at,u);let S=s.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
1008
- VALUES (?, ?, ?)`),y=s.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let w of a)S.run(d,w.session_id,w.matched_at),y.run(e,w.session_id);let k=c-a.length;s.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(k,e),g=s.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(d)})(),vt(e),vt(d);let h=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),b=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(d);return{original:Oe(h),split:Oe(b),movedMembers:g.map(Ua)}}function vt(e){try{Gg();let t=Ba(e);if(!t)return;let s=Fa(nr,`${e}.json`),n={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};qg(s,JSON.stringify(n,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function Ga(e){return e?f().prepare(`SELECT * FROM bug_pattern_clusters
1009
- WHERE signature_hash = ?
1010
- ORDER BY last_seen_at DESC, id ASC`).all(e).map(Oe):[]}function Ya(e){if(!e)return new Set;let s=f().prepare(`SELECT DISTINCT m.session_id
1011
- FROM bug_pattern_members m
1012
- JOIN bug_pattern_clusters c ON c.id = m.cluster_id
1013
- WHERE c.signature_hash = ?`).all(e);return new Set(s.map(n=>n.session_id))}import{randomUUID as O_}from"node:crypto";H();Z();import{writeFileSync as e_,mkdirSync as t_,existsSync as s_}from"node:fs";import{join as nc}from"node:path";H();var Ka=80;function za(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let s=t.split(`
1014
- `)[0].trim();return s.length>Ka?s.slice(0,Ka)+"\u2026":s}function Kg(e){return f().prepare(`SELECT s.id AS id,
1209
+ `,budgetUsed:o,truncated:r}}function Oh(e,t,s,n,r,o){let a=[];for(let c of s){if(n&&!n.has(c.link_type))continue;let u=null;if(c.source_session_id===t.session_id?u=c.target_session_id:c.target_session_id===t.session_id&&(u=c.source_session_id),!u)continue;let d=Qs(e,u);if(!d)continue;let m=Zc(t.started_at,d.started_at),h=sl({confidence:c.confidence,linkType:c.link_type,daysApart:m,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:h,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(m)}d apart`,link_type:c.link_type})}return a}function en(e,t={}){let s=Math.max(100,Math.floor(t.budget??fh)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??hh)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=Qc(e);if(!u)throw new Error(`session not found: ${e}`);let d=yh(u.project_id),m={session_id:u.id,title:tl(u),decimal:d.table?.byId.get(u.id)??null,summary:el(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,m);let h=wh(d,e),b=Rh(d,e),T=vt(e).filter($=>$.approved).filter($=>!c||c.has($.link_type)).filter($=>o||$.link_type!=="wiki_link"),S=Ah(e,T,h,b,r),w=kh(S),R=[],D=[],O=[],Y=[];for(let $ of T){let se=$.source_session_id===e?$.target_session_id:$.source_session_id,i=Qs(d,se);if(!i)continue;let l=Zc(m.started_at,i.started_at),p=sl({confidence:$.confidence,linkType:$.link_type,daysApart:l,embeddingCosine:null,pagerank:w.get(se)??0,scoring:n}),g=Vc(l),_=`${$.link_type} confidence=${$.confidence.toFixed(2)} recency=${g.toFixed(2)} (${Math.round(l)}d apart)`,E={...i,score:p,evidence:_,link_type:$.link_type};$.link_type==="citation"?R.push(E):$.link_type==="similar"?D.push(E):$.link_type==="wiki_link"?Y.push(E):O.push(E)}if(a){let $=Ke({sourceSessionId:e,status:"pending",limit:100}),se=Ke({targetSessionId:e,status:"pending",limit:100}),i=[...$,...se],l=new Set,p=i.filter(_=>l.has(_.id)?!1:(l.add(_.id),!0)),g=Oh(d,m,p,c,n,w);for(let _ of g)_.link_type==="citation"?R.push(_):_.link_type==="similar"?D.push(_):_.link_type==="wiki_link"?Y.push(_):O.push(_)}let M=($,se)=>se.score-$.score;R.sort(M),D.sort(M),O.sort(M),Y.sort(M);let oe=Nh(m,[{heading:"Parents",refs:h},{heading:"Children",refs:b},{heading:"Citations (approved)",refs:R},{heading:"Similar sessions",refs:D},{heading:"Cousins (skill track + temporal)",refs:O},{heading:"Wiki links (manual)",refs:Y}],s);return{origin:m,parents:h,children:b,citations:R,similar:D,cousins:O,wikiLinks:Y,bundle:oe.bundle,budgetUsed:oe.budgetUsed,budgetRemaining:Math.max(0,s-oe.budgetUsed),truncated:oe.truncated}}import{randomUUID as uE}from"node:crypto";H();Q();import{writeFileSync as Mh,mkdirSync as Dh,existsSync as Fh}from"node:fs";import{join as ml}from"node:path";H();var ol=80;function il(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let s=t.split(`
1210
+ `)[0].trim();return s.length>ol?s.slice(0,ol)+"\u2026":s}function Lh(e){return f().prepare(`SELECT s.id AS id,
1015
1211
  sa.alias AS alias,
1016
1212
  s.auto_title AS auto_title,
1017
1213
  s.first_user_message AS first_user_message
1018
1214
  FROM sessions s
1019
1215
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1020
- WHERE s.id = ?`).get(e)??null}function zg(e){let t=Kg(e);return t?za(t):e.slice(0,8)}function Va(e){if(!e)return null;let t=f(),s=t.prepare(`SELECT e.thread_id AS thread_id,
1216
+ WHERE s.id = ?`).get(e)??null}function Ch(e){let t=Lh(e);return t?il(t):e.slice(0,8)}function al(e){if(!e)return null;let t=f(),s=t.prepare(`SELECT e.thread_id AS thread_id,
1021
1217
  t.name AS thread_name,
1022
1218
  e.parent_session_id AS parent_session_id,
1023
1219
  e.added_at AS added_at
@@ -1026,7 +1222,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1026
1222
  WHERE e.session_id = ?
1027
1223
  AND t.archived = 0
1028
1224
  ORDER BY e.added_at DESC
1029
- LIMIT 1`).get(e);if(!s)return null;let n=s.parent_session_id?{id:s.parent_session_id,title:zg(s.parent_session_id)}:null,r=s.parent_session_id?[e,s.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1225
+ LIMIT 1`).get(e);if(!s)return null;let n=s.parent_session_id?{id:s.parent_session_id,title:Ch(s.parent_session_id)}:null,r=s.parent_session_id?[e,s.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1030
1226
  s.id AS id,
1031
1227
  sa.alias AS alias,
1032
1228
  s.auto_title AS auto_title,
@@ -1036,33 +1232,33 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1036
1232
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1037
1233
  WHERE e.thread_id = ?
1038
1234
  AND e.session_id NOT IN (${o})
1039
- ORDER BY e.added_at ASC`).all(s.thread_id,...r).map(d=>({id:d.session_id,title:d.id?za(d):d.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:c}}var Za=[/^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 rr(e){return Za.some(t=>t.test(e))}var Vg=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i],Zg=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],Qg=20;function jt(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<Qg)return"low_signal";for(let t of Za)if(t.test(e.auto_title))return"recursive_meta";for(let t of Vg)if(t.test(e.auto_title))return"programmatic";for(let t of Zg)if(t.test(e.auto_title))return"template_pending";return"clean"}function Ls(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function Qa(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let s=e.slice(0,t),n=e.slice(t+3),r=Ls(n);return!r||r===n?e:`${s} \xB7 ${r}`}var ar=nc($,"titles"),n_=80,r_=60,o_=100,i_=50,Mt=5,Cs=15,a_=500;function rc(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>!!s&&typeof s=="object"&&typeof s.title=="string"&&typeof s.replaced_at=="string")}catch{}return[]}function et(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let s=c_(t,e);if(s)return s;let n=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(n&&n.length<=n_?n:t.slice(0,r_)).trim()||null}function c_(e,t){let s=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(s){let n=`/${s[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=cr(r);return o?Qe(`${n} \xB7 ${o}`):n}for(let n of l_){if(!e.match(n.match))continue;let o=n.prefix,a=n.extract?n.extract(e,t):cr(t);return a?n.completeFromExtract?Qe(a):Qe(`${o} \xB7 ${a}`):o}for(let n of p_){if(!e.match(n.match))continue;let o=n.extract?n.extract(e,t):Is(t);return o?n.completeFromExtract?Qe(o):Qe(`${n.prefix} \xB7 ${o}`):n.prefix}return null}var l_=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let n=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=cr(t);return n&&r?`${n} \xB7 ${r}`:n||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let s=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return s?s.split("/").filter(Boolean).slice(-2).join("/")||s:null}},{match:/^(?:read|inspect) this and then begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>u_(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let s=t.match(/\.claude\/skills\/([^/\s]+)/);return s?.[1]?`[skill] ${s[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>d_(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>ec(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)=>ec(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let s=t[1],n=t[2]?.trim();return n?`Implementing ${s} \xB7 ${n}`:`Implementing ${s}`}}];function d_(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),s=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),n=t?.[1]?.trim(),r=s?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return n&&o?`[output-index] \xB7 ${n} \xB7 ${o}`:n?`[output-index] \xB7 ${n}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function ec(e,t){if(!e)return t;let s=e.indexOf("Messages:");if(s===-1)return t;let n=e.slice(s+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of n.matchAll(r)){let a=o[1]?.trim()??"";if(!a)continue;let c=a.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!c||/^<local-command-/.test(c))continue;let d=c.length>60?c.slice(0,57)+"\u2026":c;return`${t} \xB7 ${d}`}return t}function u_(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let s=t[1].split("/").filter(Boolean);return(s[s.length-1]??"").replace(/\.[^.]+$/,"")||null}var p_=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>Is(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let n=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=n?`${n} co-pilot`:"co-pilot",o=Is(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>Is(t)}];function Is(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,s;for(;(s=t.exec(e))!==null;){let n=s[2].trim();if(!n||/^(null|none|n\/a|—|-)$/i.test(n))continue;let r=n.replace(/\s+/g," ");return Ls(r)||r}return null}function Qe(e){return e.slice(0,o_).trim()}var m_=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],g_=[/^(?: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 or(e){let t=e.trim();return t.length<3?!0:m_.some(s=>s.test(t))}function __(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return g_.some(s=>s.test(t))}function cr(e){let t=f_(e);return t===null?null:Ls(t)||t}function f_(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,s,n=[];for(;(s=t.exec(e))!==null;){let d=s[2].trim(),u=s[3].trim().replace(/\s+/g," ");if(!or(u)){if(__(d))return u;n.push(u)}}if(n.length>0)return n[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!or(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(s=o.exec(e))!==null;){let d=s[1].trim().replace(/\.(md|txt|json)$/i,""),u=d.replace(/\s*\([^)]*\)\s*$/,"").trim();if(u&&!or(u)&&!/product context|reference/i.test(d))return u}let a=e.replace(/^Context for this run[^:]*:\s*/i,"");if(a!==e){let d=a.split(`
1040
- `).map(u=>u.trim()).find(u=>u.length>=4);if(d)return d.slice(0,60)}let c=e.split(`
1041
- `).map(d=>d.trim()).find(d=>d.length>=4);return c?c.slice(0,60):null}function lr(e){let t=f(),s=t.prepare(`SELECT rowid AS rid, content_text
1235
+ ORDER BY e.added_at ASC`).all(s.thread_id,...r).map(u=>({id:u.session_id,title:u.id?il(u):u.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:c}}var cl=[/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Base directory for this skill:/i,/^You are Claude Code in a fresh terminal/i,/^You are extracting a structured Output Index from a Claude/i];function Wr(e){return cl.some(t=>t.test(e))}var vh=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i],Ih=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],jh=20;function Xt(e){if(e.auto_title_source==="agent"&&e.auto_title)return"agent";if(e.has_alias)return"manual_alias";if(e.auto_title&&e.auto_title.includes(" \xB7 "))return"fixed_v0.16.1";if(!e.auto_title||e.auto_title.length<jh)return"low_signal";for(let t of cl)if(t.test(e.auto_title))return"recursive_meta";for(let t of vh)if(t.test(e.auto_title))return"programmatic";for(let t of Ih)if(t.test(e.auto_title))return"template_pending";return"clean"}function tn(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function ll(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let s=e.slice(0,t),n=e.slice(t+3),r=tn(n);return!r||r===n?e:`${s} \xB7 ${r}`}var Jr=ml(U,"titles"),Ph=80,Uh=60,$h=100,Bh=50,Jt=5,sn=15,Hh=500;function gl(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>!!s&&typeof s=="object"&&typeof s.title=="string"&&typeof s.replaced_at=="string")}catch{}return[]}function ct(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let s=Wh(t,e);if(s)return s;let n=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(n&&n.length<=Ph?n:t.slice(0,Uh)).trim()||null}function Wh(e,t){let s=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(s){let n=`/${s[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=Yr(r);return o?at(`${n} \xB7 ${o}`):n}for(let n of qh){if(!e.match(n.match))continue;let o=n.prefix,a=n.extract?n.extract(e,t):Yr(t);return a?n.completeFromExtract?at(a):at(`${o} \xB7 ${a}`):o}for(let n of Yh){if(!e.match(n.match))continue;let o=n.extract?n.extract(e,t):nn(t);return o?n.completeFromExtract?at(o):at(`${n.prefix} \xB7 ${o}`):n.prefix}return null}var qh=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let n=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=Yr(t);return n&&r?`${n} \xB7 ${r}`:n||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let s=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return s?s.split("/").filter(Boolean).slice(-2).join("/")||s:null}},{match:/^(?:read|inspect) this and then begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>Jh(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let s=t.match(/\.claude\/skills\/([^/\s]+)/);return s?.[1]?`[skill] ${s[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>Xh(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>ul(t,"[meta] auto-title (full-arc)")},{match:/^You will receive the first \d+ user messages from a Claude Code session/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>ul(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let s=t[1],n=t[2]?.trim();return n?`Implementing ${s} \xB7 ${n}`:`Implementing ${s}`}}];function Xh(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),s=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),n=t?.[1]?.trim(),r=s?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return n&&o?`[output-index] \xB7 ${n} \xB7 ${o}`:n?`[output-index] \xB7 ${n}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function ul(e,t){if(!e)return t;let s=e.indexOf("Messages:");if(s===-1)return t;let n=e.slice(s+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of n.matchAll(r)){let a=o[1]?.trim()??"";if(!a)continue;let c=a.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!c||/^<local-command-/.test(c))continue;let u=c.length>60?c.slice(0,57)+"\u2026":c;return`${t} \xB7 ${u}`}return t}function Jh(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let s=t[1].split("/").filter(Boolean);return(s[s.length-1]??"").replace(/\.[^.]+$/,"")||null}var Yh=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>nn(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let n=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=n?`${n} co-pilot`:"co-pilot",o=nn(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>nn(t)}];function nn(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,s;for(;(s=t.exec(e))!==null;){let n=s[2].trim();if(!n||/^(null|none|n\/a|—|-)$/i.test(n))continue;let r=n.replace(/\s+/g," ");return tn(r)||r}return null}function at(e){return e.slice(0,$h).trim()}var Gh=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],zh=[/^(?:brand|brand brief|brand summary)$/i,/^(?:known facts?|facts)$/i,/^(?:client|prospect|customer|account)$/i,/^(?:topic|subject|brief|task)$/i,/^(?:about|for|re)$/i];function qr(e){let t=e.trim();return t.length<3?!0:Gh.some(s=>s.test(t))}function Kh(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return zh.some(s=>s.test(t))}function Yr(e){let t=Vh(e);return t===null?null:tn(t)||t}function Vh(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,s,n=[];for(;(s=t.exec(e))!==null;){let u=s[2].trim(),d=s[3].trim().replace(/\s+/g," ");if(!qr(d)){if(Kh(u))return d;n.push(d)}}if(n.length>0)return n[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!qr(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(s=o.exec(e))!==null;){let u=s[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!qr(d)&&!/product context|reference/i.test(u))return d}let a=e.replace(/^Context for this run[^:]*:\s*/i,"");if(a!==e){let u=a.split(`
1236
+ `).map(d=>d.trim()).find(d=>d.length>=4);if(u)return u.slice(0,60)}let c=e.split(`
1237
+ `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function Gr(e){let t=f(),s=t.prepare(`SELECT rowid AS rid, content_text
1042
1238
  FROM messages
1043
1239
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1044
1240
  AND content_text IS NOT NULL AND content_text != ''
1045
1241
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1046
- LIMIT ?`).all(e,Mt),n=t.prepare(`SELECT rowid AS rid, content_text
1242
+ LIMIT ?`).all(e,Jt),n=t.prepare(`SELECT rowid AS rid, content_text
1047
1243
  FROM messages
1048
1244
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1049
1245
  AND content_text IS NOT NULL AND content_text != ''
1050
1246
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1051
- LIMIT ?`).all(e,Cs),r=new Map;for(let g of s)r.set(g.rid,g.content_text);for(let g of n)r.set(g.rid,g.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let o=Array.from(r.entries()).sort((g,h)=>g[0]-h[0]).map(([,g])=>({content_text:g})),a=s.length===Mt&&n.length===Cs&&r.size===Mt+Cs,c=o.map((g,h)=>{let b=(g.content_text??"").slice(0,a_);return a&&h===Mt?`--- (middle of session omitted) ---
1247
+ LIMIT ?`).all(e,sn),r=new Map;for(let m of s)r.set(m.rid,m.content_text);for(let m of n)r.set(m.rid,m.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let o=Array.from(r.entries()).sort((m,h)=>m[0]-h[0]).map(([,m])=>({content_text:m})),a=s.length===Jt&&n.length===sn&&r.size===Jt+sn,c=o.map((m,h)=>{let b=(m.content_text??"").slice(0,Hh);return a&&h===Jt?`--- (middle of session omitted) ---
1052
1248
  ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1053
- `),d=null;try{d=Va(e)}catch(g){console.error("[autoTitle] thread context resolution failed:",g),d=null}let u=[];return d&&(u.push(h_(d)),u.push("")),u.push(`You will receive a sample of user messages from a Claude Code session: the first ${Mt}`,`messages (initial intent) and the last ${Cs} 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),u.join(`
1054
- `)}var ir=5;function h_(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let s=e.siblings.length;if(s>0){let r=e.siblings.slice(0,ir).map(a=>`"${a.title}"`).join(", "),o=s>ir?`, and ${s-ir} more`:"";t.push(`- Sibling sessions (${s}): ${r}${o}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
1055
- `)}async function oc(e){let t=lr(e),{spawnClaudePrompt:s,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(ge(),Pe));if(!n())throw new Error("claude CLI not found on PATH");let r=await s(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=E_(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,i_)}function E_(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return tc(n)}}catch{}return tc(t)}function tc(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function de(e,t,s){let n=t.trim();if(!n)return;let r=f(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1056
- FROM sessions WHERE id = ?`).get(e);if(!o||s==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===n&&o.auto_title_source===s)return;let a=rc(o.auto_title_history),c=new Date().toISOString();o.auto_title&&o.auto_title_source&&a.push({title:o.auto_title,source:o.auto_title_source,replaced_at:c}),r.prepare(`UPDATE sessions
1249
+ `),u=null;try{u=al(e)}catch(m){console.error("[autoTitle] thread context resolution failed:",m),u=null}let d=[];return u&&(d.push(Zh(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${Jt}`,`messages (initial intent) and the last ${sn} messages (current direction).`,"Write a single descriptive title, max 50 characters, focused on what the user is","currently trying to accomplish. If initial intent and current direction differ, prefer","the current direction. Output ONLY the title, with no quotes and no trailing punctuation.","","Messages:",c),d.join(`
1250
+ `)}var Xr=5;function Zh(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let s=e.siblings.length;if(s>0){let r=e.siblings.slice(0,Xr).map(a=>`"${a.title}"`).join(", "),o=s>Xr?`, and ${s-Xr} more`:"";t.push(`- Sibling sessions (${s}): ${r}${o}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
1251
+ `)}async function _l(e){let t=Gr(e),{spawnClaudePrompt:s,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(he(),We));if(!n())throw new Error("claude CLI not found on PATH");let r=await s(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=Qh(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,Bh)}function Qh(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return dl(n)}}catch{}return dl(t)}function dl(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function me(e,t,s){let n=t.trim();if(!n)return;let r=f(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1252
+ FROM sessions WHERE id = ?`).get(e);if(!o||s==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===n&&o.auto_title_source===s)return;let a=gl(o.auto_title_history),c=new Date().toISOString();o.auto_title&&o.auto_title_source&&a.push({title:o.auto_title,source:o.auto_title_source,replaced_at:c}),r.prepare(`UPDATE sessions
1057
1253
  SET auto_title = ?,
1058
1254
  auto_title_source = ?,
1059
1255
  auto_title_generated_at = ?,
1060
1256
  auto_title_history = ?
1061
- WHERE id = ?`).run(n,s,Date.now(),JSON.stringify(a),e),y_(e,n,s,c)}function ye(e){let t=f().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1062
- 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:rc(t.auto_title_history)}:null}function ic(){let t=f().prepare(`SELECT id, first_user_message
1257
+ WHERE id = ?`).run(n,s,Date.now(),JSON.stringify(a),e),nE(e,n,s,c)}function ke(e){let t=f().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1258
+ FROM sessions WHERE id = ?`).get(e);return t?{auto_title:t.auto_title,auto_title_source:t.auto_title_source??null,auto_title_generated_at:t.auto_title_generated_at,auto_title_history:gl(t.auto_title_history)}:null}function fl(){let t=f().prepare(`SELECT id, first_user_message
1063
1259
  FROM sessions
1064
1260
  WHERE auto_title IS NULL
1065
- AND first_user_message IS NOT NULL`).all(),s=0;for(let n of t){let r=et(n.first_user_message);r&&(de(n.id,r,"heuristic"),s+=1)}return{updated:s}}function ac(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1261
+ AND first_user_message IS NOT NULL`).all(),s=0;for(let n of t){let r=ct(n.first_user_message);r&&(me(n.id,r,"heuristic"),s+=1)}return{updated:s}}function hl(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1066
1262
  SELECT auto_title, project_id
1067
1263
  FROM sessions
1068
1264
  WHERE auto_title IS NOT NULL
@@ -1115,7 +1311,7 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1115
1311
  AND content_text IS NOT NULL
1116
1312
  AND content_text != ''
1117
1313
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1118
- LIMIT 8`),a=0,c=0;for(let d of r){a+=1;let u=o.all(d.id),g=null;for(let h of u){let b=h.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!b||/^<local-command-caveat>/.test(b))continue;let S=et(b);if(S&&!sc(S)){g=S;break}}if(!g){let h=u.map(S=>S.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(S=>S.length>0&&!/^<local-command-caveat>/.test(S)),b=h?et(h):null;b&&sc(b)&&(g=`[vacuous] ${b}`)}g&&(de(d.id,g,"heuristic"),c+=1)}return{scanned:a,updated:c}}function sc(e){let t=e.trim();return!t||/^\*\*Tool result\*\*$/i.test(t)?!0:[/^all right\.?$/i,/^alright\.?$/i,/^inspect this\.?$/i,/^resolve this\.?$/i,/^do it\.?$/i,/^go\.?$/i,/^continue\.?$/i,/^proceed\.?$/i,/^keep going\.?$/i,/^ok\.?$/i,/^okay\.?$/i,/^yes\.?$/i,/^yep\.?$/i].some(n=>n.test(t))}function cc(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1314
+ LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=null;for(let h of d){let b=h.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!b||/^<local-command-caveat>/.test(b))continue;let T=ct(b);if(T&&!pl(T)){m=T;break}}if(!m){let h=d.map(T=>T.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(T=>T.length>0&&!/^<local-command-caveat>/.test(T)),b=h?ct(h):null;b&&pl(b)&&(m=`[vacuous] ${b}`)}m&&(me(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function pl(e){let t=e.trim();return!t||/^\*\*Tool result\*\*$/i.test(t)?!0:[/^all right\.?$/i,/^alright\.?$/i,/^inspect this\.?$/i,/^resolve this\.?$/i,/^do it\.?$/i,/^go\.?$/i,/^continue\.?$/i,/^proceed\.?$/i,/^keep going\.?$/i,/^ok\.?$/i,/^okay\.?$/i,/^yes\.?$/i,/^yep\.?$/i].some(n=>n.test(t))}function El(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1119
1315
  FROM sessions s
1120
1316
  WHERE s.auto_title_source = 'heuristic'${s}
1121
1317
  AND (
@@ -1130,120 +1326,89 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1130
1326
  AND content_text IS NOT NULL
1131
1327
  AND content_text != ''
1132
1328
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1133
- LIMIT 10`),a=0,c=0;for(let d of r){a+=1;let u=o.all(d.id),g=b_(u,d.auto_title);g&&(de(d.id,g,"heuristic"),c+=1)}return{scanned:a,updated:c}}function b_(e,t){for(let s of e){let n=S_(s.content_text);if(!n||rr(n))continue;let r=et(n);if(r){if(r===t)return null;if(!rr(r))return r}}return t.startsWith("[meta]")?null:Qe(`[meta] ${t}`)}function lc(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1329
+ LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=eE(d,u.auto_title);m&&(me(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function eE(e,t){for(let s of e){let n=tE(s.content_text);if(!n||Wr(n))continue;let r=ct(n);if(r){if(r===t)return null;if(!Wr(r))return r}}return t.startsWith("[meta]")?null:at(`[meta] ${t}`)}function bl(e){let t=f(),s=e?.projectId?" AND s.project_id = ?":"",n=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1134
1330
  FROM sessions s
1135
1331
  WHERE s.auto_title_source = 'heuristic'${s}
1136
1332
  AND s.auto_title IS NOT NULL
1137
1333
  AND s.auto_title LIKE '% \xB7 %'
1138
- AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...n),o=0,a=0;for(let c of r){o+=1;let d=Qa(c.auto_title);d!==c.auto_title&&(de(c.id,d,"heuristic"),a+=1)}return{scanned:o,updated:a}}function S_(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 T_(){J(),s_(ar)||t_(ar,{recursive:!0})}function y_(e,t,s,n){try{T_();let r=nc(ar,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
1139
- `;e_(r,o+t+`
1140
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var w_=50;function R_(e){if(e.length===0)return[];let t=[...e].sort((d,u)=>d.added_at<u.added_at?-1:d.added_at>u.added_at?1:0),s=new Map,n=new Set,r=[];for(let d of t)if(d.parent_session_id){let u=s.get(d.parent_session_id)??[];u.push(d),s.set(d.parent_session_id,u)}let o=t.filter(d=>d.role==="origin"),c=[...o.length>0?o:t.filter(d=>!d.parent_session_id)];for(;c.length>0;){let d=c.shift();if(n.has(d.session_id))continue;n.add(d.session_id),r.push(d.session_id);let u=s.get(d.session_id)??[];for(let g of u)n.has(g.session_id)||c.push(g)}for(let d of t)n.has(d.session_id)||(n.add(d.session_id),r.push(d.session_id));return r}function k_(e){let t=lr(e.sessionId),s=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
1334
+ AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...n),o=0,a=0;for(let c of r){o+=1;let u=ll(c.auto_title);u!==c.auto_title&&(me(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function tE(e){return e.replace(/<command-(?:name|message|args|stdout|stderr)>[\s\S]*?<\/command-(?:name|message|args|stdout|stderr)>/g,"").replace(/<local-command-(?:stdout|stderr|caveat)>[\s\S]*?<\/local-command-(?:stdout|stderr|caveat)>/g,"").trim()}function sE(){J(),Fh(Jr)||Dh(Jr,{recursive:!0})}function nE(e,t,s,n){try{sE();let r=ml(Jr,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
1335
+ `;Mh(r,o+t+`
1336
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var rE=50;function oE(e){if(e.length===0)return[];let t=[...e].sort((u,d)=>u.added_at<d.added_at?-1:u.added_at>d.added_at?1:0),s=new Map,n=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=s.get(u.parent_session_id)??[];d.push(u),s.set(u.parent_session_id,d)}let o=t.filter(u=>u.role==="origin"),c=[...o.length>0?o:t.filter(u=>!u.parent_session_id)];for(;c.length>0;){let u=c.shift();if(n.has(u.session_id))continue;n.add(u.session_id),r.push(u.session_id);let d=s.get(u.session_id)??[];for(let m of d)n.has(m.session_id)||c.push(m)}for(let u of t)n.has(u.session_id)||(n.add(u.session_id),r.push(u.session_id));return r}function iE(e){let t=Gr(e.sessionId),s=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
1141
1337
  `);return`${t}
1142
- ${s}`}function A_(e){return ye(e)?.auto_title_source??null}async function x_(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(ge(),Pe));if(!s())throw new Error("claude CLI not found on PATH");let n=await t(e.prompt,[],{model:e.model});if(!n.success)throw new Error(`claude CLI exited ${n.exitCode}: ${n.stderr.slice(-500)}`);let r=N_(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,w_)}function N_(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return dc(n)}}catch{}return dc(t)}function dc(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function pc(e,t={}){let s=te(e);if(!s)throw new Error(`thread not found: ${e}`);let n=R_(s.edges),r=n.length,o=t.force??!1,a=t.signal,c=[],d=[],u=[];for(let g=0;g<n.length;g++){let h=n[g],b=g+1;if(a?.aborted){let y={sessionId:h,reason:"cancelled"};d.push(y),t.onSkipped?.(y);continue}if(!o&&A_(h)==="agent"){let y={sessionId:h,reason:"already-titled"};d.push(y),t.onSkipped?.(y);continue}let S;try{if(uc)S=await uc({sessionId:h,current:b,total:r});else{let y=k_({sessionId:h,current:b,total:r});S=await x_({prompt:y,model:t.model})}}catch(y){let k=y instanceof Error?y.message:String(y??"unknown error"),w={sessionId:h,error:k};u.push(w),t.onFailed?.(w);continue}try{de(h,S,"agent")}catch(y){let k=y instanceof Error?y.message:String(y??"unknown error"),w={sessionId:h,error:`setAutoTitle failed: ${k}`};u.push(w),t.onFailed?.(w);continue}c.push(h),t.onProgress?.({current:b,total:r,sessionId:h,title:S})}return{generated:c,skipped:d,failed:u}}var uc=null;var Ft=new Map,L_=300*1e3;function Dt(e,t,s){let n=e.events.length+1;e.events.push({id:n,kind:t,data:s});for(let r of e.waiters)r.resolve();e.waiters.clear()}function C_(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{Ft.delete(e.jobId)},L_)}function I_(e){let t=0,s=0,n=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(s+=1),r.kind==="error"&&(n+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:s,failed:n,result:e.result}}function mc(e){let t=O_(),s=new AbortController,n={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:s,result:null,cleanupTimer:null};return Ft.set(t,n),(async()=>{await Promise.resolve();try{let r=await pc(e.threadId,{force:e.force??!1,signal:s.signal,model:e.model,onProgress:o=>{n.total=o.total,Dt(n,"progress",o)},onSkipped:o=>{Dt(n,"skipped",o)},onFailed:o=>{Dt(n,"error",o)}});n.result=r,n.status=s.signal.aborted?"cancelled":"done",n.endedAt=new Date().toISOString(),Dt(n,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");n.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},n.status="failed",n.endedAt=new Date().toISOString(),Dt(n,"done",n.result)}finally{C_(n)}})(),t}async function*gc(e,t=0){let s=Ft.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(n+=1,yield r,r.kind==="done")return}if(s.endedAt)return;await new Promise(r=>{s.waiters.add({resolve:r})})}}function _c(e){let t=Ft.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function dr(e){let t=Ft.get(e);return t?I_(t):null}Z();import{existsSync as v_,readFileSync as j_,writeFileSync as M_}from"node:fs";import{join as D_}from"node:path";import{z as ae}from"zod";var ur=D_($,"terminals.json"),fc=1440*60*1e3,F_=3e4,P_=6e4,U_=ae.object({shell_pid:ae.number(),tab_name:ae.string(),cwd:ae.string().nullable().optional(),opened_at:ae.string(),last_seen_at:ae.string()}),$_=ae.object({schema:ae.string().optional(),saved_at:ae.string().optional(),terminals:ae.array(U_).max(500).default([]),sessions_by_pid:ae.record(ae.string(),ae.array(ae.string()).max(50)).optional().default({})}),hc=/^[⠀-⣿✳\s]+/,Ec=/^\d+(\.\d+){1,3}$/;function ne(e){let t=e.trim();return!!(!t||hc.test(t)||Ec.test(t))}function tt(e){let t=e.trim();if(!t||Ec.test(t))return null;let s=t.replace(hc,"").trim();return s.length>0?s:null}function pr(e,t){if(!ne(e))return e;let s=tt(e);return s||(t&&!ne(t)?t:e)}function B_(e){let t=e.now-e.withinMs,s=e.pending.filter(n=>{let r=Date.parse(n.started_at);return Number.isFinite(r)&&r>=t});if(s.length===0)return{kind:"none"};if(e.shellPid!=null){let n=s.find(r=>r.shell_pid===e.shellPid);if(n)return{kind:"pid-match",entry:n}}if(e.cwd){let n=e.cwd.replace(/\/+$/,""),r=s.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===n);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var mr=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,!!v_(ur)))try{let t=j_(ur,"utf8"),s=JSON.parse(t),n=$_.safeParse(s);if(!n.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",n.error.issues);return}let r=n.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,a]of Object.entries(r.sessions_by_pid??{})){let c=Number(o);if(!Number.isFinite(c))continue;let d=new Set;for(let u of a)u.length>0&&d.add(u);d.size>0&&this.sessionsByPid.set(c,d)}this.gc()}catch{}}save(){try{J();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([s,n])=>[String(s),Array.from(n)]))};M_(ur,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=pr(t.tab_name,n?.tab_name),o=n?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:s};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,s){this.ensureLoaded();let n=this.entries.get(t);if(!n)return null;let r=pr(s,n.tab_name),o={...n,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let s=this.entries.delete(t),n=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(s||n)&&this.save(),s}claimPidOwnership(t,s,n=Date.now()){if(this.ensureLoaded(),!s)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===s?(r.last_claim_at=n,"refreshed"):n-r.last_claim_at>P_?(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,s])=>({shell_pid:t,instance_id:s.instance_id,last_claim_at:s.last_claim_at}))}sync(t){this.ensureLoaded();let s=new Date().toISOString(),n=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=pr(o.tab_name,a?.tab_name),d=a?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:c,opened_at:d,last_seen_at:s}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:n++}return this.gc(),this.save(),{added:n,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,s){this.ensureLoaded();let n=this.sessionsByPid.get(s);n||(n=new Set,this.sessionsByPid.set(s,n)),n.has(t)||(n.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let s of this.sessionsByPid.values())if(s.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let s=!1;for(let[n,r]of this.sessionsByPid)r.delete(t)&&(s=!0,r.size===0&&this.sessionsByPid.delete(n));return s&&this.save(),s}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let s=B_({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(s.kind==="pid-match"||s.kind==="singleton-cwd"){let n=this.pendingClaudeStarts.indexOf(s.entry);n>=0&&this.pendingClaudeStarts.splice(n,1)}return s}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,s,n,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:s,queued_at:Date.now(),cwd:n,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,s])=>({session_id:t,...s}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[s,n]of this.deferredLinks)n.queued_at<t&&this.deferredLinks.delete(s)}gcPending(){let t=Date.now()-F_;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(s=>{let n=Date.parse(s.started_at);return Number.isFinite(n)&&n>=t}))}outputTails=new Map;setOutputTail(t,s,n){this.ensureLoaded(),this.outputTails.set(t,{text:s,captured_at:n})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,s){this.ensureLoaded(),this.origins.set(t,s),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-fc;for(let[s,n]of this.entries){let r=Date.parse(n.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(s)}}gcOrigins(){let t=Date.now()-fc;for(let[s,n]of this.origins)n.detectedAt<t&&this.origins.delete(s)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let n=Date.now()-t,r=0,o=0;for(let[a,c]of this.sessionsByPid){let d=this.entries.get(a);if(d){let u=Date.parse(d.last_seen_at);if(Number.isFinite(u)&&u>=n)continue}o+=c.size,this.sessionsByPid.delete(a),d&&(this.entries.delete(a),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,s=0;for(let n of[...this.entries.keys()]){let r=!0;try{process.kill(n,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(n),t++;let o=this.sessionsByPid.get(n);o&&(s+=o.size,this.sessionsByPid.delete(n)),this.outputTails.delete(n),this.pidOwnership.delete(n)}return(t||s)&&this.save(),{pruned_pids:t,pruned_sessions:s}}},I=new mr;import{execFile as V_}from"node:child_process";import{promisify as Z_}from"node:util";import{existsSync as Q_}from"node:fs";import{basename as ef}from"node:path";H();import{execFile as H_}from"node:child_process";import{readFile as W_}from"node:fs/promises";import{promisify as q_}from"node:util";var J_=q_(H_),bc=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function X_(e){let t={};for(let s of bc){let n=e[s];typeof n=="string"&&n.length>0&&(t[s]=n)}return t}function G_(e){let t=Date.now();if(e.CURSOR_TRACE_ID)return{editor:"cursor",label:"Cursor",detectedAt:t};if(e.VSCODE_PID||e.VSCODE_INJECTION)return{editor:"vscode",label:"VS Code",detectedAt:t};let s=e.TERM_PROGRAM;return s==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:s==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:s==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:s==="WezTerm"?{editor:"wezterm",label:"WezTerm",detectedAt:t}:e.WT_SESSION?{editor:"windows-terminal",label:"Windows Terminal",detectedAt:t}:e.KITTY_WINDOW_ID?{editor:"kitty",label:"Kitty",detectedAt:t}:e.TERM==="alacritty"||e.ALACRITTY_SOCKET?{editor:"alacritty",label:"Alacritty",detectedAt:t}:null}async function Y_(e){if(process.platform==="linux")try{let t=await W_(`/proc/${e}/environ`,"utf8");return K_(t)}catch{return{}}try{let{stdout:t}=await J_("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return z_(t)}catch{return{}}}function K_(e){let t={};for(let s of e.split("\0")){if(!s)continue;let n=s.indexOf("=");if(n<=0)continue;let r=s.slice(0,n),o=s.slice(n+1);t[r]=o}return t}function z_(e){let t={},s=new Set(bc),n=e.replace(/\s+/g," ").trim().split(" ");for(let r of n){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);s.has(a)&&(t[a]=r.slice(o+1))}return t}async function Sc(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await Y_(e),s=X_(t);return G_(s)}catch{return null}}var js=Z_(V_),vs;function tf(){if(vs!==void 0)return vs;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(Q_(t))return vs=t,t;return vs=null,null}async function sf(e){let t=tf();if(!t)return null;try{let{stdout:s}=await js(t,["-Fpc",e],{timeout:2e3}),n=s.split(`
1143
- `),r=null,o=null,a=null;for(let c of n)c.startsWith("p")?(a=Number(c.slice(1)),Number.isFinite(a)&&a>0?r==null&&(r=a):a=null):c.startsWith("c")&&a!=null&&c.slice(1).trim()==="claude"&&o==null&&(o=a);return o??r}catch{return null}}async function Tc(e){try{let{stdout:t}=await js("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),s=Number(t.trim());return Number.isFinite(s)&&s>0?s:null}catch{return null}}async function nf(e){try{let{stdout:t}=await js("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),s=Date.parse(t.trim());return Number.isFinite(s)?s:null}catch{return null}}var rf=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 re(e){let t=e.trim().toLowerCase();if(!t)return!0;let s=t.replace(/^[-/]+/,"").replace(/^.*\//,"").replace(/\s*\(\d+\)\s*$/,"").trim();return rf.has(s)}function st(e){let t=e.tabName?.trim();return t&&!re(t)&&!ne(t)?t:null}function of(e){try{return f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function Ms(e){let t=ef(e,".jsonl");if(Te(t))return;let s=of(t),n=await sf(e),r=n?await Tc(n):null,o=n?await Sc(n):null;o&&I.setOrigin(t,o);let a=null,c=null,d=null,u=I.takePendingMatched({shellPid:r,cwd:s?.cwd??null,withinMs:3e4});if(u.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${u.candidates.length} candidates in ${s?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`);return}if(u.kind==="pid-match"||u.kind==="singleton-cwd"){let y=u.entry;c=y.shell_pid,d=u.kind==="pid-match"?"pending-pid":"pending-cwd",a=I.get(y.shell_pid)??{shell_pid:y.shell_pid,tab_name:y.tab_name,cwd:y.cwd,opened_at:y.started_at,last_seen_at:y.started_at}}let g=null;if(!a&&n){let y=n;for(let k=0;k<4&&y!=null;k++){let w=await Tc(y);if(!w)break;let D=I.get(w);if(D){a=D,c=w,d="ppid";break}g==null&&(g=w),y=w}}if(!a&&s?.cwd){let y=s.cwd.replace(/\/+$/,""),k=I.all().filter(w=>w.cwd&&w.cwd.replace(/\/+$/,"")===y);k.length===1?(a=k[0],c=a.shell_pid,d="cwd"):k.length>=2&&console.log(`[correlator] ${k.length} registered terminals in ${y} for ${t.slice(0,8)} \u2014 refusing to guess; heuristic title will display`)}let h=null;if(a?.tab_name&&!re(a.tab_name)&&!ne(a.tab_name))h=a.tab_name;else if(a?.tab_name&&ne(a.tab_name)){let y=tt(a.tab_name);y&&!re(y)&&(h=y)}if(!h&&!(d==="pending-pid"||d==="pending-cwd")&&a&&s?.cwd){let y=s.cwd.replace(/\/+$/,""),k=I.all().filter(w=>w.shell_pid!==c&&w.cwd&&w.cwd.replace(/\/+$/,"")===y&&!re(w.tab_name)&&!ne(w.tab_name)).sort((w,D)=>Date.parse(D.last_seen_at)-Date.parse(w.last_seen_at))[0];k&&(h=k.tab_name)}let S=st({tabName:h,origin:o,cwd:s?.cwd??null,gitBranch:s?.git_branch??null});if(g!=null&&!c&&I.deferSessionLink(t,g,s?.cwd??null,s?.git_branch??null),!!S)try{me(t,S);let y=!!h&&!re(h)&&!ne(h)&&S===h.trim();c!=null&&I.linkSession(t,c);let k=d==="pending-pid"?"pending PID-exact match":d==="pending-cwd"?"pending cwd-singleton match":d??"unknown";console.log(`[correlator] auto-aliased ${t.slice(0,8)} \u2192 "${S}"`+(y?` (tab name via ${k}, 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 af(){try{let{stdout:e}=await js("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let s of e.split(`
1144
- `)){let n=s.trim();if(!n)continue;let r=n.match(/^(\d+)\s+(\d+)\s+(.+)$/);if(!r)continue;let o=Number(r[1]),a=Number(r[2]),c=r[3].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&(!Number.isFinite(o)||!Number.isFinite(a)||t.set(o,a))}return t}catch{return new Map}}var cf=9e4;function lf(e){let t=(Date.now()-cf)/1e3,s=e.replace(/\/+$/,"");return f().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1338
+ ${s}`}function aE(e){return ke(e)?.auto_title_source??null}async function cE(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(he(),We));if(!s())throw new Error("claude CLI not found on PATH");let n=await t(e.prompt,[],{model:e.model});if(!n.success)throw new Error(`claude CLI exited ${n.exitCode}: ${n.stderr.slice(-500)}`);let r=lE(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,rE)}function lE(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return Sl(n)}}catch{}return Sl(t)}function Sl(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function yl(e,t={}){let s=re(e);if(!s)throw new Error(`thread not found: ${e}`);let n=oE(s.edges),r=n.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let m=0;m<n.length;m++){let h=n[m],b=m+1;if(a?.aborted){let S={sessionId:h,reason:"cancelled"};u.push(S),t.onSkipped?.(S);continue}if(!o&&aE(h)==="agent"){let S={sessionId:h,reason:"already-titled"};u.push(S),t.onSkipped?.(S);continue}let T;try{if(Tl)T=await Tl({sessionId:h,current:b,total:r});else{let S=iE({sessionId:h,current:b,total:r});T=await cE({prompt:S,model:t.model})}}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),R={sessionId:h,error:w};d.push(R),t.onFailed?.(R);continue}try{me(h,T,"agent")}catch(S){let w=S instanceof Error?S.message:String(S??"unknown error"),R={sessionId:h,error:`setAutoTitle failed: ${w}`};d.push(R),t.onFailed?.(R);continue}c.push(h),t.onProgress?.({current:b,total:r,sessionId:h,title:T})}return{generated:c,skipped:u,failed:d}}var Tl=null;var Gt=new Map,dE=300*1e3;function Yt(e,t,s){let n=e.events.length+1;e.events.push({id:n,kind:t,data:s});for(let r of e.waiters)r.resolve();e.waiters.clear()}function pE(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{Gt.delete(e.jobId)},dE)}function mE(e){let t=0,s=0,n=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(s+=1),r.kind==="error"&&(n+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:s,failed:n,result:e.result}}function wl(e){let t=uE(),s=new AbortController,n={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:s,result:null,cleanupTimer:null};return Gt.set(t,n),(async()=>{await Promise.resolve();try{let r=await yl(e.threadId,{force:e.force??!1,signal:s.signal,model:e.model,onProgress:o=>{n.total=o.total,Yt(n,"progress",o)},onSkipped:o=>{Yt(n,"skipped",o)},onFailed:o=>{Yt(n,"error",o)}});n.result=r,n.status=s.signal.aborted?"cancelled":"done",n.endedAt=new Date().toISOString(),Yt(n,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");n.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},n.status="failed",n.endedAt=new Date().toISOString(),Yt(n,"done",n.result)}finally{pE(n)}})(),t}async function*Rl(e,t=0){let s=Gt.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(n+=1,yield r,r.kind==="done")return}if(s.endedAt)return;await new Promise(r=>{s.waiters.add({resolve:r})})}}function Al(e){let t=Gt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function zr(e){let t=Gt.get(e);return t?mE(t):null}Q();import{existsSync as gE,readFileSync as _E,writeFileSync as fE}from"node:fs";import{join as hE}from"node:path";import{z as pe}from"zod";var Kr=hE(U,"terminals.json"),kl=1440*60*1e3,EE=3e4,bE=6e4,SE=pe.object({shell_pid:pe.number(),tab_name:pe.string(),cwd:pe.string().nullable().optional(),opened_at:pe.string(),last_seen_at:pe.string()}),TE=pe.object({schema:pe.string().optional(),saved_at:pe.string().optional(),terminals:pe.array(SE).max(500).default([]),sessions_by_pid:pe.record(pe.string(),pe.array(pe.string()).max(50)).optional().default({})}),xl=/^[⠀-⣿✳\s]+/,Nl=/^\d+(\.\d+){1,3}$/;function ie(e){let t=e.trim();return!!(!t||xl.test(t)||Nl.test(t))}function lt(e){let t=e.trim();if(!t||Nl.test(t))return null;let s=t.replace(xl,"").trim();return s.length>0?s:null}function Vr(e,t){if(!ie(e))return e;let s=lt(e);return s||(t&&!ie(t)?t:e)}function yE(e){let t=e.now-e.withinMs,s=e.pending.filter(n=>{let r=Date.parse(n.started_at);return Number.isFinite(r)&&r>=t});if(s.length===0)return{kind:"none"};if(e.shellPid!=null){let n=s.find(r=>r.shell_pid===e.shellPid);if(n)return{kind:"pid-match",entry:n}}if(e.cwd){let n=e.cwd.replace(/\/+$/,""),r=s.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===n);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var Zr=class{entries=new Map;origins=new Map;sessionsByPid=new Map;pendingClaudeStarts=[];deferredLinks=new Map;pidOwnership=new Map;loaded=!1;ensureLoaded(){if(!this.loaded&&(this.loaded=!0,!!gE(Kr)))try{let t=_E(Kr,"utf8"),s=JSON.parse(t),n=TE.safeParse(s);if(!n.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",n.error.issues);return}let r=n.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,a]of Object.entries(r.sessions_by_pid??{})){let c=Number(o);if(!Number.isFinite(c))continue;let u=new Set;for(let d of a)d.length>0&&u.add(d);u.size>0&&this.sessionsByPid.set(c,u)}this.gc()}catch{}}save(){try{J();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([s,n])=>[String(s),Array.from(n)]))};fE(Kr,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=Vr(t.tab_name,n?.tab_name),o=n?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:s};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,s){this.ensureLoaded();let n=this.entries.get(t);if(!n)return null;let r=Vr(s,n.tab_name),o={...n,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let s=this.entries.delete(t),n=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(s||n)&&this.save(),s}claimPidOwnership(t,s,n=Date.now()){if(this.ensureLoaded(),!s)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===s?(r.last_claim_at=n,"refreshed"):n-r.last_claim_at>bE?(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,s])=>({shell_pid:t,instance_id:s.instance_id,last_claim_at:s.last_claim_at}))}sync(t){this.ensureLoaded();let s=new Date().toISOString(),n=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=Vr(o.tab_name,a?.tab_name),u=a?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:c,opened_at:u,last_seen_at:s}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:n++}return this.gc(),this.save(),{added:n,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,s){this.ensureLoaded();let n=this.sessionsByPid.get(s);n||(n=new Set,this.sessionsByPid.set(s,n)),n.has(t)||(n.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let s of this.sessionsByPid.values())if(s.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let s=!1;for(let[n,r]of this.sessionsByPid)r.delete(t)&&(s=!0,r.size===0&&this.sessionsByPid.delete(n));return s&&this.save(),s}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let s=yE({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(s.kind==="pid-match"||s.kind==="singleton-cwd"){let n=this.pendingClaudeStarts.indexOf(s.entry);n>=0&&this.pendingClaudeStarts.splice(n,1)}return s}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,s,n,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:s,queued_at:Date.now(),cwd:n,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,s])=>({session_id:t,...s}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[s,n]of this.deferredLinks)n.queued_at<t&&this.deferredLinks.delete(s)}gcPending(){let t=Date.now()-EE;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(s=>{let n=Date.parse(s.started_at);return Number.isFinite(n)&&n>=t}))}outputTails=new Map;setOutputTail(t,s,n){this.ensureLoaded(),this.outputTails.set(t,{text:s,captured_at:n})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,s){this.ensureLoaded(),this.origins.set(t,s),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-kl;for(let[s,n]of this.entries){let r=Date.parse(n.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(s)}}gcOrigins(){let t=Date.now()-kl;for(let[s,n]of this.origins)n.detectedAt<t&&this.origins.delete(s)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let n=Date.now()-t,r=0,o=0;for(let[a,c]of this.sessionsByPid){let u=this.entries.get(a);if(u){let d=Date.parse(u.last_seen_at);if(Number.isFinite(d)&&d>=n)continue}o+=c.size,this.sessionsByPid.delete(a),u&&(this.entries.delete(a),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,s=0;for(let n of[...this.entries.keys()]){let r=!0;try{process.kill(n,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(n),t++;let o=this.sessionsByPid.get(n);o&&(s+=o.size,this.sessionsByPid.delete(n)),this.outputTails.delete(n),this.pidOwnership.delete(n)}return(t||s)&&this.save(),{pruned_pids:t,pruned_sessions:s}}},v=new Zr;import{execFile as vE}from"node:child_process";import{promisify as IE}from"node:util";import{existsSync as jE}from"node:fs";import{basename as ME}from"node:path";H();import{execFile as wE}from"node:child_process";import{readFile as RE}from"node:fs/promises";import{promisify as AE}from"node:util";var kE=AE(wE),Ol=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function xE(e){let t={};for(let s of Ol){let n=e[s];typeof n=="string"&&n.length>0&&(t[s]=n)}return t}function NE(e){let t=Date.now();if(e.CURSOR_TRACE_ID)return{editor:"cursor",label:"Cursor",detectedAt:t};if(e.VSCODE_PID||e.VSCODE_INJECTION)return{editor:"vscode",label:"VS Code",detectedAt:t};let s=e.TERM_PROGRAM;return s==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:s==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:s==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:s==="WezTerm"?{editor:"wezterm",label:"WezTerm",detectedAt:t}:e.WT_SESSION?{editor:"windows-terminal",label:"Windows Terminal",detectedAt:t}:e.KITTY_WINDOW_ID?{editor:"kitty",label:"Kitty",detectedAt:t}:e.TERM==="alacritty"||e.ALACRITTY_SOCKET?{editor:"alacritty",label:"Alacritty",detectedAt:t}:null}async function OE(e){if(process.platform==="linux")try{let t=await RE(`/proc/${e}/environ`,"utf8");return LE(t)}catch{return{}}try{let{stdout:t}=await kE("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return CE(t)}catch{return{}}}function LE(e){let t={};for(let s of e.split("\0")){if(!s)continue;let n=s.indexOf("=");if(n<=0)continue;let r=s.slice(0,n),o=s.slice(n+1);t[r]=o}return t}function CE(e){let t={},s=new Set(Ol),n=e.replace(/\s+/g," ").trim().split(" ");for(let r of n){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);s.has(a)&&(t[a]=r.slice(o+1))}return t}async function Ll(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await OE(e),s=xE(t);return NE(s)}catch{return null}}var on=IE(vE),rn;function DE(){if(rn!==void 0)return rn;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(jE(t))return rn=t,t;return rn=null,null}var FE=3,PE=3600*1e3,zt=new Map;function Il(){let e=v.all(),t=e.map(s=>s.shell_pid).sort((s,n)=>s-n).join(",");return`${e.length}:${t}`}function UE(e){let t=zt.get(e);return t?Date.now()-t.lastAt>PE?(zt.delete(e),!1):t.refusals<FE?!1:t.fingerprint===Il():!1}function Cl(e){let t=Il(),s=zt.get(e);s&&s.fingerprint===t?(s.refusals+=1,s.lastAt=Date.now()):zt.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function $E(e){zt.delete(e)}async function BE(e){let t=DE();if(!t)return null;try{let{stdout:s}=await on(t,["-Fpc",e],{timeout:2e3}),n=s.split(`
1339
+ `),r=null,o=null,a=null;for(let c of n)c.startsWith("p")?(a=Number(c.slice(1)),Number.isFinite(a)&&a>0?r==null&&(r=a):a=null):c.startsWith("c")&&a!=null&&c.slice(1).trim()==="claude"&&o==null&&(o=a);return o??r}catch{return null}}async function vl(e){try{let{stdout:t}=await on("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),s=Number(t.trim());return Number.isFinite(s)&&s>0?s:null}catch{return null}}async function HE(e){try{let{stdout:t}=await on("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),s=Date.parse(t.trim());return Number.isFinite(s)?s:null}catch{return null}}var WE=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu","node","deno","bun","python","python3","ruby","ts-node","tsx","go","cargo","java","php","irb","pry","iex","terminal","shell","console"]);function ce(e){let t=e.trim().toLowerCase();if(!t)return!0;let s=t.replace(/^[-/]+/,"").replace(/^.*\//,"").replace(/\s*\(\d+\)\s*$/,"").trim();return WE.has(s)}function ut(e){let t=e.tabName?.trim();return t&&!ce(t)&&!ie(t)?t:null}function qE(e){try{return f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function an(e){let t=ME(e,".jsonl");if(Ae(t)||UE(t))return;let s=qE(t),n=await BE(e),r=n?await vl(n):null,o=n?await Ll(n):null;o&&v.setOrigin(t,o);let a=null,c=null,u=null,d=v.takePendingMatched({shellPid:r,cwd:s?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${s?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),Cl(t);return}if(d.kind==="pid-match"||d.kind==="singleton-cwd"){let S=d.entry;c=S.shell_pid,u=d.kind==="pid-match"?"pending-pid":"pending-cwd",a=v.get(S.shell_pid)??{shell_pid:S.shell_pid,tab_name:S.tab_name,cwd:S.cwd,opened_at:S.started_at,last_seen_at:S.started_at}}let m=null;if(!a&&n){let S=n;for(let w=0;w<4&&S!=null;w++){let R=await vl(S);if(!R)break;let D=v.get(R);if(D){a=D,c=R,u="ppid";break}m==null&&(m=R),S=R}}if(!a&&s?.cwd){let S=s.cwd.replace(/\/+$/,""),w=v.all().filter(R=>R.cwd&&R.cwd.replace(/\/+$/,"")===S);w.length===1?(a=w[0],c=a.shell_pid,u="cwd"):w.length>=2&&(console.log(`[correlator] ${w.length} registered terminals in ${S} for ${t.slice(0,8)} \u2014 refusing to guess; heuristic title will display`),Cl(t))}let h=null;if(a?.tab_name&&!ce(a.tab_name)&&!ie(a.tab_name))h=a.tab_name;else if(a?.tab_name&&ie(a.tab_name)){let S=lt(a.tab_name);S&&!ce(S)&&(h=S)}if(!h&&!(u==="pending-pid"||u==="pending-cwd")&&a&&s?.cwd){let S=s.cwd.replace(/\/+$/,""),w=v.all().filter(R=>R.shell_pid!==c&&R.cwd&&R.cwd.replace(/\/+$/,"")===S&&!ce(R.tab_name)&&!ie(R.tab_name)).sort((R,D)=>Date.parse(D.last_seen_at)-Date.parse(R.last_seen_at))[0];w&&(h=w.tab_name)}let T=ut({tabName:h,origin:o,cwd:s?.cwd??null,gitBranch:s?.git_branch??null});if(m!=null&&!c&&v.deferSessionLink(t,m,s?.cwd??null,s?.git_branch??null),!!T)try{be(t,T),$E(t);let S=!!h&&!ce(h)&&!ie(h)&&T===h.trim();c!=null&&v.linkSession(t,c);let w=u==="pending-pid"?"pending PID-exact match":u==="pending-cwd"?"pending cwd-singleton match":u??"unknown";console.log(`[correlator] auto-aliased ${t.slice(0,8)} \u2192 "${T}"`+(S?` (tab name via ${w}, shell pid ${c})`:a?` (generic shell name "${a.tab_name}" \u2192 ${o?.editor??"origin"} fallback, shell pid ${c})`:o?` (${o.editor} origin \u2014 no terminal match)`:""))}catch{}}async function XE(){try{let{stdout:e}=await on("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let s of e.split(`
1340
+ `)){let n=s.trim();if(!n)continue;let r=n.match(/^(\d+)\s+(\d+)\s+(.+)$/);if(!r)continue;let o=Number(r[1]),a=Number(r[2]),c=r[3].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&(!Number.isFinite(o)||!Number.isFinite(a)||t.set(o,a))}return t}catch{return new Map}}var JE=9e4;function YE(e){let t=(Date.now()-JE)/1e3,s=e.replace(/\/+$/,"");return f().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1145
1341
  FROM sessions s
1146
1342
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1147
- WHERE s.cwd = ? AND s.file_mtime > ?`).all(s,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function df(e){let t=await import("node:fs/promises"),s="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);s=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let n=[];for(let r of s.split(`
1148
- `)){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,d="";if(typeof c=="string")d=c;else if(Array.isArray(c))for(let u of c)u&&typeof u=="object"&&"text"in u&&typeof u.text=="string"&&(d+=u.text+`
1149
- `);if(d){for(let u of d.split(/\r?\n/)){let g=u.trim();g.length>=60&&g.length<=400&&n.push(g)}if(n.length>=8)break}}return n}async function yc(e){if(Te(e))return null;let t=f().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let s=await df(t.file_path);if(s.length===0)return null;let n=t.cwd.replace(/\/+$/,""),r=I.allOutputTails(),o=[];for(let[a,c]of r){let d=I.get(a);if(!d||!d.cwd||d.cwd.replace(/\/+$/,"")!==n||re(d.tab_name)||ne(d.tab_name))continue;let u=0;for(let g of s)c.text.includes(g)&&u++;u>0&&o.push({shell_pid:a,tab_name:d.tab_name,matched_fingerprints:u})}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 uf=3e4;function wc(){let e=Date.now(),t=I.all(),s=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>uf},n=new Map;for(let o of t){if(s(o)||!o.cwd||re(o.tab_name)||ne(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=n.get(a);c||(c=[],n.set(a,c)),c.push({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd})}let r={rebound:0,ghosts:0,ambiguous:0};for(let o of t){if(!s(o))continue;let a=I.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let d=Te(c);if(!d)continue;let u=f().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!u?.cwd)continue;let g=`${u.cwd.replace(/\/+$/,"")}::${d}`,h=n.get(g)??[];if(h.length===0)continue;if(h.length>1){r.ambiguous++;continue}let b=h[0];b.shell_pid!==o.shell_pid&&(I.unlinkSession(c),I.linkSession(c,b.shell_pid),r.rebound++)}}}return r}function Rc(){let e={resolved:0,expired:0},t=I.allDeferredLinks();for(let s of t){let n=I.get(s.parent_shell_pid);if(!n||re(n.tab_name)||ne(n.tab_name))continue;let r=Te(s.session_id);if(r&&!I.isSessionAutoLinked(s.session_id)){I.resolveDeferredLink(s.session_id);continue}let o=I.getOrigin(s.session_id),a=st({tabName:n.tab_name,origin:o??null,cwd:s.cwd,gitBranch:s.git_branch});if(!a){I.resolveDeferredLink(s.session_id);continue}r!==a&&me(s.session_id,a),I.linkSession(s.session_id,s.parent_shell_pid),I.resolveDeferredLink(s.session_id),e.resolved++}return e}var pf=6e4;async function Ds(){let e=await af(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let s=[];for(let[a,c]of e){let d=I.get(c);if(!d||!d.cwd||re(d.tab_name)||ne(d.tab_name))continue;let u=d.tab_name.trim();if(!u)continue;let g=await nf(a);s.push({claudePid:a,shellPid:c,cwd:d.cwd.replace(/\/+$/,""),target:u,startTimeMs:g})}let n=new Map,r=a=>{let c=n.get(a);if(c)return c;let d=lf(a);return n.set(a,d),d},o=new Set;for(let a of s){let c=r(a.cwd).filter(u=>!o.has(u.id));if(c.length===0)continue;let d=null;if(a.startTimeMs!=null){let u=pf;for(let g of c){if(g.started_at_ms==null)continue;let h=Math.abs(g.started_at_ms-a.startTimeMs);h<u&&(u=h,d=g)}}if(!d&&c.length===1&&(d=c[0]),!d){t.ambiguous_cwd++;continue}if(o.add(d.id),d.alias&&!I.isSessionAutoLinked(d.id)){t.skipped_manual++;continue}if(d.alias===a.target){I.linkSession(d.id,a.shellPid);continue}try{me(d.id,a.target),I.linkSession(d.id,a.shellPid),d.alias?t.renamed++:t.linked++,console.log(`[correlator] linked ${d.id.slice(0,8)} \u2192 "${a.target}" (live sweep, claude pid ${a.claudePid}, shell pid ${a.shellPid})`)}catch{}}return t}import{existsSync as kc,mkdirSync as mf,readFileSync as gf,writeFileSync as _f,chmodSync as ff}from"node:fs";import{homedir as hf}from"node:os";import{join as Ac}from"node:path";import{z as Le}from"zod";function xc(){return process.env.RECALL_HOME??Ac(hf(),".recall")}function Ef(){let e=xc();kc(e)||mf(e,{recursive:!0})}function Nc(){return Ac(xc(),"config.json")}var Ps=Le.object({enabled:Le.boolean().default(!1),backend:Le.enum(["api","mcp"]).default("api"),apiKey:Le.string().optional(),model:Le.string().default("claude-opus-4-7"),maxTagsPerSession:Le.number().int().min(1).max(10).default(4),minTagsPerSession:Le.number().int().min(1).max(10).default(2),autopilot:Le.boolean().default(!1)}),Fs={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function Oc(){let e=Nc();if(!kc(e))return{};try{return JSON.parse(gf(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Re(){let e=Oc().autoTag;if(!e)return{...Fs};let t=Ps.safeParse({...Fs,...e});return t.success?t.data:{...Fs}}function Lc(e){Ef();let t=Oc(),s=Ps.parse({...Fs,...t.autoTag??{},...e}),n={...t,autoTag:s},r=Nc();_f(r,JSON.stringify(n,null,2));try{ff(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return s}function gr(e){let{apiKey:t,...s}=e;return{...s,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}import{existsSync as Cc,mkdirSync as bf,readFileSync as Sf,writeFileSync as Tf}from"node:fs";import{homedir as yf}from"node:os";import{join as Ic}from"node:path";import{z as _r}from"zod";function vc(){return process.env.RECALL_HOME??Ic(yf(),".recall")}function wf(){let e=vc();Cc(e)||bf(e,{recursive:!0})}function jc(){return Ic(vc(),"config.json")}var $s=_r.object({heuristicEnabled:_r.boolean().default(!0),agentEnabled:_r.boolean().default(!1)}),Us={heuristicEnabled:!0,agentEnabled:!1};function Mc(){let e=jc();if(!Cc(e))return{};try{return JSON.parse(Sf(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function He(){let e=Mc().autoTitle;if(!e)return{...Us};let t=$s.safeParse({...Us,...e});return t.success?t.data:{...Us}}function Dc(e){wf();let t=Mc(),s=$s.parse({...Us,...t.autoTitle??{},...e}),n={...t,autoTitle:s};return Tf(jc(),JSON.stringify(n,null,2)),s}H();var Bs="claude-haiku-4-5-20251001",Fc=80,Rf=2e3,nt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},Pc=null;async function kf(e,t){if(Pc)return Pc(e,t);let{spawnClaudePrompt:s,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(ge(),Pe));return n()?s(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function Af(e){let s=f().prepare(`SELECT s.id,
1343
+ WHERE s.cwd = ? AND s.file_mtime > ?`).all(s,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function GE(e){let t=await import("node:fs/promises"),s="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);s=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let n=[];for(let r of s.split(`
1344
+ `)){if(!r.trim())continue;let o;try{o=JSON.parse(r)}catch{continue}let a=o;if(!a||a.type!=="user"&&a.type!=="assistant")continue;let c=a.message?.content,u="";if(typeof c=="string")u=c;else if(Array.isArray(c))for(let d of c)d&&typeof d=="object"&&"text"in d&&typeof d.text=="string"&&(u+=d.text+`
1345
+ `);if(u){for(let d of u.split(/\r?\n/)){let m=d.trim();m.length>=60&&m.length<=400&&n.push(m)}if(n.length>=8)break}}return n}async function jl(e){if(Ae(e))return null;let t=f().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let s=await GE(t.file_path);if(s.length===0)return null;let n=t.cwd.replace(/\/+$/,""),r=v.allOutputTails(),o=[];for(let[a,c]of r){let u=v.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==n||ce(u.tab_name)||ie(u.tab_name))continue;let d=0;for(let m of s)c.text.includes(m)&&d++;d>0&&o.push({shell_pid:a,tab_name:u.tab_name,matched_fingerprints:d})}return o.length===0||(o.sort((a,c)=>c.matched_fingerprints-a.matched_fingerprints),o.length>1&&o[0].matched_fingerprints===o[1].matched_fingerprints)?null:o[0]}var zE=3e4;function Ml(){let e=Date.now(),t=v.all(),s=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>zE},n=new Map;for(let o of t){if(s(o)||!o.cwd||ce(o.tab_name)||ie(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=n.get(a);c||(c=[],n.set(a,c)),c.push({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd})}let r={rebound:0,ghosts:0,ambiguous:0};for(let o of t){if(!s(o))continue;let a=v.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=Ae(c);if(!u)continue;let d=f().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let m=`${d.cwd.replace(/\/+$/,"")}::${u}`,h=n.get(m)??[];if(h.length===0)continue;if(h.length>1){r.ambiguous++;continue}let b=h[0];b.shell_pid!==o.shell_pid&&(v.unlinkSession(c),v.linkSession(c,b.shell_pid),r.rebound++)}}}return r}function Dl(){let e={resolved:0,expired:0},t=v.allDeferredLinks();for(let s of t){let n=v.get(s.parent_shell_pid);if(!n||ce(n.tab_name)||ie(n.tab_name))continue;let r=Ae(s.session_id);if(r&&!v.isSessionAutoLinked(s.session_id)){v.resolveDeferredLink(s.session_id);continue}let o=v.getOrigin(s.session_id),a=ut({tabName:n.tab_name,origin:o??null,cwd:s.cwd,gitBranch:s.git_branch});if(!a){v.resolveDeferredLink(s.session_id);continue}r!==a&&be(s.session_id,a),v.linkSession(s.session_id,s.parent_shell_pid),v.resolveDeferredLink(s.session_id),e.resolved++}return e}var KE=6e4;async function cn(){let e=await XE(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let s=[];for(let[a,c]of e){let u=v.get(c);if(!u||!u.cwd||ce(u.tab_name)||ie(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let m=await HE(a);s.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:m})}let n=new Map,r=a=>{let c=n.get(a);if(c)return c;let u=YE(a);return n.set(a,u),u},o=new Set;for(let a of s){let c=r(a.cwd).filter(d=>!o.has(d.id));if(c.length===0)continue;let u=null;if(a.startTimeMs!=null){let d=KE;for(let m of c){if(m.started_at_ms==null)continue;let h=Math.abs(m.started_at_ms-a.startTimeMs);h<d&&(d=h,u=m)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!v.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){v.linkSession(u.id,a.shellPid);continue}try{be(u.id,a.target),v.linkSession(u.id,a.shellPid),u.alias?t.renamed++:t.linked++,console.log(`[correlator] linked ${u.id.slice(0,8)} \u2192 "${a.target}" (live sweep, claude pid ${a.claudePid}, shell pid ${a.shellPid})`)}catch{}}return t}import{existsSync as Fl,mkdirSync as VE,readFileSync as ZE,writeFileSync as QE,chmodSync as eb}from"node:fs";import{homedir as tb}from"node:os";import{join as Pl}from"node:path";import{z as je}from"zod";function Ul(){return process.env.RECALL_HOME??Pl(tb(),".recall")}function sb(){let e=Ul();Fl(e)||VE(e,{recursive:!0})}function $l(){return Pl(Ul(),"config.json")}var un=je.object({enabled:je.boolean().default(!1),backend:je.enum(["api","mcp"]).default("api"),apiKey:je.string().optional(),model:je.string().default("claude-opus-4-7"),maxTagsPerSession:je.number().int().min(1).max(10).default(4),minTagsPerSession:je.number().int().min(1).max(10).default(2),autopilot:je.boolean().default(!1)}),ln={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function Bl(){let e=$l();if(!Fl(e))return{};try{return JSON.parse(ZE(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Le(){let e=Bl().autoTag;if(!e)return{...ln};let t=un.safeParse({...ln,...e});return t.success?t.data:{...ln}}function Hl(e){sb();let t=Bl(),s=un.parse({...ln,...t.autoTag??{},...e}),n={...t,autoTag:s},r=$l();QE(r,JSON.stringify(n,null,2));try{eb(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return s}function Qr(e){let{apiKey:t,...s}=e;return{...s,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}import{existsSync as Wl,mkdirSync as nb,readFileSync as rb,writeFileSync as ob}from"node:fs";import{homedir as ib}from"node:os";import{join as ql}from"node:path";import{z as eo}from"zod";function Xl(){return process.env.RECALL_HOME??ql(ib(),".recall")}function ab(){let e=Xl();Wl(e)||nb(e,{recursive:!0})}function Jl(){return ql(Xl(),"config.json")}var pn=eo.object({heuristicEnabled:eo.boolean().default(!0),agentEnabled:eo.boolean().default(!1)}),dn={heuristicEnabled:!0,agentEnabled:!1};function Yl(){let e=Jl();if(!Wl(e))return{};try{return JSON.parse(rb(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function Ge(){let e=Yl().autoTitle;if(!e)return{...dn};let t=pn.safeParse({...dn,...e});return t.success?t.data:{...dn}}function Gl(e){ab();let t=Yl(),s=pn.parse({...dn,...t.autoTitle??{},...e}),n={...t,autoTitle:s};return ob(Jl(),JSON.stringify(n,null,2)),s}H();var mn="claude-haiku-4-5-20251001",zl=80,cb=2e3,dt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},Kl=null;async function lb(e,t){if(Kl)return Kl(e,t);let{spawnClaudePrompt:s,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(he(),We));return n()?s(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function ub(e){let s=f().prepare(`SELECT s.id,
1150
1346
  s.auto_title,
1151
1347
  s.auto_title_source,
1152
1348
  CASE WHEN sa.alias IS NOT NULL AND sa.alias != ''
1153
1349
  THEN 1 ELSE 0 END AS has_alias_int
1154
1350
  FROM sessions s
1155
1351
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1156
- WHERE s.id = ?`).get(e);return s?{id:s.id,auto_title:s.auto_title,auto_title_source:s.auto_title_source??null,has_alias:s.has_alias_int===1}:null}function xf(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 Nf(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let s=jt({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(s){case"low_signal":case"recursive_meta":case"programmatic":return{eligible:!0};case"agent":return{eligible:!1,reason:"agent_titled"};case"manual_alias":return{eligible:!1,reason:"manual_alias"};default:return{eligible:!1,reason:"clean"}}}function Of(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(`
1157
- `)}function Lf(e){let t=e.trim();if(!t)return null;let s=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")s=a.result.trim();else if(typeof a.title=="string")return Uc(a)}}catch{}let n=s.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);n&&(s=n[1].trim());let r=null;try{r=JSON.parse(s)}catch{let o=s.indexOf("{"),a=s.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(s.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:Uc(r)}function Uc(e){let t=e.title;if(typeof t!="string")return null;let s=Cf(t).trim();if(!s)return null;let n=e.evidence,r=typeof n=="string"?n.trim():"";return{title:s,evidence:r}}function Cf(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function If(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=Fc?t:t.slice(0,Fc)}function vf(e,t){let s=e.auto_title??"";return!s&&e.has_alias&&(s=f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:s,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function fr(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let s=Af(e);if(!s)throw new Error(`session not found: ${e}`);let n=Nf(s,t.force===!0);if(!n.eligible)return vf(s,n.reason);let r=t.budget??Rf,o=Os(e,{budget:r});if(xf(o))throw new nt(e);if(t.signal?.aborted)return{session_id:e,title:s.auto_title??"",source:s.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=Of(o.bundle),c=t.model??Bs,d=await kf(a,c);if(!d.success){let S=d.stderr.slice(-300);throw new Error(`claude CLI exited ${d.exitCode}: ${S||"no stderr captured"}`)}let u=Lf(d.stdout);if(!u)throw new Error("failed to parse regeneration output: not valid JSON {title, evidence}");if(!u.title||!u.title.trim())throw new Error("regeneration produced empty title");let g=If(u.title);if(!g)throw new Error("regeneration produced empty title after clamp");de(e,g,"agent");let b=ye(e)?.auto_title??g;return{session_id:e,title:b,source:"agent",confidence:Mf(o),evidence:u.evidence||`regenerated from neighborhood (${jf(o)})`,written:!0}}function jf(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 Mf(e){let t=e.parents.length,s=e.children.length,n=e.citations.length,r=e.similar.length,o=e.cousins.length,a=e.wikiLinks.length,c=.25*Math.min(1,t)+.15*Math.min(1,s)+.2*Math.min(1,n/2)+.15*Math.min(1,r/3)+.1*Math.min(1,o/3)+.15*Math.min(1,a);return Math.min(.95,c)}import{streamSSE as Me}from"hono/streaming";import{bodyLimit as cS}from"hono/body-limit";import{z as j}from"zod";H();ge();import{createHash as Df}from"node:crypto";var br=1,We="claude-haiku-4-5-20251001",Ff=3,Pf=32e3,$c=2e3,Uf=30,$f=30,Bf=30,Hf=30;function Wf(e){let s=f().prepare(`SELECT s.id,
1158
- NULLIF(sa.alias, '') AS alias,
1159
- s.auto_title,
1160
- s.auto_title_source,
1161
- s.title_quality,
1162
- s.message_count,
1163
- s.first_user_message,
1164
- p.name AS project
1165
- FROM sessions s
1166
- LEFT JOIN session_aliases sa ON sa.session_id = s.id
1167
- LEFT JOIN projects p ON p.id = s.project_id
1168
- WHERE s.id = ?`).get(e);return s?{...s,alias_source:s.alias?"manual":null}:null}function Bc(e,t={}){if(e.message_count<Ff)return{eligible:!1,reason:"too-short"};let s=e.title_quality;if(s==="programmatic"||s==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let n=Be(e.id);if(n&&n.extractor_version>=br)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function qf(e){let t=Wf(e);if(!t)return null;let n=f().prepare(`SELECT role, content_text
1169
- FROM messages
1170
- WHERE session_id = ?
1171
- AND is_sidechain = 0
1172
- AND content_text IS NOT NULL
1173
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of n){let c=a.role??"system",d=a.content_text.trim();if(!d)continue;let u=d.length>$c?d.slice(0,$c)+"\u2026":d,g=`${c}: ${u}`;if(o+g.length>Pf)break;r.push(g),o+=g.length}return{meta:t,excerpt:r.join(`
1174
-
1175
- `)}}function Jf(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(`
1176
- `)}function Xf(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function hr(e,t,s=!1){if(!Array.isArray(e))return[];let n=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=s?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!n.has(a)&&(n.add(a),r.push(a),r.length>=t))break}return r}function Gf(e,t){if(!Array.isArray(e))return[];let s=new Set,n=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,a=r.freq;if(typeof o!="string")continue;let c=o.trim().toLowerCase();if(!c||c.length>128||s.has(c))continue;s.add(c);let d=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(n.push({term:c,frequency:d}),n.length>=t)break}return n}function Yf(e,t){if(!Array.isArray(e))return[];let s=[];for(let n of e){if(!n||typeof n!="object")continue;let r=n.error_type,o=n.snippet,a=n.file,c=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",d=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!d)continue;let u=typeof a=="string"&&a.trim().length>0?a.trim().slice(0,256):null,g=Df("sha256").update(`${c}::${d}`).digest("hex").slice(0,12);if(s.push({error_type:c,message_hash:g,snippet:d,file:u}),s.length>=t)break}return s}function Kf(e){let t=e.trim();try{let b=JSON.parse(t);typeof b.result=="string"&&(t=b.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+1),o;try{o=JSON.parse(r)}catch{return null}let a=hr(o.files_written,200),c=hr(o.brands_mentioned,$f),d=Gf(o.terms_introduced,Uf),u=hr(o.plan_ids_referenced,Bf),g=Yf(o.bug_signatures,Hf);return a.length===0&&c.length===0&&d.length===0&&u.length===0&&g.length===0&&!Xf(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:d,plan_ids_referenced:u,bug_signatures:g}}var Er=null;async function zf(e,t){return Er?Er(e,t):Fe(e,[],{model:t})}async function Sr(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let s=qf(e);if(!s)return{session_id:e,ok:!1,skipped:"session-not-found"};let n=Bc(s.meta,{force:t.force});if(!n.eligible)return{session_id:e,ok:!1,skipped:n.reason};if(!Er&&!oe())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=Jf(s),o=t.model??We,a=await zf(r,o);if(!a.success)return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode};let c=Kf(a.stdout);if(!c)return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode};let d=Vf(a.stdout),u=Na({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:d,raw_response_excerpt:a.stdout.slice(0,4e3)},extractor_version:br});return{session_id:e,ok:!0,index:u,usage:d}}function Vf(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let s=t.usage,n={};return typeof s.input_tokens=="number"&&(n.input_tokens=s.input_tokens),typeof s.output_tokens=="number"&&(n.output_tokens=s.output_tokens),n}}catch{}return null}function rt(e={}){let t=f(),s=[],n=[];typeof e.projectId=="number"&&(s.push("s.project_id = ?"),n.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=s.length?`WHERE ${s.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
1177
- NULLIF(sa.alias, '') AS alias,
1178
- s.auto_title,
1179
- s.auto_title_source,
1180
- s.title_quality,
1181
- s.message_count,
1182
- s.first_user_message,
1183
- p.name AS project
1184
- FROM sessions s
1185
- LEFT JOIN session_aliases sa ON sa.session_id = s.id
1186
- LEFT JOIN projects p ON p.id = s.project_id
1187
- ${o}
1188
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(...n),c=[],d=new Map;for(let u of a){let g={...u,alias_source:u.alias?"manual":null},h=Bc(g,{force:e.force});if(!h.eligible){let b=h.reason??"session-not-found";d.set(b,(d.get(b)??0)+1);continue}if(c.push(g),c.length>=r)break}return{eligible:c,skipped:d}}async function Hc(e={}){let t=rt({projectId:e.projectId,limit:e.limit,force:e.force}),s={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())s.skipped+=r;e.onProgress?.({...s});let n=[];for(let r of t.eligible){if(e.signal?.aborted)break;s.current_session_id=r.id,e.onProgress?.({...s});let o=await Sr(r.id,{model:e.model,force:e.force,signal:e.signal});n.push(o),e.onResult?.(o),o.ok?s.ok+=1:o.skipped?s.skipped+=1:s.failed+=1,o.usage?.input_tokens&&(s.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(s.total_output_tokens+=o.usage.output_tokens),s.processed+=1,e.onProgress?.({...s})}return s.current_session_id=null,e.onProgress?.({...s}),{progress:s,results:n}}H();H();ot();function Bt(e){return f().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}H();function Gc(e){let t=f(),s=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1352
+ WHERE s.id = ?`).get(e);return s?{id:s.id,auto_title:s.auto_title,auto_title_source:s.auto_title_source??null,has_alias:s.has_alias_int===1}:null}function db(e){return e.parents.length===0&&e.children.length===0&&e.citations.length===0&&e.similar.length===0&&e.cousins.length===0&&e.wikiLinks.length===0}function pb(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let s=Xt({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(s){case"low_signal":case"recursive_meta":case"programmatic":return{eligible:!0};case"agent":return{eligible:!1,reason:"agent_titled"};case"manual_alias":return{eligible:!1,reason:"manual_alias"};default:return{eligible:!1,reason:"clean"}}}function mb(e){return["You are renaming a Claude Code session. Below is its NEIGHBORHOOD: parent","sessions, child sessions, related sessions, and citations \u2014 assembled by","Claude Recall's cog-graph. The session itself has a low-signal or","self-referential title that needs replacement.","","Read the neighborhood, then mint a single descriptive title that","reflects this session's role in the surrounding work. Constraints:","","- Maximum 80 characters.","- No quotes, no trailing punctuation.","- Output ONLY a JSON object with two fields:",' { "title": "<\u226480 char title>", "evidence": "<one sentence why>" }',"- No markdown, no preface, no commentary outside the JSON.","","--- NEIGHBORHOOD BUNDLE ---",e,"--- END NEIGHBORHOOD BUNDLE ---"].join(`
1353
+ `)}function gb(e){let t=e.trim();if(!t)return null;let s=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")s=a.result.trim();else if(typeof a.title=="string")return Vl(a)}}catch{}let n=s.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);n&&(s=n[1].trim());let r=null;try{r=JSON.parse(s)}catch{let o=s.indexOf("{"),a=s.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(s.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:Vl(r)}function Vl(e){let t=e.title;if(typeof t!="string")return null;let s=_b(t).trim();if(!s)return null;let n=e.evidence,r=typeof n=="string"?n.trim():"";return{title:s,evidence:r}}function _b(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function fb(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=zl?t:t.slice(0,zl)}function hb(e,t){let s=e.auto_title??"";return!s&&e.has_alias&&(s=f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:s,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function to(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let s=ub(e);if(!s)throw new Error(`session not found: ${e}`);let n=pb(s,t.force===!0);if(!n.eligible)return hb(s,n.reason);let r=t.budget??cb,o=en(e,{budget:r});if(db(o))throw new dt(e);if(t.signal?.aborted)return{session_id:e,title:s.auto_title??"",source:s.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=mb(o.bundle),c=t.model??mn,u=await lb(a,c);if(!u.success){let T=u.stderr.slice(-300);throw new Error(`claude CLI exited ${u.exitCode}: ${T||"no stderr captured"}`)}let d=gb(u.stdout);if(!d)throw new Error("failed to parse regeneration output: not valid JSON {title, evidence}");if(!d.title||!d.title.trim())throw new Error("regeneration produced empty title");let m=fb(d.title);if(!m)throw new Error("regeneration produced empty title after clamp");me(e,m,"agent");let b=ke(e)?.auto_title??m;return{session_id:e,title:b,source:"agent",confidence:bb(o),evidence:d.evidence||`regenerated from neighborhood (${Eb(o)})`,written:!0}}function Eb(e){let t=[];return e.parents.length&&t.push(`${e.parents.length} parents`),e.children.length&&t.push(`${e.children.length} children`),e.citations.length&&t.push(`${e.citations.length} citations`),e.similar.length&&t.push(`${e.similar.length} similar`),e.cousins.length&&t.push(`${e.cousins.length} cousins`),e.wikiLinks.length&&t.push(`${e.wikiLinks.length} wiki-links`),t.join(", ")}function bb(e){let t=e.parents.length,s=e.children.length,n=e.citations.length,r=e.similar.length,o=e.cousins.length,a=e.wikiLinks.length,c=.25*Math.min(1,t)+.15*Math.min(1,s)+.2*Math.min(1,n/2)+.15*Math.min(1,r/3)+.1*Math.min(1,o/3)+.15*Math.min(1,a);return Math.min(.95,c)}import{streamSSE as Ue}from"hono/streaming";import{bodyLimit as sy}from"hono/body-limit";import{z as j}from"zod";H();H();ze();function Kt(e){return f().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}H();function Zl(e){let t=f(),s=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1189
1354
  (message_hash, resolved_in_session_id, fix_summary, resolved_at, unresolved_at)
1190
1355
  VALUES (?, ?, ?, ?, NULL)
1191
1356
  ON CONFLICT(message_hash) DO UPDATE SET
1192
1357
  resolved_in_session_id = excluded.resolved_in_session_id,
1193
1358
  fix_summary = excluded.fix_summary,
1194
1359
  resolved_at = excluded.resolved_at,
1195
- unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,s),sh(e.messageHash)}function Yc(e){f().prepare(`UPDATE bug_signature_resolutions
1360
+ unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,s),Sb(e.messageHash)}function Ql(e){f().prepare(`UPDATE bug_signature_resolutions
1196
1361
  SET unresolved_at = ?
1197
- WHERE message_hash = ?`).run(new Date().toISOString(),e)}function sh(e){return f().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function yr(e){if(e.length===0)return new Map;let t=f(),s=e.map(()=>"?").join(","),n=t.prepare(`SELECT * FROM bug_signature_resolutions
1198
- WHERE message_hash IN (${s})`).all(...e),r=new Map;for(let o of n)r.set(o.message_hash,o);return r}function wr(e){return!!e&&e.unresolved_at===null}H();function Ht(){return new Date().toISOString()}function Rr(){let e=f(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1362
+ WHERE message_hash = ?`).run(new Date().toISOString(),e)}function Sb(e){return f().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function so(e){if(e.length===0)return new Map;let t=f(),s=e.map(()=>"?").join(","),n=t.prepare(`SELECT * FROM bug_signature_resolutions
1363
+ WHERE message_hash IN (${s})`).all(...e),r=new Map;for(let o of n)r.set(o.message_hash,o);return r}function no(e){return!!e&&e.unresolved_at===null}H();function Vt(){return new Date().toISOString()}function ro(){let e=f(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1199
1364
  FROM macro_repos
1200
1365
  ORDER BY name COLLATE NOCASE`).all();if(t.length===0)return[];let s=t.map(a=>a.id),n=s.map(()=>"?").join(","),r=e.prepare(`SELECT m.macro_repo_id, m.project_id, p.name AS project_name
1201
1366
  FROM macro_repo_members m
1202
1367
  JOIN projects p ON p.id = m.project_id
1203
1368
  WHERE m.macro_repo_id IN (${n})
1204
- ORDER BY p.name COLLATE NOCASE`).all(...s),o=new Map;for(let a of t)o.set(a.id,{...a,member_project_ids:[],member_project_names:[]});for(let a of r){let c=o.get(a.macro_repo_id);c&&(c.member_project_ids.push(a.project_id),c.member_project_names.push(a.project_name))}return Array.from(o.values())}function it(e){return Rr().find(s=>s.id===e)??null}function Kc(){return f().prepare(`SELECT p.id, p.name
1369
+ ORDER BY p.name COLLATE NOCASE`).all(...s),o=new Map;for(let a of t)o.set(a.id,{...a,member_project_ids:[],member_project_names:[]});for(let a of r){let c=o.get(a.macro_repo_id);c&&(c.member_project_ids.push(a.project_id),c.member_project_names.push(a.project_name))}return Array.from(o.values())}function pt(e){return ro().find(s=>s.id===e)??null}function eu(){return f().prepare(`SELECT p.id, p.name
1205
1370
  FROM projects p
1206
1371
  LEFT JOIN macro_repo_members m ON m.project_id = p.id
1207
1372
  WHERE m.project_id IS NULL
1208
- ORDER BY p.name COLLATE NOCASE`).all()}function zc(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let s=f(),n=Ht(),r=s.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1209
- VALUES (?, ?, ?, ?)`).run(t,e.description??null,n,n),o=Number(r.lastInsertRowid);return it(o)}function Vc(e,t){let s=it(e);if(!s)throw new Error(`macro repo ${e} not found`);let n=t.name!==void 0?t.name.trim():s.name;if(!n)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:s.description;return f().prepare(`UPDATE macro_repos
1373
+ ORDER BY p.name COLLATE NOCASE`).all()}function tu(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let s=f(),n=Vt(),r=s.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1374
+ VALUES (?, ?, ?, ?)`).run(t,e.description??null,n,n),o=Number(r.lastInsertRowid);return pt(o)}function su(e,t){let s=pt(e);if(!s)throw new Error(`macro repo ${e} not found`);let n=t.name!==void 0?t.name.trim():s.name;if(!n)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:s.description;return f().prepare(`UPDATE macro_repos
1210
1375
  SET name = ?, description = ?, updated_at = ?
1211
- WHERE id = ?`).run(n,r,Ht(),e),it(e)}function Zc(e){f().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function Qc(e,t){let s=f();if(!s.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!s.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);s.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1212
- VALUES (?, ?, ?)`).run(e,t,Ht()),s.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Ht(),e)}function el(e,t){let s=f();s.prepare(`DELETE FROM macro_repo_members
1213
- WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),s.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Ht(),e)}H();function tl(e){let t=f(),s=new Date().toISOString(),n=t.prepare(`INSERT INTO bug_synthesis_results
1376
+ WHERE id = ?`).run(n,r,Vt(),e),pt(e)}function nu(e){f().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function ru(e,t){let s=f();if(!s.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!s.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);s.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1377
+ VALUES (?, ?, ?)`).run(e,t,Vt()),s.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Vt(),e)}function ou(e,t){let s=f();s.prepare(`DELETE FROM macro_repo_members
1378
+ WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),s.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Vt(),e)}H();function iu(e){let t=f(),s=new Date().toISOString(),n=t.prepare(`INSERT INTO bug_synthesis_results
1214
1379
  (scope, target_id, mode, model, output_markdown, input_tokens, output_tokens, context_summary, created_at, job_id)
1215
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.scope,e.target_id,e.mode,e.model,e.output_markdown,e.input_tokens,e.output_tokens,JSON.stringify(e.context_summary??{}),s,e.job_id??null),r=Number(n.lastInsertRowid);return kr(r)}function sl(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 kr(e){let s=f().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return s?sl(s):null}function nl(e={}){let t=f(),s=[],n=[];e.scope&&(s.push("scope = ?"),n.push(e.scope)),e.target_id&&(s.push("target_id = ?"),n.push(e.target_id));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1380
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.scope,e.target_id,e.mode,e.model,e.output_markdown,e.input_tokens,e.output_tokens,JSON.stringify(e.context_summary??{}),s,e.job_id??null),r=Number(n.lastInsertRowid);return oo(r)}function au(e){let t={};try{t=JSON.parse(e.context_summary)}catch{t={}}return{id:e.id,scope:e.scope,target_id:e.target_id,mode:e.mode,model:e.model,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,context_summary:t,created_at:e.created_at,job_id:e.job_id}}function oo(e){let s=f().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return s?au(s):null}function cu(e={}){let t=f(),s=[],n=[];e.scope&&(s.push("scope = ?"),n.push(e.scope)),e.target_id&&(s.push("target_id = ?"),n.push(e.target_id));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1216
1381
  ${r}
1217
1382
  ORDER BY created_at DESC
1218
- LIMIT ?`).all(...n,o).map(sl)}function rl(e){let s=f().prepare(`SELECT target_id, COUNT(*) AS n
1383
+ LIMIT ?`).all(...n,o).map(au)}function lu(e){let s=f().prepare(`SELECT target_id, COUNT(*) AS n
1219
1384
  FROM bug_synthesis_results
1220
1385
  WHERE scope = ?
1221
- GROUP BY target_id`).all(e),n=new Map;for(let r of s)n.set(r.target_id,r.n);return n}function ol(e){f().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as nh,timingSafeEqual as rh}from"node:crypto";var oh=6e4,ih=new Set(["127.0.0.1","localhost"]),Wt=new Map;function Ar(){return Date.now()}function il(){let e=Ar();for(let[t,s]of Wt)(s.expiresAt<=e||s.used)&&Wt.delete(t)}function we(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!ih.has(r.hostname))return e.json({error:"forbidden: cross-origin launcher request rejected"},403)}catch{return e.json({error:"forbidden: invalid Origin header"},403)}let s=e.req.header("sec-fetch-site");return s&&s!=="same-origin"&&s!=="none"?e.json({error:"forbidden: Sec-Fetch-Site indicates cross-origin"},403):e.req.header("x-recall-launcher")!=="1"?e.json({error:"forbidden: missing X-Recall-Launcher header (this endpoint is callable only from the Recall web UI)"},403):null}function xr(e){il();let t=nh(32).toString("hex"),s=Ar()+oh;return Wt.set(t,{token:t,intent:e,expiresAt:s,used:!1}),{token:t,expiresAt:s}}function Nr(e){if(il(),typeof e!="string"||e.length!==64)return null;let t;try{t=Buffer.from(e,"hex")}catch{return null}if(t.length!==32)return null;for(let s of Wt.values()){if(s.used||s.expiresAt<=Ar())continue;let n;try{n=Buffer.from(s.token,"hex")}catch{continue}if(n.length===t.length&&rh(n,t))return s.used=!0,Wt.delete(s.token),s.intent}return null}import{existsSync as ph,mkdirSync as XR,readFileSync as mh,writeFileSync as GR}from"node:fs";import{homedir as gh}from"node:os";import{join as ml}from"node:path";import{z as Or}from"zod";import{appendFileSync as ah,existsSync as al,mkdirSync as ch,readFileSync as lh}from"node:fs";import{homedir as dh}from"node:os";import{join as cl}from"node:path";function ll(){return process.env.RECALL_HOME??cl(dh(),".recall")}function uh(){let e=ll();al(e)||ch(e,{recursive:!0})}function dl(){return cl(ll(),"launcher-audit.log")}function ee(e){uh();let t={ts:new Date().toISOString(),...e};try{ah(dl(),JSON.stringify(t)+`
1222
- `,"utf8")}catch(s){console.error("[launcher-audit] failed to append:",s)}}function ul(e){let t=dl();if(!al(t))return{input_tokens:0,output_tokens:0,records_counted:0};let s=Date.now()-e,n=0,r=0,o=0,a;try{a=lh(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1223
- `)){if(!c.trim())continue;let d;try{d=JSON.parse(c)}catch{continue}if(d.kind!=="run-completed"&&d.kind!=="synth-completed")continue;let u=Date.parse(d.ts);!Number.isFinite(u)||u<s||(n+=Number(d.input_tokens??0),r+=Number(d.output_tokens??0),o+=1)}return{input_tokens:n,output_tokens:r,records_counted:o}}var _h=1440*60*1e3,fh=Or.object({dailyTokenBudget:Or.number().int().nonnegative().default(1e6),sessionCeiling:Or.number().int().positive().max(1e4).default(500)}),Lr={dailyTokenBudget:1e6,sessionCeiling:500};function hh(){return process.env.RECALL_HOME??ml(gh(),".recall")}function Eh(){return ml(hh(),"config.json")}function bh(){let e=Eh();if(!ph(e))return{};try{return JSON.parse(mh(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function Cr(){let e=bh().launcher;if(!e)return{...Lr};let t=fh.safeParse({...Lr,...e});return t.success?t.data:{...Lr}}function at(){let e=Cr(),t=ul(_h),s=t.input_tokens+t.output_tokens,n=Math.max(0,e.dailyTokenBudget-s);return{daily_token_budget:e.dailyTokenBudget,session_ceiling:e.sessionCeiling,spent_input_tokens_24h:t.input_tokens,spent_output_tokens_24h:t.output_tokens,spent_total_tokens_24h:s,remaining_tokens_24h:n}}function Ir(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function vr(e){let t=e.mode==="root_cause"?800:2e3;if(e.scope==="cluster")return{estimated_input_tokens_max:5e3+Math.min(8,Math.max(1,e.member_session_count??1))*3e3,estimated_output_tokens_max:t};let s=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*s),estimated_output_tokens_max:t}}var pl={pro:45,"max-5x":225,"max-20x":900};function Sh(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var Th=4e3;function gl(e){return Math.max(1,Math.ceil(e/Th))}function Ce(e,t){let s=Sh(t),n=e*s,r=Object.keys(pl).map(o=>{let a=n/pl[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:s,per_plan:r,caveat:"Estimates use Anthropic\u2019s public approximate caps and assume a multiplier of ~1x for Haiku, ~5x for Sonnet, ~10x for Opus. Anthropic adjusts these limits without notice; actual consumption depends on session size. Treat as planning guidance, not a contract."}}import{randomUUID as yh}from"node:crypto";var ct=new Map,qt=new Map,wh=300*1e3;function qs(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function Rh(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{ct.delete(e.jobId),qt.get(e.project)===e.jobId&&qt.delete(e.project)},wh),e.cleanupTimer.unref?.())}function jr(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 _l(e){let t=Bt(e.project);if(!t)return{error:`project "${e.project}" not found`};let s=qt.get(t.name);if(s){let g=ct.get(s);if(g&&g.status==="running")return{jobId:s,reused:!0};qt.delete(t.name)}let n=yh(),r=e.model??We,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,d=new Date().toISOString(),u={jobId:n,project:t.name,projectId:t.id,model:r,limit:o,force:a,status:"running",startedAt:d,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 ct.set(n,u),qt.set(t.name,n),ee({kind:"run-launched",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await Hc({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:g=>{u.progress=g,qs(u,"progress",g)},onResult:g=>{!g.ok&&!g.skipped&&qs(u,"error",{session_id:g.session_id,reason:g.failed??"unknown"})}}),u.status=c.signal.aborted?"cancelled":"done",u.endedAt=new Date().toISOString(),qs(u,"done",jr(u)),ee({kind:u.status==="cancelled"?"run-cancelled":"run-completed",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null,input_tokens:u.progress.total_input_tokens,output_tokens:u.progress.total_output_tokens,sessions_processed:u.progress.processed})}catch(g){let h=g instanceof Error?g.message:String(g??"unknown error");u.status="failed",u.endedAt=new Date().toISOString(),u.error=h,qs(u,"done",jr(u)),ee({kind:"run-failed",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null,reason:h,input_tokens:u.progress.total_input_tokens,output_tokens:u.progress.total_output_tokens})}finally{Rh(u)}})(),{jobId:n,reused:!1}}async function*fl(e,t=0){let s=ct.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function hl(e){let t=ct.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Mr(e){let t=ct.get(e);return t?jr(t):null}H();import{randomUUID as kh}from"node:crypto";import{spawn as Ah}from"node:child_process";import{execSync as xh}from"node:child_process";var El="claude-haiku-4-5-20251001",dt=new Map,Gt=new Map,Nh=300*1e3;function bl(e){return`${e.scope}:${e.target_id}:${e.mode}`}function Jt(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function Oh(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{dt.delete(e.jobId);let t=bl(e.intent);Gt.get(t)===e.jobId&&Gt.delete(t)},Nh),e.cleanupTimer.unref?.())}function lt(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 Sl(){return f().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1386
+ GROUP BY target_id`).all(e),n=new Map;for(let r of s)n.set(r.target_id,r.n);return n}function uu(e){f().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as Tb,timingSafeEqual as yb}from"node:crypto";var wb=6e4,Rb=new Set(["127.0.0.1","localhost"]),Zt=new Map;function io(){return Date.now()}function du(){let e=io();for(let[t,s]of Zt)(s.expiresAt<=e||s.used)&&Zt.delete(t)}function xe(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!Rb.has(r.hostname))return e.json({error:"forbidden: cross-origin launcher request rejected"},403)}catch{return e.json({error:"forbidden: invalid Origin header"},403)}let s=e.req.header("sec-fetch-site");return s&&s!=="same-origin"&&s!=="none"?e.json({error:"forbidden: Sec-Fetch-Site indicates cross-origin"},403):e.req.header("x-recall-launcher")!=="1"?e.json({error:"forbidden: missing X-Recall-Launcher header (this endpoint is callable only from the Recall web UI)"},403):null}function ao(e){du();let t=Tb(32).toString("hex"),s=io()+wb;return Zt.set(t,{token:t,intent:e,expiresAt:s,used:!1}),{token:t,expiresAt:s}}function co(e){if(du(),typeof e!="string"||e.length!==64)return null;let t;try{t=Buffer.from(e,"hex")}catch{return null}if(t.length!==32)return null;for(let s of Zt.values()){if(s.used||s.expiresAt<=io())continue;let n;try{n=Buffer.from(s.token,"hex")}catch{continue}if(n.length===t.length&&yb(n,t))return s.used=!0,Zt.delete(s.token),s.intent}return null}import{existsSync as Lb,mkdirSync as Zk,readFileSync as Cb,writeFileSync as Qk}from"node:fs";import{homedir as vb}from"node:os";import{join as Eu}from"node:path";import{z as lo}from"zod";import{appendFileSync as Ab,existsSync as pu,mkdirSync as kb,readFileSync as xb}from"node:fs";import{homedir as Nb}from"node:os";import{join as mu}from"node:path";function gu(){return process.env.RECALL_HOME??mu(Nb(),".recall")}function Ob(){let e=gu();pu(e)||kb(e,{recursive:!0})}function _u(){return mu(gu(),"launcher-audit.log")}function ne(e){Ob();let t={ts:new Date().toISOString(),...e};try{Ab(_u(),JSON.stringify(t)+`
1387
+ `,"utf8")}catch(s){console.error("[launcher-audit] failed to append:",s)}}function fu(e){let t=_u();if(!pu(t))return{input_tokens:0,output_tokens:0,records_counted:0};let s=Date.now()-e,n=0,r=0,o=0,a;try{a=xb(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1388
+ `)){if(!c.trim())continue;let u;try{u=JSON.parse(c)}catch{continue}if(u.kind!=="run-completed"&&u.kind!=="synth-completed")continue;let d=Date.parse(u.ts);!Number.isFinite(d)||d<s||(n+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:n,output_tokens:r,records_counted:o}}var Ib=1440*60*1e3,jb=lo.object({dailyTokenBudget:lo.number().int().nonnegative().default(1e6),sessionCeiling:lo.number().int().positive().max(1e4).default(500)}),uo={dailyTokenBudget:1e6,sessionCeiling:500};function Mb(){return process.env.RECALL_HOME??Eu(vb(),".recall")}function Db(){return Eu(Mb(),"config.json")}function Fb(){let e=Db();if(!Lb(e))return{};try{return JSON.parse(Cb(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function po(){let e=Fb().launcher;if(!e)return{...uo};let t=jb.safeParse({...uo,...e});return t.success?t.data:{...uo}}function mt(){let e=po(),t=fu(Ib),s=t.input_tokens+t.output_tokens,n=Math.max(0,e.dailyTokenBudget-s);return{daily_token_budget:e.dailyTokenBudget,session_ceiling:e.sessionCeiling,spent_input_tokens_24h:t.input_tokens,spent_output_tokens_24h:t.output_tokens,spent_total_tokens_24h:s,remaining_tokens_24h:n}}function mo(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function go(e){let t=e.mode==="root_cause"?800:2e3;if(e.scope==="cluster")return{estimated_input_tokens_max:5e3+Math.min(8,Math.max(1,e.member_session_count??1))*3e3,estimated_output_tokens_max:t};let s=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*s),estimated_output_tokens_max:t}}var hu={pro:45,"max-5x":225,"max-20x":900};function Pb(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var Ub=4e3;function bu(e){return Math.max(1,Math.ceil(e/Ub))}function Me(e,t){let s=Pb(t),n=e*s,r=Object.keys(hu).map(o=>{let a=n/hu[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:s,per_plan:r,caveat:"Estimates use Anthropic\u2019s public approximate caps and assume a multiplier of ~1x for Haiku, ~5x for Sonnet, ~10x for Opus. Anthropic adjusts these limits without notice; actual consumption depends on session size. Treat as planning guidance, not a contract."}}import{randomUUID as $b}from"node:crypto";var gt=new Map,Qt=new Map,Bb=300*1e3;function gn(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function Hb(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{gt.delete(e.jobId),Qt.get(e.project)===e.jobId&&Qt.delete(e.project)},Bb),e.cleanupTimer.unref?.())}function _o(e){return{jobId:e.jobId,project:e.project,model:e.model,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.progress.total,processed:e.progress.processed,ok:e.progress.ok,failed:e.progress.failed,skipped:e.progress.skipped,total_input_tokens:e.progress.total_input_tokens,total_output_tokens:e.progress.total_output_tokens,current_session_id:e.progress.current_session_id,error:e.error}}function Su(e){let t=Kt(e.project);if(!t)return{error:`project "${e.project}" not found`};let s=Qt.get(t.name);if(s){let m=gt.get(s);if(m&&m.status==="running")return{jobId:s,reused:!0};Qt.delete(t.name)}let n=$b(),r=e.model??qe,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:n,project:t.name,projectId:t.id,model:r,limit:o,force:a,status:"running",startedAt:u,endedAt:null,events:[],waiters:new Set,controller:c,progress:{total:0,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0},error:null,cleanupTimer:null};return gt.set(n,d),Qt.set(t.name,n),ne({kind:"run-launched",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await Ta({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:m=>{d.progress=m,gn(d,"progress",m)},onResult:m=>{!m.ok&&!m.skipped&&gn(d,"error",{session_id:m.session_id,reason:m.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),gn(d,"done",_o(d)),ne({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens,sessions_processed:d.progress.processed})}catch(m){let h=m instanceof Error?m.message:String(m??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=h,gn(d,"done",_o(d)),ne({kind:"run-failed",job_id:n,project:t.name,model:r,limit:o,origin:e.origin??null,reason:h,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{Hb(d)}})(),{jobId:n,reused:!1}}async function*Tu(e,t=0){let s=gt.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function yu(e){let t=gt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function fo(e){let t=gt.get(e);return t?_o(t):null}H();import{randomUUID as Wb}from"node:crypto";import{spawn as qb}from"node:child_process";import{execSync as Xb}from"node:child_process";var wu="claude-haiku-4-5-20251001",ft=new Map,ss=new Map,Jb=300*1e3;function Ru(e){return`${e.scope}:${e.target_id}:${e.mode}`}function es(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function Yb(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{ft.delete(e.jobId);let t=Ru(e.intent);ss.get(t)===e.jobId&&ss.delete(t)},Jb),e.cleanupTimer.unref?.())}function _t(e){return{jobId:e.jobId,scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,status:e.status,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,startedAt:e.startedAt,endedAt:e.endedAt,error:e.error,context_summary:e.context_summary}}function Au(){return f().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1224
1389
  oi.bug_signatures
1225
1390
  FROM session_output_index oi
1226
1391
  JOIN sessions s ON s.id = oi.session_id
1227
1392
  JOIN projects p ON p.id = s.project_id
1228
1393
  WHERE oi.bug_signatures IS NOT NULL
1229
- AND oi.bug_signatures != '[]'`).all()}function Tl(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Lh(e){let t=Sl(),s=[];for(let a of t)for(let c of Tl(a.bug_signatures)){let d=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||d===e)&&s.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(s.length===0)return null;let n=s[0],r=Array.from(new Set(s.map(a=>a.session_id))),o=Array.from(new Set(s.map(a=>a.project))).sort();return{message_hash:n.sig.message_hash??null,error_type:n.sig.error_type??null,snippet:(n.sig.snippet??"").slice(0,400),file:n.sig.file??null,occurrence_count:s.length,projects:o,member_session_ids:r}}function Ch(e){let t=Sl().filter(o=>o.project===e),s=new Map;for(let o of t)for(let a of Tl(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,d=s.get(c);d?(d.sigs.push(a),d.sessions.add(o.session_id)):s.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let n=new Map;try{let o=Array.from(s.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=f(),c=o.map(()=>"?").join(","),d=a.prepare(`SELECT message_hash, fix_summary
1394
+ AND oi.bug_signatures != '[]'`).all()}function ku(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Gb(e){let t=Au(),s=[];for(let a of t)for(let c of ku(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&s.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(s.length===0)return null;let n=s[0],r=Array.from(new Set(s.map(a=>a.session_id))),o=Array.from(new Set(s.map(a=>a.project))).sort();return{message_hash:n.sig.message_hash??null,error_type:n.sig.error_type??null,snippet:(n.sig.snippet??"").slice(0,400),file:n.sig.file??null,occurrence_count:s.length,projects:o,member_session_ids:r}}function zb(e){let t=Au().filter(o=>o.project===e),s=new Map;for(let o of t)for(let a of ku(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=s.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):s.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let n=new Map;try{let o=Array.from(s.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=f(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1230
1395
  FROM bug_signature_resolutions
1231
1396
  WHERE message_hash IN (${c})
1232
- AND unresolved_at IS NULL`).all(...o);for(let u of d)n.set(u.message_hash,{fix_summary:u.fix_summary})}}catch{}let r=[];for(let[o,a]of s){let c=a.first.message_hash??null,d=c?n.get(c)??null:null;r.push({cluster_id:c??o,error_type:a.first.error_type??null,snippet:(a.first.snippet??"").slice(0,200),file:a.first.file??null,occurrence_count:a.sessions.size,resolved:d!==null,fix_summary:d?.fix_summary??null})}return r.sort((o,a)=>a.occurrence_count-o.occurrence_count),r}function Ih(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function vh(e,t){let s=f(),n=Ih(t),r=[];for(let o of e.slice(0,8)){let a=s.prepare(`SELECT s.id, p.name AS project, s.auto_title
1397
+ AND unresolved_at IS NULL`).all(...o);for(let d of u)n.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of s){let c=a.first.message_hash??null,u=c?n.get(c)??null:null;r.push({cluster_id:c??o,error_type:a.first.error_type??null,snippet:(a.first.snippet??"").slice(0,200),file:a.first.file??null,occurrence_count:a.sessions.size,resolved:u!==null,fix_summary:u?.fix_summary??null})}return r.sort((o,a)=>a.occurrence_count-o.occurrence_count),r}function Kb(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function Vb(e,t){let s=f(),n=Kb(t),r=[];for(let o of e.slice(0,8)){let a=s.prepare(`SELECT s.id, p.name AS project, s.auto_title
1233
1398
  FROM sessions s
1234
1399
  JOIN projects p ON p.id = s.project_id
1235
- WHERE s.id = ?`).get(o);if(!a)continue;let c=[];if(n.length>0){let d=s.prepare(`SELECT rowid FROM messages
1400
+ WHERE s.id = ?`).get(o);if(!a)continue;let c=[];if(n.length>0){let u=s.prepare(`SELECT rowid FROM messages
1236
1401
  WHERE session_id = ?
1237
1402
  AND is_sidechain = 0
1238
1403
  AND content_text LIKE ?
1239
1404
  ORDER BY rowid
1240
- LIMIT 5`).all(o,`%${n}%`),u=new Set,g=0;for(let h of d){let b=s.prepare(`SELECT rowid, role, content_text FROM messages
1405
+ LIMIT 5`).all(o,`%${n}%`),d=new Set,m=0;for(let h of u){let b=s.prepare(`SELECT rowid, role, content_text FROM messages
1241
1406
  WHERE session_id = ?
1242
1407
  AND is_sidechain = 0
1243
1408
  AND rowid BETWEEN ? AND ?
1244
- ORDER BY rowid`).all(o,h.rowid-2,h.rowid+2);for(let S of b){if(u.has(S.rowid))continue;u.add(S.rowid);let y=(S.content_text??"").slice(0,800);if(g+y.length>4e3)break;g+=y.length,c.push({role:S.role??"unknown",content:y})}if(g>=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 jh="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 Mh(e,t){let s=[];s.push("[CONTEXT]"),s.push("Bug fingerprint:"),s.push(`- error_type: ${e.error_type??"unknown"}`),s.push(`- description: ${e.snippet||"(no snippet)"}`),s.push(`- file: ${e.file??"(no file recorded)"}`),s.push(`- occurrences: ${e.occurrence_count} sessions`),s.push(`- projects: ${e.projects.join(", ")}`),s.push(`- finding_id: ${e.message_hash??"(no hash)"}`),s.push(""),s.push("Member session excerpts:");for(let n of t){let r=n.auto_title??"(untitled)";if(s.push(`=== Session ${n.short_id} | ${n.project} | "${r}" ===`),n.excerpts.length===0)s.push("(no surrounding messages found for this snippet)");else for(let o of n.excerpts)s.push(`${o.role}: ${o.content}`);s.push("")}return s.join(`
1245
- `)}function Dh(e,t,s){let n=[];n.push("[CONTEXT]"),n.push(`Project: ${e}`),n.push(`Total clusters: ${s}`),n.push(""),n.push("Clusters (sorted by occurrence_count desc):");for(let r of t)n.push(`- cluster_id: ${r.cluster_id}`),n.push(` error_type: ${r.error_type??"unknown"}`),n.push(` snippet: ${r.snippet||"(none)"}`),r.file&&n.push(` file: ${r.file}`),n.push(` occurrence_count: ${r.occurrence_count}`),n.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&n.push(` fix_summary: ${r.fix_summary}`),n.push("");return t.length<s&&n.push(`(Showing top ${t.length} of ${s} clusters by occurrence.)`),n.join(`
1246
- `)}var Fh=`[TASK]
1409
+ ORDER BY rowid`).all(o,h.rowid-2,h.rowid+2);for(let T of b){if(d.has(T.rowid))continue;d.add(T.rowid);let S=(T.content_text??"").slice(0,800);if(m+S.length>4e3)break;m+=S.length,c.push({role:T.role??"unknown",content:S})}if(m>=4e3)break}}r.push({session_id:o,short_id:o.slice(0,8),project:a.project,auto_title:a.auto_title,excerpts:c})}return r}var Zb="You are analyzing extracted bug findings from a developer's past Claude Code sessions. You are NOT being asked to fix code. You are being asked to synthesize patterns, identify likely root causes, or prioritize concerns based ONLY on the structured findings you are given. Output Markdown only, no preamble, no apologies, no questions.";function Qb(e,t){let s=[];s.push("[CONTEXT]"),s.push("Bug fingerprint:"),s.push(`- error_type: ${e.error_type??"unknown"}`),s.push(`- description: ${e.snippet||"(no snippet)"}`),s.push(`- file: ${e.file??"(no file recorded)"}`),s.push(`- occurrences: ${e.occurrence_count} sessions`),s.push(`- projects: ${e.projects.join(", ")}`),s.push(`- finding_id: ${e.message_hash??"(no hash)"}`),s.push(""),s.push("Member session excerpts:");for(let n of t){let r=n.auto_title??"(untitled)";if(s.push(`=== Session ${n.short_id} | ${n.project} | "${r}" ===`),n.excerpts.length===0)s.push("(no surrounding messages found for this snippet)");else for(let o of n.excerpts)s.push(`${o.role}: ${o.content}`);s.push("")}return s.join(`
1410
+ `)}function eS(e,t,s){let n=[];n.push("[CONTEXT]"),n.push(`Project: ${e}`),n.push(`Total clusters: ${s}`),n.push(""),n.push("Clusters (sorted by occurrence_count desc):");for(let r of t)n.push(`- cluster_id: ${r.cluster_id}`),n.push(` error_type: ${r.error_type??"unknown"}`),n.push(` snippet: ${r.snippet||"(none)"}`),r.file&&n.push(` file: ${r.file}`),n.push(` occurrence_count: ${r.occurrence_count}`),n.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&n.push(` fix_summary: ${r.fix_summary}`),n.push("");return t.length<s&&n.push(`(Showing top ${t.length} of ${s} clusters by occurrence.)`),n.join(`
1411
+ `)}var tS=`[TASK]
1247
1412
  Output a Markdown synopsis with these sections:
1248
1413
 
1249
1414
  ## What this bug is
@@ -1265,7 +1430,7 @@ prefer "consider <pattern>" over "do <action>" unless the evidence
1265
1430
  is overwhelming.
1266
1431
 
1267
1432
  ## Confidence
1268
- One of: high / medium / low. With one sentence justifying the level.`,Ph=`[TASK]
1433
+ One of: high / medium / low. With one sentence justifying the level.`,sS=`[TASK]
1269
1434
  Output a Markdown response with these sections:
1270
1435
 
1271
1436
  ## Most likely root cause
@@ -1282,7 +1447,7 @@ list is acceptable but explicitly say "(none found)".
1282
1447
  At most 2 alternatives, each with one sentence why it is less likely.
1283
1448
 
1284
1449
  ## Confidence
1285
- high / medium / low + one-sentence justification.`,Uh=`[TASK]
1450
+ high / medium / low + one-sentence justification.`,nS=`[TASK]
1286
1451
  Output a Markdown response with these sections:
1287
1452
 
1288
1453
  ## Top 5 recurring concerns
@@ -1300,20 +1465,20 @@ Clusters that look small + high-confidence-fix. Empty list is fine.
1300
1465
  Clusters that suggest deeper issues (multiple files, repeated patterns).
1301
1466
 
1302
1467
  ## Confidence
1303
- high / medium / low.`;function $h(e,t){let s;return e.scope==="cluster"?s=e.mode==="root_cause"?Ph:Fh:s=Uh,[jh,"",t,"",s].join(`
1304
- `)}var Bh=null;var Xt;function Hh(){if(Xt)return Xt;try{Xt=xh("which claude",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{Xt="claude"}return Xt}function Wh(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
1468
+ high / medium / low.`;function rS(e,t){let s;return e.scope==="cluster"?s=e.mode==="root_cause"?sS:tS:s=nS,[Zb,"",t,"",s].join(`
1469
+ `)}var oS=null;var ts;function iS(){if(ts)return ts;try{ts=Xb("which claude",{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{ts="claude"}return ts}function aS(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
1305
1470
  `);for(;n!==-1;){let r=t.slice(0,n);t=t.slice(n+1),r.length>0&&e(r),n=t.indexOf(`
1306
- `)}}}function qh(e){return new Promise(t=>{let s=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],n="",r=0,o=0,a=null,c=Ah(Hh(),s,{stdio:["ignore","pipe","pipe"]}),d=()=>{try{c.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",d,{once:!0});let g=Wh(b=>{let S=b.trim();if(!S.startsWith("{"))return;let y;try{y=JSON.parse(S)}catch{return}if(!y||typeof y!="object")return;let k=y;if(k.type==="assistant"&&k.message?.content){for(let D of k.message.content)D?.type==="text"&&typeof D.text=="string"&&(n+=D.text,e.onPartial(D.text,n));let w=k.message?.usage;w&&(typeof w.input_tokens=="number"&&(r=Math.max(r,w.input_tokens)),typeof w.output_tokens=="number"&&(o=Math.max(o,w.output_tokens)))}});c.stdout.on("data",b=>g(b)),c.stderr.on("data",b=>{let S=b.toString("utf8");S.trim()&&(a=(a??"")+S)});let h=setTimeout(()=>{try{c.kill("SIGKILL")}catch{}},1800*1e3);c.on("close",b=>{clearTimeout(h),e.signal.removeEventListener("abort",d);let S=b===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${b}`);t({output_markdown:n,input_tokens:r,output_tokens:o,error:S})}),c.on("error",b=>{clearTimeout(h),e.signal.removeEventListener("abort",d),t({output_markdown:n,input_tokens:r,output_tokens:o,error:b instanceof Error?b.message:String(b)})})})}function Js(e){if(e.scope==="cluster"){let r=Lh(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=Ch(e.target_id);if(t.length===0)return null;let s=t.slice(0,30),n=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:s,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:n}}}function yl(e){let t=Js(e.intent);if(!t)return{error:e.intent.scope==="cluster"?`cluster "${e.intent.target_id}" not found in any extracted findings`:`project "${e.intent.target_id}" has no extracted findings to synthesize`};let s=bl(e.intent),n=Gt.get(s);if(n){let d=dt.get(n);if(d&&d.status==="running")return{jobId:n,reused:!0};Gt.delete(s)}let r=kh(),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 dt.set(r,c),Gt.set(s,r),ee({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 d;if(e.intent.scope==="cluster"&&t.cluster){let b=vh(t.cluster.member_session_ids,t.cluster.snippet);d=Mh(t.cluster,b)}else if(e.intent.scope==="project"&&t.project_clusters)d=Dh(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let u=$h(e.intent,d),h=await(Bh??qh)({prompt:u,model:e.intent.model,signal:o.signal,onPartial:(b,S)=>{c.output_markdown=S,Jt(c,"partial",lt(c))}});if(c.output_markdown=h.output_markdown,c.input_tokens=h.input_tokens,c.output_tokens=h.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),Jt(c,"done",lt(c)),ee({kind:"synth-cancelled",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else if(h.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=h.error,Jt(c,"done",lt(c)),ee({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:h.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),Jt(c,"done",lt(c)),ee({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{tl({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(d){let u=d instanceof Error?d.message:String(d??"unknown error");c.status="failed",c.endedAt=new Date().toISOString(),c.error=u,Jt(c,"done",lt(c)),ee({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:u,input_tokens:c.input_tokens,output_tokens:c.output_tokens})}finally{Oh(c)}})(),{jobId:r,reused:!1}}async function*wl(e,t=0){let s=dt.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function Rl(e){let t=dt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Dr(e){let t=dt.get(e);return t?lt(t):null}import{randomUUID as nE}from"node:crypto";H();import{randomUUID as Qh}from"node:crypto";H();var kl=10,Al=20;function Jh(e){if(!e)return!1;let t=e.split(`
1307
- `);if(t.length<4)return!1;let s=0,n=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?s++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&n++;return s/t.length>.7||n/t.length>.5}function Xh(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let s=new Float32Array(t),n=new Float32Array(e.buffer,e.byteOffset,t);return s.set(n),s}function xl(e){let t=0;for(let n=0;n<e.length;n++)t+=e[n]*e[n];if(t<=0)return!1;let s=1/Math.sqrt(t);for(let n=0;n<e.length;n++)e[n]*=s;return!0}function Fr(e){if(e.length===0)return null;let t=e[0].length,s=new Float32Array(t);for(let n of e)if(n.length===t)for(let r=0;r<t;r++)s[r]+=n[r];for(let n=0;n<t;n++)s[n]/=e.length;return xl(s)?s:null}function Nl(e){let t=new Map;if(e.length===0)return t;let s=f(),n=e.map(()=>"?").join(","),r=[];try{r=s.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1471
+ `)}}}function cS(e){return new Promise(t=>{let s=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],n="",r=0,o=0,a=null,c=qb(iS(),s,{stdio:["ignore","pipe","pipe"]}),u=()=>{try{c.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",u,{once:!0});let m=aS(b=>{let T=b.trim();if(!T.startsWith("{"))return;let S;try{S=JSON.parse(T)}catch{return}if(!S||typeof S!="object")return;let w=S;if(w.type==="assistant"&&w.message?.content){for(let D of w.message.content)D?.type==="text"&&typeof D.text=="string"&&(n+=D.text,e.onPartial(D.text,n));let R=w.message?.usage;R&&(typeof R.input_tokens=="number"&&(r=Math.max(r,R.input_tokens)),typeof R.output_tokens=="number"&&(o=Math.max(o,R.output_tokens)))}});c.stdout.on("data",b=>m(b)),c.stderr.on("data",b=>{let T=b.toString("utf8");T.trim()&&(a=(a??"")+T)});let h=setTimeout(()=>{try{c.kill("SIGKILL")}catch{}},1800*1e3);c.on("close",b=>{clearTimeout(h),e.signal.removeEventListener("abort",u);let T=b===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${b}`);t({output_markdown:n,input_tokens:r,output_tokens:o,error:T})}),c.on("error",b=>{clearTimeout(h),e.signal.removeEventListener("abort",u),t({output_markdown:n,input_tokens:r,output_tokens:o,error:b instanceof Error?b.message:String(b)})})})}function _n(e){if(e.scope==="cluster"){let r=Gb(e.target_id);return r?{cluster:r,context_summary:{cluster_count:1,session_count:r.member_session_ids.length,findings_count:r.occurrence_count}}:null}let t=zb(e.target_id);if(t.length===0)return null;let s=t.slice(0,30),n=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:s,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:n}}}function xu(e){let t=_n(e.intent);if(!t)return{error:e.intent.scope==="cluster"?`cluster "${e.intent.target_id}" not found in any extracted findings`:`project "${e.intent.target_id}" has no extracted findings to synthesize`};let s=Ru(e.intent),n=ss.get(s);if(n){let u=ft.get(n);if(u&&u.status==="running")return{jobId:n,reused:!0};ss.delete(s)}let r=Wb(),o=new AbortController,a=new Date().toISOString(),c={jobId:r,intent:e.intent,status:"running",startedAt:a,endedAt:null,events:[],waiters:new Set,controller:o,output_markdown:"",input_tokens:0,output_tokens:0,error:null,context_summary:t.context_summary,cleanupTimer:null};return ft.set(r,c),ss.set(s,r),ne({kind:"synth-launched",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:`${e.intent.scope}/${e.intent.mode}/${e.intent.target_id}`}),(async()=>{await Promise.resolve();try{let u;if(e.intent.scope==="cluster"&&t.cluster){let b=Vb(t.cluster.member_session_ids,t.cluster.snippet);u=Qb(t.cluster,b)}else if(e.intent.scope==="project"&&t.project_clusters)u=eS(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=rS(e.intent,u),h=await(oS??cS)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(b,T)=>{c.output_markdown=T,es(c,"partial",_t(c))}});if(c.output_markdown=h.output_markdown,c.input_tokens=h.input_tokens,c.output_tokens=h.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),es(c,"done",_t(c)),ne({kind:"synth-cancelled",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else if(h.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=h.error,es(c,"done",_t(c)),ne({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:h.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),es(c,"done",_t(c)),ne({kind:"synth-completed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});try{iu({scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,output_markdown:c.output_markdown,input_tokens:c.input_tokens,output_tokens:c.output_tokens,job_id:r})}catch(b){console.error("[synthesize-jobs] failed to persist synthesis result:",b)}}}catch(u){let d=u instanceof Error?u.message:String(u??"unknown error");c.status="failed",c.endedAt=new Date().toISOString(),c.error=d,es(c,"done",_t(c)),ne({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:d,input_tokens:c.input_tokens,output_tokens:c.output_tokens})}finally{Yb(c)}})(),{jobId:r,reused:!1}}async function*Nu(e,t=0){let s=ft.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function Ou(e){let t=ft.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function ho(e){let t=ft.get(e);return t?_t(t):null}import{randomUUID as TS}from"node:crypto";H();Ls();import{randomUUID as hS}from"node:crypto";H();var Lu=10,Cu=20;function lS(e){if(!e)return!1;let t=e.split(`
1472
+ `);if(t.length<4)return!1;let s=0,n=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?s++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&n++;return s/t.length>.7||n/t.length>.5}function uS(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let s=new Float32Array(t),n=new Float32Array(e.buffer,e.byteOffset,t);return s.set(n),s}function vu(e){let t=0;for(let n=0;n<e.length;n++)t+=e[n]*e[n];if(t<=0)return!1;let s=1/Math.sqrt(t);for(let n=0;n<e.length;n++)e[n]*=s;return!0}function Eo(e){if(e.length===0)return null;let t=e[0].length,s=new Float32Array(t);for(let n of e)if(n.length===t)for(let r=0;r<t;r++)s[r]+=n[r];for(let n=0;n<t;n++)s[n]/=e.length;return vu(s)?s:null}function Iu(e){let t=new Map;if(e.length===0)return t;let s=f(),n=e.map(()=>"?").join(","),r=[];try{r=s.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1308
1473
  cm.text AS text, v.embedding AS embedding
1309
1474
  FROM chunk_meta cm
1310
1475
  JOIN vec_chunks v ON v.rowid = cm.rowid
1311
1476
  WHERE cm.stale = 0
1312
1477
  AND cm.session_id IN (${n})
1313
- 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 d=c.filter(M=>!Jh(M.text)),u=d.length>0?d:c,g=[];for(let M of u){let B=Xh(M.embedding);B&&xl(B)&&g.push(B)}if(g.length===0)continue;let h=Math.min(kl,g.length),b=Math.max(0,g.length-kl),S=g.slice(0,h),y=g.slice(b),k=Fr(S),w=Fr(y),D=Fr(g),L=new Map;for(let M=0;M<S.length&&L.size<Al;M++)L.set(M,S[M]);for(let M=0;M<y.length&&L.size<Al;M++)L.set(b+M,y[M]);let X=Array.from(L.values());t.set(a,{session_id:a,full_mean:D,head_pool:k,tail_pool:w,sample_chunks:X})}return t}function Gh(e,t){if(e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function Ol(e,t=.8){let s=new Map,n=[],r=[];for(let[u,g]of e)g.full_mean&&(n.push(u),r.push(g.full_mean));if(n.length===0)return s;let o=new Int32Array(n.length);for(let u=0;u<o.length;u++)o[u]=u;let a=u=>{let g=u;for(;o[g]!==g;)g=o[g];let h=u;for(;o[h]!==g;){let b=o[h];o[h]=g,h=b}return g},c=(u,g)=>{let h=a(u),b=a(g);h!==b&&(o[h]=b)};for(let u=0;u<n.length;u++)for(let g=u+1;g<n.length;g++)Gh(r[u],r[g])>=t&&c(u,g);let d=new Map;for(let u=0;u<n.length;u++){let g=a(u),h=d.get(g);h===void 0&&(h=d.size,d.set(g,h)),s.set(n[u],h)}return s}var Ae={lo:.4,hi:.7},ut="claude-haiku-4-5-20251001",pt=50,Ll=1500,Cl=50,Yh={same_workflow:.15,unrelated:-.2,unsure:0};function Il(e,t,s=Ae){if(s.lo>s.hi)throw new Error(`borderline band invalid: lo=${s.lo} > hi=${s.hi}`);let n=[];for(let r of e){if(r.confidence<s.lo||r.confidence>s.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||n.push({parent:o,child:a,step1:r})}return n.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),n}function vl(e,t=Ae){let s=0;for(let n of e)n.confidence>=t.lo&&n.confidence<=t.hi&&s++;return s}function Kh(e,t){let s=e.replace(/\s+/g," ").trim();return s.length>t?s.slice(0,t-1)+"\u2026":s}function zh(e,t){if(e===null)return"unknown";let s=t-e;if(s<0)return"overlap";let n=Math.round(s/6e4);if(n<60)return`${n}m`;let r=Math.round(s/36e5);return r<24?`${r}h`:`${Math.round(s/864e5)}d`}function Vh(e){let s=e.parent.recent_user_messages,n=e.child.recent_user_messages,r=s.slice(0,3),o=s.length>3?s.slice(-3):[],a=n.slice(0,3),c=u=>u.length===0?" (none captured)":u.map(g=>` - ${Kh(g,500)}`).join(`
1314
- `),d=zh(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(u=>` - ${u}`).join(`
1315
- `),` step1_confidence: ${e.step1.confidence.toFixed(2)}`,"","PARENT SESSION","First user messages:",c(r),"Last user messages:",c(o),"",`CHILD SESSION (gap from parent: ${d})`,"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(`
1316
- `)}function Zh(e){if(!e)return null;let t=e.trim();if(!t)return null;let s=t;try{let d=JSON.parse(t);typeof d.result=="string"&&(s=d.result.trim())}catch{}let n=s.match(/\{[\s\S]*\}/);if(!n)return null;let r;try{r=JSON.parse(n[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,a=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(a!=="same_workflow"&&a!=="unrelated"&&a!=="unsure")return null;let c=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:a,reason:c,delta:Yh[a]}}async function jl(e,t={}){if(t.signal?.aborted)return null;let s=t.model??ut,n=Vh(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(ge(),Pe));if(!a.isClaudeCliAvailable())return null;r=(c,d)=>a.spawnClaudePrompt(c,[],d)}catch{return null}let o;try{o=await Promise.race([r(n,{model:s}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:Zh(o.stdout)}function Ml(e){let t=[];for(let s of e.proposals){let n=`${s.parent_id}::${s.child_id}`,r=e.rescored.get(n);if(!r){t.push(s);continue}let o=Math.max(0,Math.min(1,s.confidence+r.delta));if(o<e.applyThreshold)continue;let a=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...s,confidence:o,reasons:[...s.reasons,a]})}return t}function Dl(e){return`${e.parent_id}::${e.child_id}`}async function eE(e){if(e.signal?.aborted)return null;let t,s;try{({spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(ge(),Pe)))}catch{return null}if(!s())return null;let n=[];for(let o of e.sessionIds){let a=e.rowById.get(o);if(!a)continue;let c=a.alias||a.auto_title||(a.first_user_message?a.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");n.push(`- ${c}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1478
+ ORDER BY cm.session_id, cm.rowid ASC`).all(...e)}catch{return t}if(r.length===0)return t;let o=new Map;for(let a of r){let c=o.get(a.session_id);c||(c=[],o.set(a.session_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(M=>!lS(M.text)),d=u.length>0?u:c,m=[];for(let M of d){let W=uS(M.embedding);W&&vu(W)&&m.push(W)}if(m.length===0)continue;let h=Math.min(Lu,m.length),b=Math.max(0,m.length-Lu),T=m.slice(0,h),S=m.slice(b),w=Eo(T),R=Eo(S),D=Eo(m),O=new Map;for(let M=0;M<T.length&&O.size<Cu;M++)O.set(M,T[M]);for(let M=0;M<S.length&&O.size<Cu;M++)O.set(b+M,S[M]);let Y=Array.from(O.values());t.set(a,{session_id:a,full_mean:D,head_pool:w,tail_pool:R,sample_chunks:Y})}return t}function dS(e,t){if(e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function ju(e,t=.8){let s=new Map,n=[],r=[];for(let[d,m]of e)m.full_mean&&(n.push(d),r.push(m.full_mean));if(n.length===0)return s;let o=new Int32Array(n.length);for(let d=0;d<o.length;d++)o[d]=d;let a=d=>{let m=d;for(;o[m]!==m;)m=o[m];let h=d;for(;o[h]!==m;){let b=o[h];o[h]=m,h=b}return m},c=(d,m)=>{let h=a(d),b=a(m);h!==b&&(o[h]=b)};for(let d=0;d<n.length;d++)for(let m=d+1;m<n.length;m++)dS(r[d],r[m])>=t&&c(d,m);let u=new Map;for(let d=0;d<n.length;d++){let m=a(d),h=u.get(m);h===void 0&&(h=u.size,u.set(m,h)),s.set(n[d],h)}return s}var Ce={lo:.4,hi:.7},ht="claude-haiku-4-5-20251001",Et=50,Mu=1500,Du=50,pS={same_workflow:.15,unrelated:-.2,unsure:0};function Fu(e,t,s=Ce){if(s.lo>s.hi)throw new Error(`borderline band invalid: lo=${s.lo} > hi=${s.hi}`);let n=[];for(let r of e){if(r.confidence<s.lo||r.confidence>s.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||n.push({parent:o,child:a,step1:r})}return n.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),n}function Pu(e,t=Ce){let s=0;for(let n of e)n.confidence>=t.lo&&n.confidence<=t.hi&&s++;return s}function mS(e,t){let s=e.replace(/\s+/g," ").trim();return s.length>t?s.slice(0,t-1)+"\u2026":s}function gS(e,t){if(e===null)return"unknown";let s=t-e;if(s<0)return"overlap";let n=Math.round(s/6e4);if(n<60)return`${n}m`;let r=Math.round(s/36e5);return r<24?`${r}h`:`${Math.round(s/864e5)}d`}function _S(e){let s=e.parent.recent_user_messages,n=e.child.recent_user_messages,r=s.slice(0,3),o=s.length>3?s.slice(-3):[],a=n.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(m=>` - ${mS(m,500)}`).join(`
1479
+ `),u=gS(e.parent.ended_at_ms??e.parent.started_at_ms,e.child.started_at_ms);return["You are evaluating whether two Claude Code sessions belong to the same continuous workflow.","","A deterministic scorer rated this pair in the borderline band. Its signals:",e.step1.reasons.length===0?" (no signals fired strongly)":e.step1.reasons.map(d=>` - ${d}`).join(`
1480
+ `),` step1_confidence: ${e.step1.confidence.toFixed(2)}`,"","PARENT SESSION","First user messages:",c(r),"Last user messages:",c(o),"",`CHILD SESSION (gap from parent: ${u})`,"First user messages:",c(a),"","Reply with EXACTLY one JSON object on a single line, no prose, no markdown:",'{"verdict":"same_workflow"|"unrelated"|"unsure","reason":"<one short sentence>"}',"","Guidance:",'- "same_workflow" = the child is clearly continuing what the parent was doing.','- "unrelated" = different problem, different files, different intent.','- "unsure" = could go either way; do not force a verdict.'].join(`
1481
+ `)}function fS(e){if(!e)return null;let t=e.trim();if(!t)return null;let s=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(s=u.result.trim())}catch{}let n=s.match(/\{[\s\S]*\}/);if(!n)return null;let r;try{r=JSON.parse(n[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,a=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(a!=="same_workflow"&&a!=="unrelated"&&a!=="unsure")return null;let c=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:a,reason:c,delta:pS[a]}}async function Uu(e,t={}){if(t.signal?.aborted)return null;let s=t.model??ht,n=_S(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(he(),We));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(n,{model:s}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:fS(o.stdout)}function $u(e){let t=[];for(let s of e.proposals){let n=`${s.parent_id}::${s.child_id}`,r=e.rescored.get(n);if(!r){t.push(s);continue}let o=Math.max(0,Math.min(1,s.confidence+r.delta));if(o<e.applyThreshold)continue;let a=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...s,confidence:o,reasons:[...s.reasons,a]})}return t}function Bu(e){return`${e.parent_id}::${e.child_id}`}async function ES(e){if(e.signal?.aborted)return null;let t,s;try{({spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(he(),We)))}catch{return null}if(!s())return null;let n=[];for(let o of e.sessionIds){let a=e.rowById.get(o);if(!a)continue;let c=a.alias||a.auto_title||(a.first_user_message?a.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");n.push(`- ${c}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1317
1482
  - 4 to 8 words
1318
1483
  - Title-case, no trailing punctuation
1319
1484
  - Capture the WORKFLOW (e.g., "Update Remotion deps + lint cleanup"), not any one session
@@ -1324,12 +1489,12 @@ Sessions:
1324
1489
  `+n.join(`
1325
1490
  `)+`
1326
1491
 
1327
- Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(h=>{e.signal&&(a=()=>h(null),e.signal.addEventListener("abort",a,{once:!0}))}),d=await Promise.race([o,c]);if(a&&e.signal&&e.signal.removeEventListener("abort",a),!d||!d.success)return null;let u=d.stdout.trim();if(!u)return null;let g;try{let h=JSON.parse(u);g=typeof h.result=="string"?h.result:u}catch{g=u}return g=g.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),g?g.length>80?g.slice(0,77)+"...":g:null}catch{return null}}function Pr(e){return tE(e)}function Ur(e){let t=new Map;for(let o of e){let a=t.get(o.project);a||(a=[],t.set(o.project,a)),a.push(o)}let s=e.map(o=>o.id),n=new Map;try{n=Nl(s)}catch{n=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let g of a){let h=n.get(g.id);h&&c.set(g.id,h)}let d=Ol(c,.8),u=[];for(let g of a){let h=sE(g,n,d);h&&u.push(h)}r.set(o,u)}return{byProject:t,scannablesByProject:r}}function Pl(e){return f().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1492
+ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(h=>{e.signal&&(a=()=>h(null),e.signal.addEventListener("abort",a,{once:!0}))}),u=await Promise.race([o,c]);if(a&&e.signal&&e.signal.removeEventListener("abort",a),!u||!u.success)return null;let d=u.stdout.trim();if(!d)return null;let m;try{let h=JSON.parse(d);m=typeof h.result=="string"?h.result:d}catch{m=d}return m=m.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),m?m.length>80?m.slice(0,77)+"...":m:null}catch{return null}}function bo(e){return bS(e)}function So(e){let t=new Map;for(let o of e){let a=t.get(o.project);a||(a=[],t.set(o.project,a)),a.push(o)}let s=e.map(o=>o.id),n=new Map;try{n=Iu(s)}catch{n=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let m of a){let h=n.get(m.id);h&&c.set(m.id,h)}let u=ju(c,.8),d=[];for(let m of a){let h=SS(m,n,u);h&&d.push(h)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function Wu(e){return f().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1328
1493
  FROM threads t
1329
1494
  JOIN thread_edges te ON te.thread_id = t.id
1330
1495
  JOIN sessions s ON s.id = te.session_id
1331
1496
  JOIN projects p ON p.id = s.project_id
1332
- WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function tE(e){let t=f(),s={},n="1=1 AND s.message_count > 2";return n+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(n+=" AND p.name = @project",s.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1497
+ WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function bS(e){let t=f(),s={},n="1=1 AND s.message_count > 2";return n+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(n+=" AND p.name = @project",s.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1333
1498
  s.first_user_message, s.auto_title,
1334
1499
  NULLIF(sa.alias, '') AS alias,
1335
1500
  s.file_path
@@ -1337,56 +1502,27 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1337
1502
  JOIN projects p ON p.id = s.project_id
1338
1503
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1339
1504
  WHERE ${n}
1340
- ORDER BY p.name ASC, s.started_at ASC`).all(s)}function sE(e,t,s){if(!e.started_at)return null;let n=Date.parse(e.started_at);if(!Number.isFinite(n))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=ys(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:n,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:a?.full_mean??null,head_pool:a?.head_pool??null,tail_pool:a?.tail_pool??null,sample_chunks:a?.sample_chunks??[],cluster_id:s?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function Fl(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 Ul(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??Ae,s=e.rescore.cap??pt,n=e.rescore.model??ut,r=new Map(e.scannables.map(b=>[b.id,b])),o=Il(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>s)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let a=new Map,c=0,d=0,u=0,g=0;for(let b=0;b<o.length&&!e.signal?.aborted;b++){let S=o[b],y=await jl(S,{model:n,signal:e.signal});y?(a.set(Dl(S.step1),y),y.verdict==="same_workflow"?c++:y.verdict==="unrelated"?d++:u++):g++,e.onProgress?.({phase:"rescoring",current:b+1,total:o.length,verdict:y?.verdict??"failed"})}return{proposals:Ml({proposals:e.proposals,rescored:a,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:c,demoted:d,unsure:u,failed:g,capped:!1}}async function $l(e){let t=f(),s=new Map(e.rows.map(u=>[u.id,u])),n=Qi(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let u of n){if(c++,e.signal?.aborted)break;let g=s.get(u.rootId),h=Fl(g);if(!e.llmNames){a.set(u.rootId,h),e.onProgress?.({phase:"naming",current:c,total:n.length,thread_name:h});continue}let S=await eE({rootRow:g,sessionIds:u.sessionIds,rowById:s,signal:e.signal,model:e.model})??h;a.set(u.rootId,S),e.onProgress?.({phase:"naming",current:c,total:n.length,thread_name:S})}return t.transaction(()=>{for(let u of n){if(!a.has(u.rootId))continue;let g=s.get(u.rootId),h=`auto-scan-${Qh()}`,b=a.get(u.rootId)??Fl(g),S=`Auto-detected workflow chain (${u.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(h,b,S,r),t.prepare(`INSERT INTO thread_edges
1505
+ ORDER BY p.name ASC, s.started_at ASC`).all(s)}function SS(e,t,s){if(!e.started_at)return null;let n=Date.parse(e.started_at);if(!Number.isFinite(n))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=Ks(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:n,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:a?.full_mean??null,head_pool:a?.head_pool??null,tail_pool:a?.tail_pool??null,sample_chunks:a?.sample_chunks??[],cluster_id:s?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function Hu(e){let t=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,60).replace(/\n/g," ").trim():`thread from ${e.id.slice(0,8)}`);return t.length>80?t.slice(0,77)+"...":t}async function qu(e){if(!e.rescore.enabled)return{proposals:e.proposals,ran:!1,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};let t=e.rescore.band??Ce,s=e.rescore.cap??Et,n=e.rescore.model??ht,r=new Map(e.scannables.map(b=>[b.id,b])),o=Fu(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>s)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let a=new Map,c=0,u=0,d=0,m=0;for(let b=0;b<o.length&&!e.signal?.aborted;b++){let T=o[b],S=await Uu(T,{model:n,signal:e.signal});S?(a.set(Bu(T.step1),S),S.verdict==="same_workflow"?c++:S.verdict==="unrelated"?u++:d++):m++,e.onProgress?.({phase:"rescoring",current:b+1,total:o.length,verdict:S?.verdict??"failed"})}return{proposals:$u({proposals:e.proposals,rescored:a,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:c,demoted:u,unsure:d,failed:m,capped:!1}}async function Xu(e){let t=f(),s=new Map(e.rows.map(d=>[d.id,d])),n=Mc(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of n){if(c++,e.signal?.aborted)break;let m=s.get(d.rootId),h=Hu(m);if(!e.llmNames){a.set(d.rootId,h),e.onProgress?.({phase:"naming",current:c,total:n.length,thread_name:h});continue}let T=await ES({rootRow:m,sessionIds:d.sessionIds,rowById:s,signal:e.signal,model:e.model})??h;a.set(d.rootId,T),e.onProgress?.({phase:"naming",current:c,total:n.length,thread_name:T})}return t.transaction(()=>{for(let d of n){if(!a.has(d.rootId))continue;let m=s.get(d.rootId),h=`auto-scan-${hS()}`,b=a.get(d.rootId)??Hu(m),T=`Auto-detected workflow chain (${d.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(h,b,T,r),t.prepare(`INSERT INTO thread_edges
1341
1506
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1342
- VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(h,u.rootId,r);let y=new Map;for(let k of e.edges)u.sessionIds.includes(k.child_id)&&y.set(k.child_id,k);for(let k of u.sessionIds){if(k===u.rootId)continue;let w=y.get(k);w&&t.prepare(`INSERT INTO thread_edges
1507
+ VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(h,d.rootId,r);let S=new Map;for(let w of e.edges)d.sessionIds.includes(w.child_id)&&S.set(w.child_id,w);for(let w of d.sessionIds){if(w===d.rootId)continue;let R=S.get(w);R&&t.prepare(`INSERT INTO thread_edges
1343
1508
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1344
- VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(h,k,w.parent_id,w.confidence,r)}o.push({thread_id:h,name:b,session_count:u.sessionIds.length})}})(),{project:e.project,threads:o}}var gt=new Map,Yt=new Map,rE=300*1e3;function mt(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function oE(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{gt.delete(e.jobId),Yt.get(e.project)===e.jobId&&Yt.delete(e.project)},rE),e.cleanupTimer.unref?.())}function Xs(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 iE=500,aE=30,$r="claude-haiku-4-5-20251001",Bl={"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 Hl(e){return Bl[e]??Bl[$r]}function Wl(e){let t=typeof e.threshold=="number"?e.threshold:Ss;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let s=e.model??$r,n="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Ae.lo,hi:e.llm_rescore?.band_hi??Ae.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??ut,c=Pr({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:Ce(0,s),accuracy_caveat:n,model:s,llm_rescore:r?{enabled:!0,band:o,cap:pt,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:Ce(0,a),exceeds_cap:!1,model:a}:null};let{byProject:d,scannablesByProject:u}=Ur(c),g=d.get(e.project)??[],h=u.get(e.project)??[],b=r?Math.min(t,o.lo):t,S=ze(h,b),y=r?S.filter(p=>p.confidence>=t):S,k=ql(y),w=k*iE,D=k*aE,L=Hl(s),X=w/1e6*L.in,M=D/1e6*L.out,B=X+M,se=Ce(k,s),i=Pl(e.project),l=null;if(r){let p=vl(S,o),m=Hl(a),_=p*Ll,E=p*Cl,T=_/1e6*m.in+E/1e6*m.out;l={enabled:!0,band:o,cap:pt,estimated_borderline_pairs:p,estimated_input_tokens_max:_,estimated_output_tokens_max:E,estimated_cost_usd_max:Math.round(T*1e4)/1e4,plan_window_estimate:Ce(p,a),exceeds_cap:p>pt,model:a}}return{eligible_sessions:g.length,proposed_edges:y.length,estimated_threads:k,estimated_llm_calls:k,estimated_input_tokens_max:w,estimated_output_tokens_max:D,estimated_cost_usd_max:Math.round(B*1e4)/1e4,existing_auto_scan_threads:i,plan_window_estimate:se,accuracy_caveat:n,model:s,llm_rescore:l}}function ql(e){let t=new Map,s=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},n=(o,a)=>{let c=s(o),d=s(a);c!==d&&t.set(c,d)};for(let o of e)t.has(o.parent_id)||t.set(o.parent_id,o.parent_id),t.has(o.child_id)||t.set(o.child_id,o.child_id),n(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(s(o));return r.size}function Jl(e){let t=typeof e.threshold=="number"?e.threshold:Ss;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let s=e.llm_names??!0,n=e.model??$r,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Ae.lo,hi:e.llm_rescore?.band_hi??Ae.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??ut,c=Yt.get(e.project);if(c){let B=gt.get(c);if(B&&B.status==="running")return{jobId:c,reused:!0};Yt.delete(e.project)}let d=Pr({project:e.project});if(d.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:u,scannablesByProject:g}=Ur(d),h=u.get(e.project),b=g.get(e.project);if(!h||!b)return{error:`project "${e.project}" not found`};let S=r?Math.min(t,o.lo):t,y=ze(b,S),k=r?y.filter(B=>B.confidence>=t):y;if(y.length===0||k.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let w=nE(),D=new AbortController,L=ql(k),X=new Date().toISOString(),M={jobId:w,project:e.project,threshold:t,llmNames:s,status:"running",startedAt:X,endedAt:null,events:[],waiters:new Set,controller:D,totalThreads:L,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 gt.set(w,M),Yt.set(e.project,w),(async()=>{await Promise.resolve();try{mt(M,"progress",{phase:"linking",edges_proposed:y.length,estimated_threads:L});let B=y;if(r){let i=await Ul({proposals:y,scannables:b,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:D.signal,onProgress:l=>{l.phase==="rescoring"&&mt(M,"progress",{phase:"rescoring",current:l.current,total:l.total,verdict:l.verdict})}});B=i.proposals,M.rescore={enabled:!0,considered:i.considered,promoted:i.promoted,demoted:i.demoted,unsure:i.unsure,failed:i.failed,capped:i.capped}}else B=y.filter(i=>i.confidence>=t);if(B.length===0){M.status=D.signal.aborted?"cancelled":"done",M.endedAt=new Date().toISOString(),mt(M,"done",{...Xs(M),threads:[]});return}let se=await $l({project:e.project,rows:h,edges:B,scannables:b,llmNames:s,model:n,signal:D.signal,onProgress:i=>{i.phase==="naming"&&(M.threadsNamed=i.current,M.currentThreadName=i.thread_name??null,mt(M,"progress",{phase:"naming",current:i.current,total:i.total,thread_name:i.thread_name}))}});M.threadsCreated=se.threads.length,M.edgesWritten=B.length+se.threads.length,M.status=D.signal.aborted?"cancelled":"done",M.endedAt=new Date().toISOString(),mt(M,"done",{...Xs(M),threads:se.threads})}catch(B){M.status="failed",M.endedAt=new Date().toISOString(),M.error=B instanceof Error?B.message:String(B??"unknown error"),mt(M,"done",Xs(M))}finally{oE(M)}})(),{jobId:w,reused:!1}}async function*Xl(e,t=0){let s=gt.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function Gl(e){let t=gt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Br(e){let t=gt.get(e);return t?Xs(t):null}rs();import{randomUUID as cE}from"node:crypto";var Gs=new Map;function Yl(e){let t={id:cE(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return Gs.set(t.id,t),t}function Ys(e){return Gs.get(e)}function _t(e,t){for(let s of e.listeners)s(t)}function Kl(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function zl(e){let t=Gs.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),_t(t,{type:"status",status:"cancelled"}),!0):!1}function Vl(e){return Gs.delete(e)}Ge();function Ks(e){let{session:t,knownTags:s,minTags:n,maxTags:r}=e,o=s.slice(0,50).map(c=>c.tag).join(", "),a=t.current_tags.length>0?`already applied, do not repeat: [${t.current_tags.join(", ")}]`:"currently has no tags";return[`You are tagging a software engineering session. Produce ${n}-${r} concise, lowercase, hyphen-separated tags that describe:`," - the domain or subsystem touched (auth, db, frontend, ...)"," - the kind of work (bugfix, feature, refactor, research, ...)"," - any specific tool, library, or product if prominent","","Prefer tags from the known-tags list below when applicable \u2014 consistency over creativity. Never invent marketing-sounding labels. Never tag based on speculation; only on observed work.","",`known tags (most used first, up to 50): ${o||"(none yet)"}`,"","Session:",` project: ${t.project}`,t.alias?` alias: ${JSON.stringify(t.alias)}`:" alias: (none)",t.git_branch?` git_branch: ${t.git_branch}`:"",` ${a}`," first_user_message:",Zl(t.first_user_message," ")," message_sample:",Zl(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${n}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1345
- `)}function Zl(e,t){return e.split(`
1509
+ VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(h,w,R.parent_id,R.confidence,r)}o.push({thread_id:h,name:b,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var St=new Map,ns=new Map,yS=300*1e3;function bt(e,t,s){e.events.push({id:e.events.length+1,kind:t,data:s});for(let n of e.waiters)n();e.waiters.clear()}function wS(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{St.delete(e.jobId),ns.get(e.project)===e.jobId&&ns.delete(e.project)},yS),e.cleanupTimer.unref?.())}function fn(e){return{jobId:e.jobId,project:e.project,threshold:e.threshold,llm_names:e.llmNames,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total_threads:e.totalThreads,threads_named:e.threadsNamed,edges_written:e.edgesWritten,threads_created:e.threadsCreated,current_thread_name:e.currentThreadName,error:e.error,rescore:e.rescore}}var RS=500,AS=30,To="claude-haiku-4-5-20251001",Ju={"claude-haiku-4-5-20251001":{in:1,out:5,label:"Haiku 4.5"},"claude-sonnet-4-6":{in:3,out:15,label:"Sonnet 4.6"},"claude-opus-4-7":{in:15,out:75,label:"Opus 4.7"}};function Yu(e){return Ju[e]??Ju[To]}function Gu(e){let t=typeof e.threshold=="number"?e.threshold:Gs;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let s=e.model??To,n="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Ce.lo,hi:e.llm_rescore?.band_hi??Ce.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??ht,c=bo({project:e.project});if(c.length===0)return{eligible_sessions:0,proposed_edges:0,estimated_threads:0,estimated_llm_calls:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,existing_auto_scan_threads:0,plan_window_estimate:Me(0,s),accuracy_caveat:n,model:s,llm_rescore:r?{enabled:!0,band:o,cap:Et,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:Me(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=So(c),m=u.get(e.project)??[],h=d.get(e.project)??[],b=r?Math.min(t,o.lo):t,T=ot(h,b),S=r?T.filter(i=>i.confidence>=t):T,w=zu(S),R=w*RS,D=w*AS,O=Yu(s),Y=R/1e6*O.in,M=D/1e6*O.out,W=Y+M,oe=Me(w,s),$=Wu(e.project),se=null;if(r){let i=Pu(T,o),l=Yu(a),p=i*Mu,g=i*Du,_=p/1e6*l.in+g/1e6*l.out;se={enabled:!0,band:o,cap:Et,estimated_borderline_pairs:i,estimated_input_tokens_max:p,estimated_output_tokens_max:g,estimated_cost_usd_max:Math.round(_*1e4)/1e4,plan_window_estimate:Me(i,a),exceeds_cap:i>Et,model:a}}return{eligible_sessions:m.length,proposed_edges:S.length,estimated_threads:w,estimated_llm_calls:w,estimated_input_tokens_max:R,estimated_output_tokens_max:D,estimated_cost_usd_max:Math.round(W*1e4)/1e4,existing_auto_scan_threads:$,plan_window_estimate:oe,accuracy_caveat:n,model:s,llm_rescore:se}}function zu(e){let t=new Map,s=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},n=(o,a)=>{let c=s(o),u=s(a);c!==u&&t.set(c,u)};for(let o of e)t.has(o.parent_id)||t.set(o.parent_id,o.parent_id),t.has(o.child_id)||t.set(o.child_id,o.child_id),n(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(s(o));return r.size}function Ku(e){let t=typeof e.threshold=="number"?e.threshold:Gs;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let s=e.llm_names??!0,n=e.model??To,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Ce.lo,hi:e.llm_rescore?.band_hi??Ce.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??ht,c=ns.get(e.project);if(c){let W=St.get(c);if(W&&W.status==="running")return{jobId:c,reused:!0};ns.delete(e.project)}let u=bo({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:m}=So(u),h=d.get(e.project),b=m.get(e.project);if(!h||!b)return{error:`project "${e.project}" not found`};let T=r?Math.min(t,o.lo):t,S=ot(b,T),w=r?S.filter(W=>W.confidence>=t):S;if(S.length===0||w.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let R=TS(),D=new AbortController,O=zu(w),Y=new Date().toISOString(),M={jobId:R,project:e.project,threshold:t,llmNames:s,status:"running",startedAt:Y,endedAt:null,events:[],waiters:new Set,controller:D,totalThreads:O,threadsNamed:0,edgesWritten:0,threadsCreated:0,currentThreadName:null,error:null,cleanupTimer:null,rescore:r?{enabled:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1}:null};return St.set(R,M),ns.set(e.project,R),(async()=>{await Promise.resolve();try{bt(M,"progress",{phase:"linking",edges_proposed:S.length,estimated_threads:O});let W=S;if(r){let $=await qu({proposals:S,scannables:b,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:D.signal,onProgress:se=>{se.phase==="rescoring"&&bt(M,"progress",{phase:"rescoring",current:se.current,total:se.total,verdict:se.verdict})}});W=$.proposals,M.rescore={enabled:!0,considered:$.considered,promoted:$.promoted,demoted:$.demoted,unsure:$.unsure,failed:$.failed,capped:$.capped}}else W=S.filter($=>$.confidence>=t);if(W.length===0){M.status=D.signal.aborted?"cancelled":"done",M.endedAt=new Date().toISOString(),bt(M,"done",{...fn(M),threads:[]});return}let oe=await Xu({project:e.project,rows:h,edges:W,scannables:b,llmNames:s,model:n,signal:D.signal,onProgress:$=>{$.phase==="naming"&&(M.threadsNamed=$.current,M.currentThreadName=$.thread_name??null,bt(M,"progress",{phase:"naming",current:$.current,total:$.total,thread_name:$.thread_name}))}});M.threadsCreated=oe.threads.length,M.edgesWritten=W.length+oe.threads.length,M.status=D.signal.aborted?"cancelled":"done",M.endedAt=new Date().toISOString(),bt(M,"done",{...fn(M),threads:oe.threads})}catch(W){M.status="failed",M.endedAt=new Date().toISOString(),M.error=W instanceof Error?W.message:String(W??"unknown error"),bt(M,"done",fn(M))}finally{wS(M)}})(),{jobId:R,reused:!1}}async function*Vu(e,t=0){let s=St.get(e);if(!s)return;let n=t;for(;;){for(;n<s.events.length;){let r=s.events[n];if(!r)break;if(n+=1,yield r,r.kind==="done")return}if(s.status!=="running")return;await new Promise(r=>s.waiters.add(r))}}function Zu(e){let t=St.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function yo(e){let t=St.get(e);return t?fn(t):null}bs();import{randomUUID as kS}from"node:crypto";var hn=new Map;function Qu(e){let t={id:kS(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return hn.set(t.id,t),t}function En(e){return hn.get(e)}function Tt(e,t){for(let s of e.listeners)s(t)}function ed(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function td(e){let t=hn.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),Tt(t,{type:"status",status:"cancelled"}),!0):!1}function sd(e){return hn.delete(e)}Qe();function bn(e){let{session:t,knownTags:s,minTags:n,maxTags:r}=e,o=s.slice(0,50).map(c=>c.tag).join(", "),a=t.current_tags.length>0?`already applied, do not repeat: [${t.current_tags.join(", ")}]`:"currently has no tags";return[`You are tagging a software engineering session. Produce ${n}-${r} concise, lowercase, hyphen-separated tags that describe:`," - the domain or subsystem touched (auth, db, frontend, ...)"," - the kind of work (bugfix, feature, refactor, research, ...)"," - any specific tool, library, or product if prominent","","Prefer tags from the known-tags list below when applicable \u2014 consistency over creativity. Never invent marketing-sounding labels. Never tag based on speculation; only on observed work.","",`known tags (most used first, up to 50): ${o||"(none yet)"}`,"","Session:",` project: ${t.project}`,t.alias?` alias: ${JSON.stringify(t.alias)}`:" alias: (none)",t.git_branch?` git_branch: ${t.git_branch}`:"",` ${a}`," first_user_message:",nd(t.first_user_message," ")," message_sample:",nd(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${n}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1510
+ `)}function nd(e,t){return e.split(`
1346
1511
  `).map(s=>t+s).join(`
1347
- `)}Ge();import{z as Kt}from"zod";var lE=Kt.object({tags:Kt.array(Kt.string()).min(1),confidence:Kt.number().min(0).max(1),rationale:Kt.string().min(1).max(500)});function dE(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function zs(e,t={}){let s=t.maxTags??10,n;try{n=JSON.parse(dE(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=lE.safeParse(n);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>De(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,s);return a.length===0?{ok:!1,reason:"no usable tags after normalization"}:{ok:!0,data:{tags:a,confidence:r.data.confidence,rationale:r.data.rationale}}}import uE from"@anthropic-ai/sdk";async function Vs(e){let n=(await new uE({apiKey:e.apiKey}).messages.create({model:e.model,max_tokens:512,temperature:.2,messages:[{role:"user",content:e.prompt}]},e.signal?{signal:e.signal}:void 0)).content.find(r=>r.type==="text");if(!n||n.type!=="text")throw new Error("Anthropic response contained no text block");return n.text}function pE(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 Zs(e,t={}){let s=t.maxAttempts??3,n=t.baseDelayMs??500,r;for(let o=0;o<s;o++)try{return await e()}catch(a){if(r=a,!pE(a)||o===s-1)throw a;await new Promise(c=>setTimeout(c,n*Math.pow(2,o)))}throw r}async function Ql(e,t){e.status="running",_t(e,{type:"status",status:"running"});let s=Xe();for(let n of t.sessions){if(e.controller.signal.aborted)break;let r=Ks({session:n,knownTags:s,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:n.id,project:n.project,alias:n.alias,first_user_message:n.first_user_message,current_tags:n.current_tags,suggestion:null,error:null,applied:!1};try{let a=await Zs(()=>Vs({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=zs(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,_t(e,{type:"result",result:o}),_t(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let n=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;_t(e,{type:"done",summary:{ok:n,failed:r}})}}function ed(e,t){let s=0,n=0;for(let r of t){let o=e.results.find(a=>a.sessionId===r.sessionId);if(o){for(let a of r.tags)try{let{added:c}=Je(r.sessionId,a);c&&(s+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),n+=1}o.applied=!0}}return{applied:s,failed:n}}Ge();rs();var Y={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},Hr=new Set;function Vt(){return{status:Y.status,processed:Y.processed,total:Y.total,currentSessionId:Y.currentSessionId,lastError:Y.lastError,lastRunAt:Y.lastRunAt}}function td(e){return Hr.add(e),()=>{Hr.delete(e)}}function zt(){let e=Vt();for(let t of Hr)t(e)}async function Qs(){let e=Re();if(!(!e.enabled||!e.autopilot)&&!(e.backend!=="api"||!e.apiKey)&&!Y.running){Y.running=!0,Y.status="scanning",Y.processed=0,Y.total=0,Y.currentSessionId=null,Y.lastError=null,Y.controller=new AbortController,zt();try{let t=Ye({untaggedOnly:!0,limit:200});if(Y.total=t.length,zt(),t.length===0){Y.status="idle",Y.lastRunAt=new Date().toISOString();return}let s=Xe();for(let n of t){if(Y.controller.signal.aborted)break;let r=Re();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;Y.currentSessionId=n.id,zt();let o=Ks({session:n,knownTags:s,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await Zs(()=>Vs({apiKey:r.apiKey,model:r.model,prompt:o,signal:Y.controller.signal})),c=zs(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let d of c.data.tags)try{Je(n.id,d)}catch(u){console.error("[autopilot] addTag failed:",n.id,d,u)}}catch(a){console.error("[autopilot] scan failed for",n.id,a)}Y.processed+=1,zt(),await new Promise(a=>setTimeout(a,1500))}Y.status="idle",Y.lastRunAt=new Date().toISOString()}catch(t){Y.status="error",Y.lastError=t instanceof Error?t.message:String(t),console.error("[autopilot] fatal:",t)}finally{Y.running=!1,Y.currentSessionId=null,Y.controller=null,zt()}}}import{execFileSync as mE}from"node:child_process";import{existsSync as sd,readFileSync as gE,writeFileSync as nd}from"node:fs";import{homedir as _E}from"node:os";import{dirname as fE,join as hE,resolve as EE}from"node:path";import{fileURLToPath as bE}from"node:url";var ft=hE(_E(),".claude.json"),SE=fE(bE(import.meta.url)),TE=EE(SE,"..","mcp","server.js");function qr(){if(!sd(ft))return{};try{let e=gE(ft,"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 Wr=new Map;function yE(e){let t=Wr.get(e);if(t!==void 0)return t;try{return mE("command",["-v",e],{stdio:"ignore"}),Wr.set(e,!0),!0}catch{return Wr.set(e,!1),!1}}function wE(){return yE("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[TE]}}function Ie(){let t=qr().mcpServers?.recall;return{configPath:ft,configExists:sd(ft),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function rd(){let e=qr(),t=e.mcpServers??{},s=wE(),n=t.recall;if(n?.command===s.command&&JSON.stringify(n?.args??[])===JSON.stringify(s.args))return Ie();let o={command:s.command};s.args.length>0&&(o.args=s.args);let a={...e,mcpServers:{...t,recall:o}};return nd(ft,JSON.stringify(a,null,2)),Ie()}function od(){let e=qr(),t=e.mcpServers??{};if(!t.recall)return Ie();let{recall:s,...n}=t,r={...e,mcpServers:n};return nd(ft,JSON.stringify(r,null,2)),Ie()}import{existsSync as id,mkdirSync as RE,readFileSync as kE,writeFileSync as ad}from"node:fs";import{homedir as AE}from"node:os";import{join as cd}from"node:path";import{z as ve}from"zod";function ld(){return process.env.RECALL_HOME??cd(AE(),".recall")}function dd(){let e=ld();id(e)||RE(e,{recursive:!0})}function Xr(){return cd(ld(),"onboarding.json")}var en=ve.object({version:ve.literal(1).default(1),completed:ve.boolean().default(!1),skipped:ve.boolean().default(!1),finishedAt:ve.string().nullable().default(null),completedSteps:ve.array(ve.string().max(100)).max(50).default([]),threadsIntroSeen:ve.boolean().default(!1)}),Jr={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function tn(){let e=Xr();if(!id(e))return{...Jr};try{let t=JSON.parse(kE(e,"utf8")),s=en.safeParse(t);return s.success?s.data:{...Jr}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...Jr}}}function ud(e){dd();let t=tn(),s=en.parse({...t,...e,completedSteps:xE([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return ad(Xr(),JSON.stringify(s,null,2)),s}function pd(){dd();let e=tn(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return ad(Xr(),JSON.stringify(t,null,2)),t}function xE(e){let t=new Set,s=[];for(let n of e)t.has(n)||(t.add(n),s.push(n));return s}ge();In();Cn();import{existsSync as md,mkdirSync as NE,readFileSync as OE,writeFileSync as LE}from"node:fs";import{homedir as CE}from"node:os";import{join as gd}from"node:path";import{z as xe}from"zod";function _d(){return process.env.RECALL_HOME??gd(CE(),".recall")}function IE(){let e=_d();md(e)||NE(e,{recursive:!0})}function fd(){return gd(_d(),"config.json")}var nn=xe.object({enabled:xe.boolean().default(!1),model:xe.string().optional(),ratePerMinute:xe.number().int().min(1).max(600).default(30),lastProcessedSessionId:xe.string().nullable().default(null),backfillPaused:xe.boolean().default(!1),autoExtractEnabled:xe.boolean().default(!1),autoExtractIntervalMinutes:xe.number().int().min(5).max(720).default(60),autoExtractBatchSize:xe.number().int().min(1).max(20).default(1)}),sn={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1};function hd(){let e=fd();if(!md(e))return{};try{return JSON.parse(OE(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ce(){let e=hd().semantic;if(!e)return{...sn};let t=nn.safeParse({...sn,...e});return t.success?t.data:{...sn}}function rn(e){IE();let t=hd(),s=nn.parse({...sn,...t.semantic??{},...e}),n={...t,semantic:s};return LE(fd(),JSON.stringify(n,null,2)),s}H();ge();import{existsSync as vE,mkdirSync as jE,writeFileSync as ME}from"node:fs";import{homedir as DE}from"node:os";import{join as Gr}from"node:path";var FE=1,PE=12e3,Ed=3,UE=[];function $E(){return process.env.RECALL_HOME??Gr(DE(),".recall")}function bd(){return Gr($E(),"semantic")}function BE(){let e=bd();vE(e)||jE(e,{recursive:!0})}function HE(e){let t=f(),s=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
1348
- p.name AS project,
1349
- NULLIF(sa.alias, '') AS alias
1350
- FROM sessions s
1351
- JOIN projects p ON p.id = s.project_id
1352
- LEFT JOIN session_aliases sa ON sa.session_id = s.id
1353
- WHERE s.id = ?`).get(e);if(!s)return null;let n=t.prepare(`SELECT role, content_text
1354
- FROM messages
1355
- WHERE session_id = ? AND is_sidechain = 0
1356
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of n){if(!a.content_text)continue;let c=a.role??"system",d=a.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!d)continue;let u=d.length>1500?d.slice(0,1500)+"\u2026":d,g=`${c}: ${u}`;if(o+g.length>PE)break;r.push(g),o+=g.length}return{id:s.id,alias:s.alias,project:s.project,firstUserMessage:s.first_user_message,excerpt:r.join(`
1512
+ `)}Qe();import{z as rs}from"zod";var xS=rs.object({tags:rs.array(rs.string()).min(1),confidence:rs.number().min(0).max(1),rationale:rs.string().min(1).max(500)});function NS(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function Sn(e,t={}){let s=t.maxTags??10,n;try{n=JSON.parse(NS(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=xS.safeParse(n);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>Be(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,s);return a.length===0?{ok:!1,reason:"no usable tags after normalization"}:{ok:!0,data:{tags:a,confidence:r.data.confidence,rationale:r.data.rationale}}}import OS from"@anthropic-ai/sdk";async function Tn(e){let n=(await new OS({apiKey:e.apiKey}).messages.create({model:e.model,max_tokens:512,temperature:.2,messages:[{role:"user",content:e.prompt}]},e.signal?{signal:e.signal}:void 0)).content.find(r=>r.type==="text");if(!n||n.type!=="text")throw new Error("Anthropic response contained no text block");return n.text}function LS(e){if(!(e instanceof Error))return!1;let t=e;return t.status===429||t.status===502||t.status===503||t.status===504}async function yn(e,t={}){let s=t.maxAttempts??3,n=t.baseDelayMs??500,r;for(let o=0;o<s;o++)try{return await e()}catch(a){if(r=a,!LS(a)||o===s-1)throw a;await new Promise(c=>setTimeout(c,n*Math.pow(2,o)))}throw r}async function rd(e,t){e.status="running",Tt(e,{type:"status",status:"running"});let s=Ze();for(let n of t.sessions){if(e.controller.signal.aborted)break;let r=bn({session:n,knownTags:s,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:n.id,project:n.project,alias:n.alias,first_user_message:n.first_user_message,current_tags:n.current_tags,suggestion:null,error:null,applied:!1};try{let a=await yn(()=>Tn({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=Sn(a,{maxTags:t.maxTags});c.ok?o.suggestion=c.data:o.error=c.reason}catch(a){o.error=a instanceof Error?a.message:String(a)}e.results.push(o),e.completed+=1,Tt(e,{type:"result",result:o}),Tt(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let n=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;Tt(e,{type:"done",summary:{ok:n,failed:r}})}}function od(e,t){let s=0,n=0;for(let r of t){let o=e.results.find(a=>a.sessionId===r.sessionId);if(o){for(let a of r.tags)try{let{added:c}=Ve(r.sessionId,a);c&&(s+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),n+=1}o.applied=!0}}return{applied:s,failed:n}}Qe();bs();var z={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},wo=new Set;function is(){return{status:z.status,processed:z.processed,total:z.total,currentSessionId:z.currentSessionId,lastError:z.lastError,lastRunAt:z.lastRunAt}}function id(e){return wo.add(e),()=>{wo.delete(e)}}function os(){let e=is();for(let t of wo)t(e)}async function wn(){let e=Le();if(!(!e.enabled||!e.autopilot)&&!(e.backend!=="api"||!e.apiKey)&&!z.running){z.running=!0,z.status="scanning",z.processed=0,z.total=0,z.currentSessionId=null,z.lastError=null,z.controller=new AbortController,os();try{let t=et({untaggedOnly:!0,limit:200});if(z.total=t.length,os(),t.length===0){z.status="idle",z.lastRunAt=new Date().toISOString();return}let s=Ze();for(let n of t){if(z.controller.signal.aborted)break;let r=Le();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;z.currentSessionId=n.id,os();let o=bn({session:n,knownTags:s,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await yn(()=>Tn({apiKey:r.apiKey,model:r.model,prompt:o,signal:z.controller.signal})),c=Sn(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{Ve(n.id,u)}catch(d){console.error("[autopilot] addTag failed:",n.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",n.id,a)}z.processed+=1,os(),await new Promise(a=>setTimeout(a,1500))}z.status="idle",z.lastRunAt=new Date().toISOString()}catch(t){z.status="error",z.lastError=t instanceof Error?t.message:String(t),console.error("[autopilot] fatal:",t)}finally{z.running=!1,z.currentSessionId=null,z.controller=null,os()}}}import{execFileSync as CS}from"node:child_process";import{existsSync as ad,readFileSync as vS,writeFileSync as cd}from"node:fs";import{homedir as IS}from"node:os";import{dirname as jS,join as MS,resolve as DS}from"node:path";import{fileURLToPath as FS}from"node:url";var yt=MS(IS(),".claude.json"),PS=jS(FS(import.meta.url)),US=DS(PS,"..","mcp","server.js");function Ao(){if(!ad(yt))return{};try{let e=vS(yt,"utf8"),t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch(e){return console.error("[mcp-installer] failed to parse ~/.claude.json:",e),{}}}var Ro=new Map;function $S(e){let t=Ro.get(e);if(t!==void 0)return t;try{return CS("command",["-v",e],{stdio:"ignore"}),Ro.set(e,!0),!0}catch{return Ro.set(e,!1),!1}}function BS(){return $S("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[US]}}function De(){let t=Ao().mcpServers?.recall;return{configPath:yt,configExists:ad(yt),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function ld(){let e=Ao(),t=e.mcpServers??{},s=BS(),n=t.recall;if(n?.command===s.command&&JSON.stringify(n?.args??[])===JSON.stringify(s.args))return De();let o={command:s.command};s.args.length>0&&(o.args=s.args);let a={...e,mcpServers:{...t,recall:o}};return cd(yt,JSON.stringify(a,null,2)),De()}function ud(){let e=Ao(),t=e.mcpServers??{};if(!t.recall)return De();let{recall:s,...n}=t,r={...e,mcpServers:n};return cd(yt,JSON.stringify(r,null,2)),De()}import{existsSync as dd,mkdirSync as HS,readFileSync as WS,writeFileSync as pd}from"node:fs";import{homedir as qS}from"node:os";import{join as md}from"node:path";import{z as Fe}from"zod";function gd(){return process.env.RECALL_HOME??md(qS(),".recall")}function _d(){let e=gd();dd(e)||HS(e,{recursive:!0})}function xo(){return md(gd(),"onboarding.json")}var Rn=Fe.object({version:Fe.literal(1).default(1),completed:Fe.boolean().default(!1),skipped:Fe.boolean().default(!1),finishedAt:Fe.string().nullable().default(null),completedSteps:Fe.array(Fe.string().max(100)).max(50).default([]),threadsIntroSeen:Fe.boolean().default(!1)}),ko={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function An(){let e=xo();if(!dd(e))return{...ko};try{let t=JSON.parse(WS(e,"utf8")),s=Rn.safeParse(t);return s.success?s.data:{...ko}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...ko}}}function fd(e){_d();let t=An(),s=Rn.parse({...t,...e,completedSteps:XS([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return pd(xo(),JSON.stringify(s,null,2)),s}function hd(){_d();let e=An(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return pd(xo(),JSON.stringify(t,null,2)),t}function XS(e){let t=new Set,s=[];for(let n of e)t.has(n)||(t.add(n),s.push(n));return s}he();nr();sr();H();H();import{createReadStream as JS}from"node:fs";import{createInterface as YS}from"node:readline";function kn(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Lo(e){let t=e?.usage;if(!t||typeof t!="object")return null;let s={inputTokens:kn(t.input_tokens),outputTokens:kn(t.output_tokens),cacheCreateTokens:kn(t.cache_creation_input_tokens),cacheReadTokens:kn(t.cache_read_input_tokens)};return s.inputTokens===0&&s.outputTokens===0&&s.cacheCreateTokens===0&&s.cacheReadTokens===0?null:s}var GS=/\x1B\[[0-9;]*[a-zA-Z]/g;function No(e){return e.replace(GS,"")}var Oo=12e3;function Ed(e,t){if(e.length<=Oo)return e;let s=e.slice(0,Oo),n=e.length-Oo;return`${s}
1357
1513
 
1358
- `),messageCount:s.message_count}}function WE(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(`
1359
- `)}function qE(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+1);try{let o=JSON.parse(r),a=typeof o.summary=="string"?o.summary.trim():"",d=(Array.isArray(o.keywords)?o.keywords:[]).filter(u=>typeof u=="string").map(u=>u.trim().toLowerCase()).filter(u=>u.length>0&&u.length<64);return!a||d.length===0?null:{summary:a,keywords:Array.from(new Set(d)).slice(0,20)}}catch{return null}}function JE(e){let t=f(),s=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
1360
- (session_id, summary, keywords, model, source_message_count, generated_at)
1361
- VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
1362
- ON CONFLICT(session_id) DO UPDATE SET
1363
- summary = excluded.summary,
1364
- keywords = excluded.keywords,
1365
- model = excluded.model,
1366
- source_message_count = excluded.source_message_count,
1367
- generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:s,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),BE();let n=Gr(bd(),`${e.sessionId}.json`);ME(n,JSON.stringify({version:FE,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 on=null;function XE(){let t=ce().ratePerMinute,s=t/6e4;return(!on||on.capacity!==t)&&(on={tokens:t,capacity:t,refillPerMs:s,lastRefill:Date.now()}),on}function GE(e){let t=Date.now(),s=t-e.lastRefill;s>0&&(e.tokens=Math.min(e.capacity,e.tokens+s*e.refillPerMs),e.lastRefill=t)}async function YE(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=XE();if(GE(t),t.tokens>=1){t.tokens-=1;return}let s=1-t.tokens,n=Math.max(50,Math.ceil(s/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(n,5e3)))}}async function an(e,t={}){let s=ce();if(!s.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!oe())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let n=HE(e);if(!n)return{sessionId:e,ok:!1,reason:"session-not-found"};if(n.messageCount<Ed)return{sessionId:e,ok:!1,reason:"too-short"};if(!n.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await YE(t.signal);let r=WE(n),o=await Fe(r,UE,{model:s.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:s.model??null};let a=qE(o.stdout);return a?(JE({sessionId:n.id,summary:a.summary,keywords:a.keywords,model:s.model??null,sourceMessageCount:n.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:s.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:s.model??null}}async function cn(e={}){let t=ce();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let s=f(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let a=s.prepare(`SELECT s.id
1368
- FROM sessions s
1369
- LEFT JOIN session_semantic ss ON ss.session_id = s.id
1370
- WHERE ${o}
1371
- ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
1372
- LIMIT @limit`).all(r),c={total:a.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(c);for(let{id:d}of a){if(e.signal?.aborted||ce().backfillPaused)break;c.currentSessionId=d,e.onProgress?.({...c});try{(await an(d,{signal:e.signal})).ok?c.ok+=1:c.failed+=1}catch(g){c.failed+=1,console.error("[semantic.backfill] failed for",d,g)}c.processed+=1,rn({lastProcessedSessionId:d}),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function Sd(e){if(!ce().enabled)return;let n=f().prepare(`SELECT s.message_count, s.ended_at,
1373
- ss.generated_at, ss.source_message_count
1374
- FROM sessions s
1375
- LEFT JOIN session_semantic ss ON ss.session_id = s.id
1376
- WHERE s.id = ?`).get(e);if(n&&!(n.message_count<Ed)&&!(n.generated_at&&n.source_message_count!=null&&n.source_message_count>=n.message_count))try{await an(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function Yr(){let e=ce(),t=f(),s=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,n=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:oe(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:s,processedSessions:n,pendingSessions:Math.max(0,s-n),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}H();H();import{createReadStream as KE}from"node:fs";import{createInterface as zE}from"node:readline";function ln(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Vr(e){let t=e?.usage;if(!t||typeof t!="object")return null;let s={inputTokens:ln(t.input_tokens),outputTokens:ln(t.output_tokens),cacheCreateTokens:ln(t.cache_creation_input_tokens),cacheReadTokens:ln(t.cache_read_input_tokens)};return s.inputTokens===0&&s.outputTokens===0&&s.cacheCreateTokens===0&&s.cacheReadTokens===0?null:s}var VE=/\x1B\[[0-9;]*[a-zA-Z]/g;function Kr(e){return e.replace(VE,"")}var zr=12e3;function Td(e,t){if(e.length<=zr)return e;let s=e.slice(0,zr),n=e.length-zr;return`${s}
1377
-
1378
- \u27E8\u2026 ${n.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function ZE(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function QE(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let s of e.content)if(s&&typeof s=="object"){let n=s;n.type==="text"&&typeof n.text=="string"?t.push(n.text):n.type==="image"&&t.push("[image]")}return t.join(`
1379
- `)}return""}function eb(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Kr(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],s=[];for(let n of e.content)if(!(!n||typeof n!="object")){if(n.type==="text"&&typeof n.text=="string"){t.push(Kr(n.text));continue}if(n.type==="tool_use"&&typeof n.name=="string"){s.push(n.name);let r=n.input!=null?ZE(n.input):"",o=Td(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${n.name}\`**
1514
+ \u27E8\u2026 ${n.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function zS(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function KS(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let s of e.content)if(s&&typeof s=="object"){let n=s;n.type==="text"&&typeof n.text=="string"?t.push(n.text):n.type==="image"&&t.push("[image]")}return t.join(`
1515
+ `)}return""}function VS(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:No(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],s=[];for(let n of e.content)if(!(!n||typeof n!="object")){if(n.type==="text"&&typeof n.text=="string"){t.push(No(n.text));continue}if(n.type==="tool_use"&&typeof n.name=="string"){s.push(n.name);let r=n.input!=null?zS(n.input):"",o=Ed(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${n.name}\`**
1380
1516
 
1381
1517
  \`\`\`json
1382
1518
  ${o}
1383
- \`\`\``);continue}if(n.type==="tool_result"){let r=Kr(QE(n));if(r){let o=Td(r,"tool result");t.push(`**Tool result**
1519
+ \`\`\``);continue}if(n.type==="tool_result"){let r=No(KS(n));if(r){let o=Ed(r,"tool result");t.push(`**Tool result**
1384
1520
 
1385
1521
  \`\`\`
1386
1522
  ${o}
1387
1523
  \`\`\``)}else t.push("_(tool result was empty or image-only)_");continue}if(n.type==="image"){t.push("_(image)_");continue}t.push(`_(unknown block: ${n.type})_`)}return{text:t.join(`
1388
1524
 
1389
- `),toolNames:s}}async function*yd(e){let t=KE(e,{encoding:"utf8"}),s=zE({input:t,crlfDelay:1/0});for await(let n of s){if(!n.trim())continue;let r;try{r=JSON.parse(n)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:a}=eb(r.message);yield{uuid:r.uuid,parentUuid:r.parentUuid??null,sessionId:r.sessionId,type:r.type??"unknown",role:r.message?.role??null,timestamp:r.timestamp??null,isSidechain:r.isSidechain===!0,cwd:r.cwd??null,gitBranch:r.gitBranch??null,version:r.version??null,contentText:o,toolNames:a,raw:n,usage:Vr(r.message),model:r.message?.model??null}}}function wd(e,t,s){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let n=e.prepare(`
1525
+ `),toolNames:s}}async function*bd(e){let t=JS(e,{encoding:"utf8"}),s=YS({input:t,crlfDelay:1/0});for await(let n of s){if(!n.trim())continue;let r;try{r=JSON.parse(n)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:a}=VS(r.message);yield{uuid:r.uuid,parentUuid:r.parentUuid??null,sessionId:r.sessionId,type:r.type??"unknown",role:r.message?.role??null,timestamp:r.timestamp??null,isSidechain:r.isSidechain===!0,cwd:r.cwd??null,gitBranch:r.gitBranch??null,version:r.version??null,contentText:o,toolNames:a,raw:n,usage:Lo(r.message),model:r.message?.model??null}}}function Sd(e,t,s){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let n=e.prepare(`
1390
1526
  INSERT INTO message_usage (
1391
1527
  message_uuid, session_id, model,
1392
1528
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1403,7 +1539,7 @@ ${o}
1403
1539
  cache_create_tokens = excluded.cache_create_tokens,
1404
1540
  cache_read_tokens = excluded.cache_read_tokens,
1405
1541
  timestamp = excluded.timestamp
1406
- `);for(let r of s)r.usage&&r.role==="assistant"&&n.run({uuid:r.uuid,session_id:t,model:r.model,input:r.usage.inputTokens,output:r.usage.outputTokens,cc:r.usage.cacheCreateTokens,cr:r.usage.cacheReadTokens,ts:r.timestamp})}function dn(e,t){let s=e.prepare(`SELECT
1542
+ `);for(let r of s)r.usage&&r.role==="assistant"&&n.run({uuid:r.uuid,session_id:t,model:r.model,input:r.usage.inputTokens,output:r.usage.outputTokens,cc:r.usage.cacheCreateTokens,cr:r.usage.cacheReadTokens,ts:r.timestamp})}function xn(e,t){let s=e.prepare(`SELECT
1407
1543
  COALESCE(SUM(input_tokens), 0) AS input_tokens,
1408
1544
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
1409
1545
  COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
@@ -1418,14 +1554,14 @@ ${o}
1418
1554
  total_cache_create_tokens = @cc,
1419
1555
  total_cache_read_tokens = @cr,
1420
1556
  primary_model = @model
1421
- WHERE id = @id`).run({id:t,input:s.input_tokens,output:s.output_tokens,cc:s.cache_create_tokens,cr:s.cache_read_tokens,model:n?.model??null})}var tb=500,un=new Set,Rd=null,pn=!1;function kd(){return un.size}function sb(){return`
1557
+ WHERE id = @id`).run({id:t,input:s.input_tokens,output:s.output_tokens,cc:s.cache_create_tokens,cr:s.cache_read_tokens,model:n?.model??null})}var ZS=500,Nn=new Set,Td=null,On=!1;function yd(){return Nn.size}function QS(){return`
1422
1558
  SELECT m.uuid, m.session_id, m.timestamp, m.raw_json
1423
1559
  FROM messages m
1424
1560
  LEFT JOIN message_usage mu ON mu.message_uuid = m.uuid
1425
1561
  WHERE m.role = 'assistant' AND mu.message_uuid IS NULL
1426
1562
  AND m.uuid NOT IN (SELECT value FROM json_each(?))
1427
1563
  LIMIT ?
1428
- `}function Ad(e,t){let s=t.limit??Number.MAX_SAFE_INTEGER,n=Math.max(1,t.chunkSize??tb),r=e.prepare(sb()),o=e.prepare(`
1564
+ `}function wd(e,t){let s=t.limit??Number.MAX_SAFE_INTEGER,n=Math.max(1,t.chunkSize??ZS),r=e.prepare(QS()),o=e.prepare(`
1429
1565
  INSERT INTO message_usage (
1430
1566
  message_uuid, session_id, model,
1431
1567
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1435,7 +1571,7 @@ ${o}
1435
1571
  @input, @output, @cc, @cr, @ts
1436
1572
  )
1437
1573
  ON CONFLICT(message_uuid) DO NOTHING
1438
- `),a=0,c=0,d=new Set;for(;a<s;){let u=Math.min(n,s-a),g=JSON.stringify([...un]),h=r.all(g,u);if(h.length===0)break;let b=new Set;if(e.transaction(()=>{for(let y of h){let k;try{k=JSON.parse(y.raw_json)}catch{un.add(y.uuid);continue}let w=Vr(k.message);if(!w){un.add(y.uuid);continue}o.run({uuid:y.uuid,session_id:y.session_id,model:k.message?.model??null,input:w.inputTokens,output:w.outputTokens,cc:w.cacheCreateTokens,cr:w.cacheReadTokens,ts:y.timestamp}),c+=1,b.add(y.session_id)}for(let y of b)dn(e,y),d.add(y)})(),a+=h.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:d.size,done:h.length<u}),h.length<u)break}return{scanned:a,inserted:c,sessionsTouched:d.size,done:!0}}function xd(e={}){return Ad(f(),e)}function Nd(e={}){return pn?!1:(pn=!0,queueMicrotask(()=>{try{let t=Ad(f(),e);Rd={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{pn=!1}}),!0)}function Od(){return pn}function Zr(){return Rd}var nb=[[/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}]],Ld={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function je(e){if(!e)return Ld;for(let[t,s]of nb)if(t.test(e))return s;return Ld}function he(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[u,g]of Object.entries(e.byModel)){let h=je(u);a.input+=g.inputTokens/1e6*h.inputCentsPerMtok,a.output+=g.outputTokens/1e6*h.outputCentsPerMtok,a.cacheCreate+=g.cacheCreateTokens/1e6*h.cacheCreateCentsPerMtok,a.cacheRead+=g.cacheReadTokens/1e6*h.cacheReadCentsPerMtok,c+=g.inputTokens+g.outputTokens+g.cacheCreateTokens+g.cacheReadTokens}let d=a.input+a.output+a.cacheCreate+a.cacheRead;return{cents:d,dollars:d/100,totalTokens:c,parts:a}}let s=je(t),n={input:e.inputTokens/1e6*s.inputCentsPerMtok,output:e.outputTokens/1e6*s.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*s.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*s.cacheReadCentsPerMtok},r=n.input+n.output+n.cacheCreate+n.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:n}}function ht(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 Et(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 Qr(e){let t=new Map;for(let n of e){let r=n.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=n.input_tokens,o.outputTokens+=n.output_tokens,o.cacheCreateTokens+=n.cache_create_tokens,o.cacheReadTokens+=n.cache_read_tokens,o.messageCount+=n.n,t.set(r,o)}let s=[];for(let[n,r]of t.entries()){let o=he({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},n);s.push({model:n,modelLabel:je(n).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return s.sort((n,r)=>r.cost.cents-n.cost.cents)}function eo(e){let t={};for(let s of e)t[s.model??"__unknown__"]={inputTokens:s.inputTokens,outputTokens:s.outputTokens,cacheCreateTokens:s.cacheCreateTokens,cacheReadTokens:s.cacheReadTokens};return{byModel:t}}function Cd(e){let t=f(),s=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1574
+ `),a=0,c=0,u=new Set;for(;a<s;){let d=Math.min(n,s-a),m=JSON.stringify([...Nn]),h=r.all(m,d);if(h.length===0)break;let b=new Set;if(e.transaction(()=>{for(let S of h){let w;try{w=JSON.parse(S.raw_json)}catch{Nn.add(S.uuid);continue}let R=Lo(w.message);if(!R){Nn.add(S.uuid);continue}o.run({uuid:S.uuid,session_id:S.session_id,model:w.message?.model??null,input:R.inputTokens,output:R.outputTokens,cc:R.cacheCreateTokens,cr:R.cacheReadTokens,ts:S.timestamp}),c+=1,b.add(S.session_id)}for(let S of b)xn(e,S),u.add(S)})(),a+=h.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:h.length<d}),h.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function Rd(e={}){return wd(f(),e)}function Ad(e={}){return On?!1:(On=!0,queueMicrotask(()=>{try{let t=wd(f(),e);Td={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{On=!1}}),!0)}function kd(){return On}function Co(){return Td}var eT=[[/opus[-_ ]?4[-_. ]?7/i,{label:"Opus 4.7",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/opus[-_ ]?4[-_. ]?6/i,{label:"Opus 4.6",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4[-_. ]?6/i,{label:"Sonnet 4.6",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/sonnet[-_ ]?4[-_. ]?5/i,{label:"Sonnet 4.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku[-_ ]?4[-_. ]?5/i,{label:"Haiku 4.5",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}],[/opus[-_ ]?4(?!.*[5-9])/i,{label:"Opus 4",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4(?!.*[5-9])/i,{label:"Sonnet 4",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?7[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?7/i,{label:"Sonnet 3.7",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?5/i,{label:"Sonnet 3.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?haiku|haiku[-_ ]?3[-_. ]?5/i,{label:"Haiku 3.5",inputCentsPerMtok:80,outputCentsPerMtok:400,cacheCreateCentsPerMtok:100,cacheReadCentsPerMtok:8}],[/3[-_ ]?opus|opus[-_ ]?3/i,{label:"Opus 3",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/3[-_ ]?haiku|haiku(?!.*3[-_. ]?5)/i,{label:"Haiku 3",inputCentsPerMtok:25,outputCentsPerMtok:125,cacheCreateCentsPerMtok:30,cacheReadCentsPerMtok:3}],[/opus/i,{label:"Opus",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet/i,{label:"Sonnet",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku/i,{label:"Haiku",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}]],xd={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function Pe(e){if(!e)return xd;for(let[t,s]of eT)if(t.test(e))return s;return xd}function ye(e,t){if(e.byModel&&Object.keys(e.byModel).length>0){let a={input:0,output:0,cacheCreate:0,cacheRead:0},c=0;for(let[d,m]of Object.entries(e.byModel)){let h=Pe(d);a.input+=m.inputTokens/1e6*h.inputCentsPerMtok,a.output+=m.outputTokens/1e6*h.outputCentsPerMtok,a.cacheCreate+=m.cacheCreateTokens/1e6*h.cacheCreateCentsPerMtok,a.cacheRead+=m.cacheReadTokens/1e6*h.cacheReadCentsPerMtok,c+=m.inputTokens+m.outputTokens+m.cacheCreateTokens+m.cacheReadTokens}let u=a.input+a.output+a.cacheCreate+a.cacheRead;return{cents:u,dollars:u/100,totalTokens:c,parts:a}}let s=Pe(t),n={input:e.inputTokens/1e6*s.inputCentsPerMtok,output:e.outputTokens/1e6*s.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*s.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*s.cacheReadCentsPerMtok},r=n.input+n.output+n.cacheCreate+n.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:n}}function wt(e){let t=e/100;return t===0?"$0.00":t<.01?"<$0.01":t<1?`$${t.toFixed(2)}`:t<100?`$${t.toFixed(2)}`:t<1e4?`$${t.toFixed(0)}`:`$${(t/1e3).toFixed(1)}k`}function Rt(e){return!Number.isFinite(e)||e<0?"0":e<1e3?String(Math.round(e)):e<1e6?`${(e/1e3).toFixed(1)}k`:e<1e9?`${(e/1e6).toFixed(2)}M`:e<1e12?`${(e/1e9).toFixed(2)}B`:`${(e/1e12).toFixed(2)}T`}function vo(e){let t=new Map;for(let n of e){let r=n.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=n.input_tokens,o.outputTokens+=n.output_tokens,o.cacheCreateTokens+=n.cache_create_tokens,o.cacheReadTokens+=n.cache_read_tokens,o.messageCount+=n.n,t.set(r,o)}let s=[];for(let[n,r]of t.entries()){let o=ye({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},n);s.push({model:n,modelLabel:Pe(n).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return s.sort((n,r)=>r.cost.cents-n.cost.cents)}function Io(e){let t={};for(let s of e)t[s.model??"__unknown__"]={inputTokens:s.inputTokens,outputTokens:s.outputTokens,cacheCreateTokens:s.cacheCreateTokens,cacheReadTokens:s.cacheReadTokens};return{byModel:t}}function Nd(e){let t=f(),s=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1439
1575
  s.message_count,
1440
1576
  s.total_input_tokens, s.total_output_tokens,
1441
1577
  s.total_cache_create_tokens, s.total_cache_read_tokens,
@@ -1450,7 +1586,7 @@ ${o}
1450
1586
  COUNT(*) AS n
1451
1587
  FROM message_usage
1452
1588
  WHERE session_id = ?
1453
- GROUP BY model`).all(e),r=Qr(n),o=s.total_input_tokens??0,a=s.total_output_tokens??0,c=s.total_cache_create_tokens??0,d=s.total_cache_read_tokens??0,u=he({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:d,...eo(r)},s.primary_model);return{sessionId:s.id,project:s.project,startedAt:s.started_at,endedAt:s.ended_at,messageCount:s.message_count,primaryModel:s.primary_model,primaryModelLabel:je(s.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:d,totalTokens:u.totalTokens,cost:u,byModel:r,display:{dollars:ht(u.cents),tokens:Et(u.totalTokens),model:je(s.primary_model).label}}}function Id(e){let t=f(),s=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT mu.model,
1589
+ GROUP BY model`).all(e),r=vo(n),o=s.total_input_tokens??0,a=s.total_output_tokens??0,c=s.total_cache_create_tokens??0,u=s.total_cache_read_tokens??0,d=ye({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...Io(r)},s.primary_model);return{sessionId:s.id,project:s.project,startedAt:s.started_at,endedAt:s.ended_at,messageCount:s.message_count,primaryModel:s.primary_model,primaryModelLabel:Pe(s.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:wt(d.cents),tokens:Rt(d.totalTokens),model:Pe(s.primary_model).label}}}function Od(e){let t=f(),s=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT mu.model,
1454
1590
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1455
1591
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1456
1592
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1459,12 +1595,12 @@ ${o}
1459
1595
  FROM message_usage mu
1460
1596
  JOIN sessions s ON s.id = mu.session_id
1461
1597
  WHERE s.project_id = ?
1462
- GROUP BY mu.model`).all(s.id),r=Qr(n),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1598
+ GROUP BY mu.model`).all(s.id),r=vo(n),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1463
1599
  COALESCE(SUM(total_output_tokens), 0) AS output_tokens,
1464
1600
  COALESCE(SUM(total_cache_create_tokens), 0) AS cache_create_tokens,
1465
1601
  COALESCE(SUM(total_cache_read_tokens), 0) AS cache_read_tokens,
1466
1602
  COUNT(*) AS session_count
1467
- FROM sessions WHERE project_id = ?`).get(s.id),a=he({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...eo(r)},null),d=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1603
+ FROM sessions WHERE project_id = ?`).get(s.id),a=ye({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...Io(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1468
1604
  s.total_input_tokens, s.total_output_tokens,
1469
1605
  s.total_cache_create_tokens, s.total_cache_read_tokens,
1470
1606
  s.primary_model
@@ -1475,7 +1611,7 @@ ${o}
1475
1611
  + COALESCE(s.total_output_tokens,0)
1476
1612
  + COALESCE(s.total_cache_create_tokens,0)
1477
1613
  + COALESCE(s.total_cache_read_tokens,0)) DESC
1478
- LIMIT 10`).all(s.id).map(u=>{let g=he({inputTokens:u.total_input_tokens??0,outputTokens:u.total_output_tokens??0,cacheCreateTokens:u.total_cache_create_tokens??0,cacheReadTokens:u.total_cache_read_tokens??0},u.primary_model);return{sessionId:u.id,alias:u.alias,startedAt:u.started_at,totalTokens:g.totalTokens,cost:g}});return{project:s.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:a.totalTokens,cost:a,byModel:r,topSessions:d,display:{dollars:ht(a.cents),tokens:Et(a.totalTokens)}}}function vd(e="all"){let t=f(),s=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,n=s?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=s?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=s?{since:s}:{},a=m=>s?t.prepare(m).get(o):t.prepare(m).get(),c=m=>s?t.prepare(m).all(o):t.prepare(m).all(),d=c(`SELECT mu.model,
1614
+ LIMIT 10`).all(s.id).map(d=>{let m=ye({inputTokens:d.total_input_tokens??0,outputTokens:d.total_output_tokens??0,cacheCreateTokens:d.total_cache_create_tokens??0,cacheReadTokens:d.total_cache_read_tokens??0},d.primary_model);return{sessionId:d.id,alias:d.alias,startedAt:d.started_at,totalTokens:m.totalTokens,cost:m}});return{project:s.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:a.totalTokens,cost:a,byModel:r,topSessions:u,display:{dollars:wt(a.cents),tokens:Rt(a.totalTokens)}}}function Ld(e="all"){let t=f(),s=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,n=s?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=s?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=s?{since:s}:{},a=l=>s?t.prepare(l).get(o):t.prepare(l).get(),c=l=>s?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1479
1615
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1480
1616
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1481
1617
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1484,7 +1620,7 @@ ${o}
1484
1620
  FROM message_usage mu
1485
1621
  JOIN sessions s ON s.id = mu.session_id
1486
1622
  ${n}
1487
- GROUP BY mu.model`),u=Qr(d),g=0,h=0,b=0,S=0;for(let m of u)g+=m.inputTokens,h+=m.outputTokens,b+=m.cacheCreateTokens,S+=m.cacheReadTokens;let y=he({inputTokens:g,outputTokens:h,cacheCreateTokens:b,cacheReadTokens:S,...eo(u)},null),k=s?a(`SELECT
1623
+ GROUP BY mu.model`),d=vo(u),m=0,h=0,b=0,T=0;for(let l of d)m+=l.inputTokens,h+=l.outputTokens,b+=l.cacheCreateTokens,T+=l.cacheReadTokens;let S=ye({inputTokens:m,outputTokens:h,cacheCreateTokens:b,cacheReadTokens:T,...Io(d)},null),w=s?a(`SELECT
1488
1624
  (SELECT COUNT(DISTINCT m.session_id)
1489
1625
  FROM messages m
1490
1626
  JOIN sessions s2 ON s2.id = m.session_id
@@ -1498,7 +1634,7 @@ ${o}
1498
1634
  WHERE (COALESCE(total_input_tokens,0)
1499
1635
  +COALESCE(total_output_tokens,0)
1500
1636
  +COALESCE(total_cache_create_tokens,0)
1501
- +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`).get(),w=c(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
1637
+ +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`).get(),R=c(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
1502
1638
  mu.model,
1503
1639
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1504
1640
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
@@ -1508,7 +1644,7 @@ ${o}
1508
1644
  JOIN sessions s ON s.id = mu.session_id
1509
1645
  ${n}
1510
1646
  GROUP BY day, mu.model
1511
- ORDER BY day ASC`),D=new Map;for(let m of w){if(!m.day)continue;let _=he({inputTokens:m.input_tokens,outputTokens:m.output_tokens,cacheCreateTokens:m.cache_create_tokens,cacheReadTokens:m.cache_read_tokens},m.model),E=D.get(m.day)??{tokens:0,cents:0};E.tokens+=_.totalTokens,E.cents+=_.cents,D.set(m.day,E)}let L=[...D.entries()].map(([m,_])=>({day:m,tokens:_.tokens,cents:_.cents})).sort((m,_)=>m.day.localeCompare(_.day)),M=c(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1647
+ ORDER BY day ASC`),D=new Map;for(let l of R){if(!l.day)continue;let p=ye({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.model),g=D.get(l.day)??{tokens:0,cents:0};g.tokens+=p.totalTokens,g.cents+=p.cents,D.set(l.day,g)}let O=[...D.entries()].map(([l,p])=>({day:l,tokens:p.tokens,cents:p.cents})).sort((l,p)=>l.day.localeCompare(p.day)),M=c(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1512
1648
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1513
1649
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1514
1650
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1524,7 +1660,7 @@ ${o}
1524
1660
  + COALESCE(SUM(mu.output_tokens),0)
1525
1661
  + COALESCE(SUM(mu.cache_create_tokens),0)
1526
1662
  + COALESCE(SUM(mu.cache_read_tokens),0)) DESC
1527
- LIMIT 10`).map(m=>{let _=he({inputTokens:m.input_tokens,outputTokens:m.output_tokens,cacheCreateTokens:m.cache_create_tokens,cacheReadTokens:m.cache_read_tokens},m.primary_model);return{sessionId:m.id,project:m.project,alias:m.alias,startedAt:m.started_at,totalTokens:_.totalTokens,cost:_}}),B=c(`SELECT p.id AS project_id,
1663
+ LIMIT 10`).map(l=>{let p=ye({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.primary_model);return{sessionId:l.id,project:l.project,alias:l.alias,startedAt:l.started_at,totalTokens:p.totalTokens,cost:p}}),W=c(`SELECT p.id AS project_id,
1528
1664
  p.name AS project,
1529
1665
  mu.model,
1530
1666
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
@@ -1536,24 +1672,24 @@ ${o}
1536
1672
  JOIN sessions s ON s.id = mu.session_id
1537
1673
  LEFT JOIN projects p ON p.id = s.project_id
1538
1674
  ${n}
1539
- GROUP BY p.id, mu.model`),se=new Map;for(let m of B){let _=m.project_id??"__none__",E=se.get(_);E||(E={project:m.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},se.set(_,E)),m.sessions>E.sessionsApprox&&(E.sessionsApprox=m.sessions),E.byModel[m.model??"__unknown__"]={inputTokens:m.input_tokens,outputTokens:m.output_tokens,cacheCreateTokens:m.cache_create_tokens,cacheReadTokens:m.cache_read_tokens}}let i=[...se.values()].map(m=>{let _=0,E=0,T=0,R=0;for(let x of Object.values(m.byModel))_+=x.inputTokens,E+=x.outputTokens,T+=x.cacheCreateTokens,R+=x.cacheReadTokens;let A=he({inputTokens:_,outputTokens:E,cacheCreateTokens:T,cacheReadTokens:R,byModel:m.byModel},null);return{project:m.project,sessions:m.sessionsApprox,totalTokens:A.totalTokens,cost:A}});i.sort((m,_)=>_.totalTokens-m.totalTokens);let l=i.slice(0,20),p=t.prepare(`SELECT
1675
+ GROUP BY p.id, mu.model`),oe=new Map;for(let l of W){let p=l.project_id??"__none__",g=oe.get(p);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},oe.set(p,g)),l.sessions>g.sessionsApprox&&(g.sessionsApprox=l.sessions),g.byModel[l.model??"__unknown__"]={inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens}}let $=[...oe.values()].map(l=>{let p=0,g=0,_=0,E=0;for(let A of Object.values(l.byModel))p+=A.inputTokens,g+=A.outputTokens,_+=A.cacheCreateTokens,E+=A.cacheReadTokens;let y=ye({inputTokens:p,outputTokens:g,cacheCreateTokens:_,cacheReadTokens:E,byModel:l.byModel},null);return{project:l.project,sessions:l.sessionsApprox,totalTokens:y.totalTokens,cost:y}});$.sort((l,p)=>p.totalTokens-l.totalTokens);let se=$.slice(0,20),i=t.prepare(`SELECT
1540
1676
  (SELECT COUNT(*) FROM messages WHERE role='assistant') AS assistant_messages,
1541
- (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:k.total_sessions,sessionsWithUsage:k.sessions_with_usage,inputTokens:g,outputTokens:h,cacheCreateTokens:b,cacheReadTokens:S,totalTokens:y.totalTokens,cost:y,daily:L,byModel:u,topSessions:M,topRepos:l,backfill:{assistantMessages:p.assistant_messages,messagesWithUsage:p.messages_with_usage,pending:Math.max(0,p.assistant_messages-p.messages_with_usage),unrecoverable:Math.min(kd(),Math.max(0,p.assistant_messages-p.messages_with_usage))},display:{dollars:ht(y.cents),tokens:Et(y.totalTokens)}}}H();function Zt(e){return Math.max(0,Math.min(1,e))}function to(e){let t=f(),s=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT COUNT(*) AS cnt,
1677
+ (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:w.total_sessions,sessionsWithUsage:w.sessions_with_usage,inputTokens:m,outputTokens:h,cacheCreateTokens:b,cacheReadTokens:T,totalTokens:S.totalTokens,cost:S,daily:O,byModel:d,topSessions:M,topRepos:se,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min(yd(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:wt(S.cents),tokens:Rt(S.totalTokens)}}}H();function as(e){return Math.max(0,Math.min(1,e))}function jo(e){let t=f(),s=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT COUNT(*) AS cnt,
1542
1678
  MAX(started_at) AS latest
1543
- FROM sessions WHERE project_id = ?`).get(e),r=n.cnt;if(r===0)return{projectId:e,projectName:s.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=Zt(r/10),a=n.latest?(Date.now()-new Date(n.latest).getTime())/(1e3*60*60*24):90,c=Zt(1-a/90),u=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1544
- FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,g=Zt((u-2)/3),h=t.prepare(`SELECT COUNT(*) AS total,
1679
+ FROM sessions WHERE project_id = ?`).get(e),r=n.cnt;if(r===0)return{projectId:e,projectName:s.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=as(r/10),a=n.latest?(Date.now()-new Date(n.latest).getTime())/(1e3*60*60*24):90,c=as(1-a/90),d=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1680
+ FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,m=as((d-2)/3),h=t.prepare(`SELECT COUNT(*) AS total,
1545
1681
  SUM(CASE WHEN m.content_text IS NOT NULL AND m.content_text != '' THEN 1 ELSE 0 END) AS covered
1546
1682
  FROM messages m
1547
1683
  JOIN sessions s ON s.id = m.session_id
1548
- WHERE s.project_id = ?`).get(e),b=h.total>0?h.covered/h.total:.5,S=Zt(b),y=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
1684
+ WHERE s.project_id = ?`).get(e),b=h.total>0?h.covered/h.total:.5,T=as(b),S=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
1549
1685
  COUNT(DISTINCT st.session_id) AS tagged
1550
1686
  FROM sessions s
1551
1687
  LEFT JOIN session_tags st ON st.session_id = s.id
1552
- WHERE s.project_id = ?`).get(e),k=y.total>0?y.tagged/y.total:0,w=Zt(k),D=Math.round((o*.2+c*.25+g*.15+S*.2+w*.2)*100);return{projectId:e,projectName:s.name,score:D,breakdown:{sessionCount:{raw:r,score:o,weight:.2},recency:{daysSinceLastSession:Math.round(a),score:c,weight:.25},fragmentation:{avgMessages:Math.round(u*10)/10,score:g,weight:.15},searchCoverage:{ratio:Math.round(b*100)/100,score:S,weight:.2},tagCoverage:{ratio:Math.round(k*100)/100,score:w,weight:.2}}}}function jd(){let t=f().prepare("SELECT id FROM projects ORDER BY name").all(),s=[];for(let n of t){let r=to(n.id);r&&s.push(r)}return s}H();import{execFile as rb}from"node:child_process";import{promisify as ob}from"node:util";import{stat as ib}from"node:fs/promises";var Md=ob(rb),Dd=1e4,ab="%H%x09%aI%x09%s";async function cb(e){try{let{stdout:t}=await Md("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:Dd});return t.trim()==="true"}catch{return!1}}async function lb(e,t,s){let n=["--no-pager","log","--all","--no-color","--since",t,"--until",s,`--pretty=format:${ab}`],{stdout:r}=await Md("git",n,{cwd:e,timeout:Dd,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1553
- `)){if(!c)continue;let[d,u,...g]=c.split(" ");!d||a.has(d)||(a.add(d),o.push({commit_sha:d,committed_at:u??null,subject:g.join(" ")||null}))}return o}function db(e){return f().prepare(`SELECT id, cwd, started_at, ended_at
1554
- FROM sessions WHERE id = ?`).get(e)??null}function ub(e,t,s){if(s.length===0)return 0;let n=f(),r=new Date().toISOString(),o=n.prepare(`INSERT OR IGNORE INTO session_commits
1688
+ WHERE s.project_id = ?`).get(e),w=S.total>0?S.tagged/S.total:0,R=as(w),D=Math.round((o*.2+c*.25+m*.15+T*.2+R*.2)*100);return{projectId:e,projectName:s.name,score:D,breakdown:{sessionCount:{raw:r,score:o,weight:.2},recency:{daysSinceLastSession:Math.round(a),score:c,weight:.25},fragmentation:{avgMessages:Math.round(d*10)/10,score:m,weight:.15},searchCoverage:{ratio:Math.round(b*100)/100,score:T,weight:.2},tagCoverage:{ratio:Math.round(w*100)/100,score:R,weight:.2}}}}function Cd(){let t=f().prepare("SELECT id FROM projects ORDER BY name").all(),s=[];for(let n of t){let r=jo(n.id);r&&s.push(r)}return s}H();import{execFile as tT}from"node:child_process";import{promisify as sT}from"node:util";import{stat as nT}from"node:fs/promises";var vd=sT(tT),Id=1e4,rT="%H%x09%aI%x09%s";async function oT(e){try{let{stdout:t}=await vd("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:Id});return t.trim()==="true"}catch{return!1}}async function iT(e,t,s){let n=["--no-pager","log","--all","--no-color","--since",t,"--until",s,`--pretty=format:${rT}`],{stdout:r}=await vd("git",n,{cwd:e,timeout:Id,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1689
+ `)){if(!c)continue;let[u,d,...m]=c.split(" ");!u||a.has(u)||(a.add(u),o.push({commit_sha:u,committed_at:d??null,subject:m.join(" ")||null}))}return o}function aT(e){return f().prepare(`SELECT id, cwd, started_at, ended_at
1690
+ FROM sessions WHERE id = ?`).get(e)??null}function cT(e,t,s){if(s.length===0)return 0;let n=f(),r=new Date().toISOString(),o=n.prepare(`INSERT OR IGNORE INTO session_commits
1555
1691
  (session_id, commit_sha, committed_at, subject, cwd_snapshot, correlated_at)
1556
- VALUES (?, ?, ?, ?, ?, ?)`),a=0;return n.transaction(d=>{for(let u of d)o.run(e,u.commit_sha,u.committed_at,u.subject,t,r).changes>0&&(a+=1)})(s),a}async function so(e){let t=db(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let s=t.started_at,n=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await ib(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 cb(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await lb(t.cwd,s,n),a=ub(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 mn(e){let t=f(),s=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(s))return[];let n=`${s.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1692
+ VALUES (?, ?, ?, ?, ?, ?)`),a=0;return n.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(s),a}async function Mo(e){let t=aT(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let s=t.started_at,n=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await nT(t.cwd)).isDirectory())return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}catch{return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}if(!await oT(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await iT(t.cwd,s,n),a=cT(e,t.cwd,o);return{sessionId:e,status:"ok",commitsFound:o.length,commitsInserted:a}}catch(o){return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:o.message}}}function Ln(e){let t=f(),s=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(s))return[];let n=`${s.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1557
1693
  NULLIF(sa.alias, '') AS alias,
1558
1694
  p.name AS project,
1559
1695
  s.started_at AS startedAt,
@@ -1567,20 +1703,20 @@ ${o}
1567
1703
  LEFT JOIN session_aliases sa ON sa.session_id = sc.session_id
1568
1704
  WHERE lower(sc.commit_sha) = lower(?)
1569
1705
  OR lower(sc.commit_sha) LIKE ?
1570
- ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(s,n)}function no(e){return f().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1706
+ ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(s,n)}function Do(e){return f().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1571
1707
  FROM session_commits
1572
1708
  WHERE session_id = ?
1573
- ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var pb=3e4;function Fd(e){try{let s=f().prepare(`SELECT MAX(correlated_at) AS last_at
1574
- FROM session_commits WHERE session_id = ?`).get(e),n=s?.last_at?Date.parse(s.last_at):0;if(n&&Date.now()-n<pb)return}catch{}so(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}H();import{execFile as mb}from"node:child_process";import{promisify as gb}from"node:util";import{stat as _b}from"node:fs/promises";var fb=gb(mb),hb=60,Eb=7,bb=7,Sb=5e3;function Tb(){let e=f(),t=s=>{try{return!!e.prepare(s).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1709
+ ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var lT=3e4;function jd(e){try{let s=f().prepare(`SELECT MAX(correlated_at) AS last_at
1710
+ FROM session_commits WHERE session_id = ?`).get(e),n=s?.last_at?Date.parse(s.last_at):0;if(n&&Date.now()-n<lT)return}catch{}Mo(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}H();import{execFile as uT}from"node:child_process";import{promisify as dT}from"node:util";import{stat as pT}from"node:fs/promises";var mT=dT(uT),gT=60,_T=7,fT=7,hT=5e3;function ET(){let e=f(),t=s=>{try{return!!e.prepare(s).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1575
1711
  WHERE (COALESCE(total_input_tokens,0)
1576
1712
  + COALESCE(total_output_tokens,0)
1577
1713
  + COALESCE(total_cache_create_tokens,0)
1578
1714
  + COALESCE(total_cache_read_tokens,0)) > 0
1579
- LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function ro(e){if(!e)return[];let t=new Set,s=[];for(let n of e.split(",")){let r=n.trim().toLowerCase();!r||t.has(r)||(t.add(r),s.push(r))}return s}function Pd(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 yb(){let e=f(),t=e.prepare(`SELECT ss.keywords
1715
+ LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function Fo(e){if(!e)return[];let t=new Set,s=[];for(let n of e.split(",")){let r=n.trim().toLowerCase();!r||t.has(r)||(t.add(r),s.push(r))}return s}function Md(e){return{sessionId:e.session_id,project:e.project,alias:e.alias,startedAt:e.started_at,endedAt:e.ended_at,firstUserMessage:e.first_user_message}}function bT(){let e=f(),t=e.prepare(`SELECT ss.keywords
1580
1716
  FROM session_semantic ss
1581
1717
  JOIN sessions s ON s.id = ss.session_id
1582
1718
  WHERE s.started_at IS NOT NULL
1583
- AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:Eb});if(t.length===0)return null;let s=new Set;for(let o of t)for(let a of ro(o.keywords))s.add(a);if(s.size===0)return null;let n=e.prepare(`SELECT ss.session_id AS session_id,
1719
+ AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:_T});if(t.length===0)return null;let s=new Set;for(let o of t)for(let a of Fo(o.keywords))s.add(a);if(s.size===0)return null;let n=e.prepare(`SELECT ss.session_id AS session_id,
1584
1720
  ss.summary AS summary,
1585
1721
  ss.keywords AS keywords,
1586
1722
  p.name AS project,
@@ -1596,7 +1732,7 @@ ${o}
1596
1732
  WHERE s.started_at IS NOT NULL
1597
1733
  AND s.message_count > 2
1598
1734
  AND julianday('now') - julianday(s.started_at) >= @ageDays
1599
- ORDER BY s.started_at ASC`).all({ageDays:hb});if(n.length===0)return null;let r=null;for(let o of n){let c=ro(o.keywords).filter(d=>s.has(d));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{...Pd(r.row),summary:r.row.summary,keywords:ro(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function wb(){let t=f().prepare(`SELECT s.id AS session_id,
1735
+ ORDER BY s.started_at ASC`).all({ageDays:gT});if(n.length===0)return null;let r=null;for(let o of n){let c=Fo(o.keywords).filter(u=>s.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{...Md(r.row),summary:r.row.summary,keywords:Fo(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function ST(){let t=f().prepare(`SELECT s.id AS session_id,
1600
1736
  p.name AS project,
1601
1737
  NULLIF(sa.alias, '') AS alias,
1602
1738
  s.started_at AS started_at,
@@ -1615,36 +1751,36 @@ ${o}
1615
1751
  AND (COALESCE(s.total_input_tokens, 0)
1616
1752
  + COALESCE(s.total_output_tokens, 0)
1617
1753
  + COALESCE(s.total_cache_create_tokens, 0)
1618
- + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:bb});if(t.length===0)return null;let s=null;for(let n of t){let r=he({inputTokens:n.input_tokens,outputTokens:n.output_tokens,cacheCreateTokens:n.cache_create_tokens,cacheReadTokens:n.cache_read_tokens},n.primary_model);r.cents<=0||(!s||r.cents>s.cents)&&(s={row:n,cents:r.cents,totalTokens:r.totalTokens})}return s?{...Pd(s.row),totalTokens:s.totalTokens,costCents:s.cents,costDisplay:ht(s.cents),tokensDisplay:Et(s.totalTokens),primaryModel:s.row.primary_model,primaryModelLabel:je(s.row.primary_model).label}:null}async function Rb(e){try{if(!(await _b(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await fb("git",["rev-parse","HEAD"],{cwd:e,timeout:Sb}),s=t.trim();return/^[0-9a-f]{40}$/.test(s)?s:null}catch{return null}}async function kb(){let e=f(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1754
+ + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:fT});if(t.length===0)return null;let s=null;for(let n of t){let r=ye({inputTokens:n.input_tokens,outputTokens:n.output_tokens,cacheCreateTokens:n.cache_create_tokens,cacheReadTokens:n.cache_read_tokens},n.primary_model);r.cents<=0||(!s||r.cents>s.cents)&&(s={row:n,cents:r.cents,totalTokens:r.totalTokens})}return s?{...Md(s.row),totalTokens:s.totalTokens,costCents:s.cents,costDisplay:wt(s.cents),tokensDisplay:Rt(s.totalTokens),primaryModel:s.row.primary_model,primaryModelLabel:Pe(s.row.primary_model).label}:null}async function TT(e){try{if(!(await pT(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await mT("git",["rev-parse","HEAD"],{cwd:e,timeout:hT}),s=t.trim();return/^[0-9a-f]{40}$/.test(s)?s:null}catch{return null}}async function yT(){let e=f(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1619
1755
  FROM sessions s
1620
1756
  WHERE s.cwd IS NOT NULL AND s.started_at IS NOT NULL
1621
1757
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1622
- LIMIT 1`).get();if(!t?.cwd)return null;let s=await Rb(t.cwd);if(!s)return null;let n=mn(s);if(n.length===0)return null;let r=n[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1758
+ LIMIT 1`).get();if(!t?.cwd)return null;let s=await TT(t.cwd);if(!s)return null;let n=Ln(s);if(n.length===0)return null;let r=n[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1623
1759
  FROM sessions s
1624
- 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 Ud(){let e=Tb(),t=e.semantic?Promise.resolve().then(()=>{try{return yb()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),s=e.cost?Promise.resolve().then(()=>{try{return wb()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),n=e.git?kb().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,s,n]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}ot();H();ot();async function $d(e,t=50){let s=await Tr(e),n=f(),r=Buffer.from(s.buffer,s.byteOffset,s.byteLength);return n.prepare(`SELECT v.rowid, v.distance, cm.session_id, cm.text, cm.message_uuids
1760
+ WHERE s.id = ?`).get(r.sessionId);return{sessionId:r.sessionId,project:r.project,alias:r.alias,startedAt:r.startedAt,endedAt:o?.ended_at??r.endedAt,firstUserMessage:o?.first_user_message??null,commitSha:r.commitSha,shortSha:r.commitSha.slice(0,7),subject:r.subject,committedAt:r.committedAt,cwd:t.cwd}}async function Dd(){let e=ET(),t=e.semantic?Promise.resolve().then(()=>{try{return bT()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),s=e.cost?Promise.resolve().then(()=>{try{return ST()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),n=e.git?yT().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,s,n]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}ze();H();ze();async function Fd(e,t=50){let s=await qn(e),n=f(),r=Buffer.from(s.buffer,s.byteOffset,s.byteLength);return n.prepare(`SELECT v.rowid, v.distance, cm.session_id, cm.text, cm.message_uuids
1625
1761
  FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
1626
- WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(r,t).map(a=>({sessionId:a.session_id,chunkRowid:a.rowid,distance:a.distance,text:a.text,messageUuids:JSON.parse(a.message_uuids)}))}async function Bd(e,t=10,s=.65){let n=f(),r=n.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=n.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=n.prepare(`SELECT v.rowid, v.distance, cm.session_id FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
1627
- WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(o.embedding,t*5),c=new Map;for(let u of a){if(u.session_id===e)continue;let g=c.get(u.session_id);(g===void 0||u.distance<g)&&c.set(u.session_id,u.distance)}let d=[];for(let[u,g]of c){let h=1-g;h>=s&&d.push({sessionId:u,similarity:h})}return d.sort((u,g)=>g.similarity-u.similarity),d.slice(0,t)}function Ab(){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 Hd(e){let t=Ab(),s=new Map;for(let r of e)for(let o=0;o<r.length;o++){let a=r[o],c=1/(t+o+1),d=s.get(a.id);d?(d.score+=c,d.lanes.push(a.lane),o+1<d.bestRank&&(d.bestRank=o+1,d.bestData=a.data)):s.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let n=[];for(let[r,o]of s)n.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return n.sort((r,o)=>o.score-r.score),n}H();ot();H();function Wd(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function xb(e){let t=[],o=0;for(;o<e.length;){let a=[],c=0;for(;a.length<5&&o<e.length;){let u=e[o],g=Wd(u.content_text??"");if(c+g.length>2e3&&a.length>=3)break;a.push(u),c+=g.length,o++}if(a.length===0)break;let d=a.map(u=>{let g=u.role??"system",h=Wd(u.content_text??"");return`[${g}] ${h}`}).join(`
1762
+ WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(r,t).map(a=>({sessionId:a.session_id,chunkRowid:a.rowid,distance:a.distance,text:a.text,messageUuids:JSON.parse(a.message_uuids)}))}async function Pd(e,t=10,s=.65){let n=f(),r=n.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=n.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=n.prepare(`SELECT v.rowid, v.distance, cm.session_id FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
1763
+ WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(o.embedding,t*5),c=new Map;for(let d of a){if(d.session_id===e)continue;let m=c.get(d.session_id);(m===void 0||d.distance<m)&&c.set(d.session_id,d.distance)}let u=[];for(let[d,m]of c){let h=1-m;h>=s&&u.push({sessionId:d,similarity:h})}return u.sort((d,m)=>m.similarity-d.similarity),u.slice(0,t)}function wT(){let e=process.env.RECALL_RRF_K;if(e){let t=parseInt(e,10);if(!isNaN(t)&&t>=1&&t<=1e3)return t}return 60}function Ud(e){let t=wT(),s=new Map;for(let r of e)for(let o=0;o<r.length;o++){let a=r[o],c=1/(t+o+1),u=s.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):s.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let n=[];for(let[r,o]of s)n.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return n.sort((r,o)=>o.score-r.score),n}H();ze();H();function $d(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function RT(e){let t=[],o=0;for(;o<e.length;){let a=[],c=0;for(;a.length<5&&o<e.length;){let d=e[o],m=$d(d.content_text??"");if(c+m.length>2e3&&a.length>=3)break;a.push(d),c+=m.length,o++}if(a.length===0)break;let u=a.map(d=>{let m=d.role??"system",h=$d(d.content_text??"");return`[${m}] ${h}`}).join(`
1628
1764
 
1629
- `);t.push({messageUuids:a.map(u=>u.uuid),text:d}),o<e.length&&a.length>=3&&(o=Math.max(o-1,o-1))}return t}function qd(e){let s=f().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return xb(s)}var Jd=2e3,Nb=1e4,gn=null,oo=!1,Xd=null;function Ob(){return f().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function Lb(){let e=f(),t=e.prepare("SELECT DISTINCT session_id FROM chunk_queue ORDER BY id LIMIT 1").get();if(!t)return!1;let s=t.session_id;try{e.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(s),e.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(s);let n=qd(s);if(n.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s),!0;let r=n.map(u=>u.text),o=await $t(r),a=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, 'bge-base-en-v1.5', 768, 0, datetime('now'))"),c=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)");e.transaction(()=>{for(let u=0;u<n.length;u++){let g=a.run(s,JSON.stringify(n[u].messageUuids),n[u].text),h=Buffer.from(o[u].buffer,o[u].byteOffset,o[u].byteLength);c.run(g.lastInsertRowid,h)}e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s)})(),Xd=new Date().toISOString()}catch(n){console.error("[vector-worker] failed for session",s,n),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s)}return!0}async function Gd(){if(!ke().loaded)return;let e=await Lb();gn!==null&&clearTimeout(gn),gn=setTimeout(()=>{Gd()},e?Jd:Nb)}function Yd(){if(!oo){if(!ke().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}oo=!0,gn=setTimeout(()=>{Gd()},Jd)}}function Kd(){return{running:oo,queueDepth:Ob(),lastProcessedAt:Xd}}Z();import{existsSync as Zd,mkdirSync as zd,rmSync as Vd,createWriteStream as Cb,statSync as Ib}from"node:fs";import{join as _n}from"node:path";import{createHash as vb}from"node:crypto";import{readFile as jb}from"node:fs/promises";var Mb="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",Qd=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function eu(){return _n($,"models","BAAI","bge-base-en-v1.5")}function io(){let e=eu();return Qd.every(t=>Zd(_n(e,t.path)))}async function tu(e){let t=eu();zd(t,{recursive:!0}),zd(_n(t,"onnx"),{recursive:!0});for(let s of Qd){let n=_n(t,s.path),r=Mb+s.path,o=0;Zd(n)&&(o=Ib(n).size);let a={};o>0&&(a.Range=`bytes=${o}-`);let c=await fetch(r,{headers:a});if(!c.ok&&c.status!==206)throw new Error(`Failed to download ${s.path}: HTTP ${c.status}`);let d=c.headers.get("content-length"),u=d?o+Number(d):0,g=c.body;if(!g)throw new Error(`No response body for ${s.path}`);let h=Cb(n,{flags:o>0?"a":"w"}),b=g.getReader(),S=o;for(;;){let{done:w,value:D}=await b.read();if(w)break;h.write(Buffer.from(D)),S+=D.byteLength,e?.(s.path,S,u)}if(h.end(),await new Promise((w,D)=>{h.on("finish",w),h.on("error",D)}),s.sha256==="TODO_PLACEHOLDER")throw Vd(n),new Error(`Refusing to install: SHA-256 not pinned for ${s.path}. Update model-download.ts.`);let y=await jb(n);if(vb("sha256").update(y).digest("hex")!==s.sha256)throw Vd(n),new Error(`SHA-256 mismatch for ${s.path}`)}}H();var Db=[/\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],Fb=1440*60*1e3;function Pb(e){let t=f(),s=t.prepare(`SELECT content_text, tool_names FROM messages
1765
+ `);t.push({messageUuids:a.map(d=>d.uuid),text:u}),o<e.length&&a.length>=3&&(o=Math.max(o-1,o-1))}return t}function Bd(e){let s=f().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return RT(s)}var Hd=2e3,AT=1e4,Cn=null,Po=!1,Wd=null;function kT(){return f().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function xT(){let e=f(),t=e.prepare("SELECT DISTINCT session_id FROM chunk_queue ORDER BY id LIMIT 1").get();if(!t)return!1;let s=t.session_id;try{e.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(s),e.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(s);let n=Bd(s);if(n.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s),!0;let r=n.map(d=>d.text),o=await Ct(r),a=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, 'bge-base-en-v1.5', 768, 0, datetime('now'))"),c=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)");e.transaction(()=>{for(let d=0;d<n.length;d++){let m=a.run(s,JSON.stringify(n[d].messageUuids),n[d].text),h=Buffer.from(o[d].buffer,o[d].byteOffset,o[d].byteLength);c.run(m.lastInsertRowid,h)}e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s)})(),Wd=new Date().toISOString()}catch(n){console.error("[vector-worker] failed for session",s,n),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(s)}return!0}async function qd(){if(!Ne().loaded)return;let e=await xT();Cn!==null&&clearTimeout(Cn),Cn=setTimeout(()=>{qd()},e?Hd:AT)}function Xd(){if(!Po){if(!Ne().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}Po=!0,Cn=setTimeout(()=>{qd()},Hd)}}function Jd(){return{running:Po,queueDepth:kT(),lastProcessedAt:Wd}}Q();import{existsSync as zd,mkdirSync as Yd,rmSync as Gd,createWriteStream as NT,statSync as OT}from"node:fs";import{join as vn}from"node:path";import{createHash as LT}from"node:crypto";import{readFile as CT}from"node:fs/promises";var vT="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",Kd=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function Vd(){return vn(U,"models","BAAI","bge-base-en-v1.5")}function Uo(){let e=Vd();return Kd.every(t=>zd(vn(e,t.path)))}async function Zd(e){let t=Vd();Yd(t,{recursive:!0}),Yd(vn(t,"onnx"),{recursive:!0});for(let s of Kd){let n=vn(t,s.path),r=vT+s.path,o=0;zd(n)&&(o=OT(n).size);let a={};o>0&&(a.Range=`bytes=${o}-`);let c=await fetch(r,{headers:a});if(!c.ok&&c.status!==206)throw new Error(`Failed to download ${s.path}: HTTP ${c.status}`);let u=c.headers.get("content-length"),d=u?o+Number(u):0,m=c.body;if(!m)throw new Error(`No response body for ${s.path}`);let h=NT(n,{flags:o>0?"a":"w"}),b=m.getReader(),T=o;for(;;){let{done:R,value:D}=await b.read();if(R)break;h.write(Buffer.from(D)),T+=D.byteLength,e?.(s.path,T,d)}if(h.end(),await new Promise((R,D)=>{h.on("finish",R),h.on("error",D)}),s.sha256==="TODO_PLACEHOLDER")throw Gd(n),new Error(`Refusing to install: SHA-256 not pinned for ${s.path}. Update model-download.ts.`);let S=await CT(n);if(LT("sha256").update(S).digest("hex")!==s.sha256)throw Gd(n),new Error(`SHA-256 mismatch for ${s.path}`)}}H();var IT=[/\btask\s+complete/i,/\bdone\b/i,/\bfinished\b/i,/\bimplemented\b/i,/\bcompleted\b/i,/\bshipped\b/i,/\ball\s+(?:tests?\s+)?pass/i,/\bsuccessfully\b/i],jT=1440*60*1e3;function MT(e){let t=f(),s=t.prepare(`SELECT content_text, tool_names FROM messages
1630
1766
  WHERE session_id = ? AND role = 'assistant'
1631
- ORDER BY timestamp DESC LIMIT 5`).all(e),n=!1;for(let d of s)if(d.content_text&&Db.some(u=>u.test(d.content_text))){n=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1632
- WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let d of r){let u=d.tool_names??"",g=d.content_text??"";/\bWrite\b|\bEdit\b/.test(u)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(g)&&/pass|ok|✓/i.test(g)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(g)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(g)||/tsc.*(?:0 errors|no errors)/i.test(g))&&(o.buildSuccess=!0)}return n?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:n}:{status:"neutral",evidence:o,claimFound:n}}function Ub(e){let t=Pb(e);return f().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function su(e){let s=f().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(s?.verification_status&&s.verification_computed_at&&Date.now()-s.verification_computed_at<Fb){let n={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:s.verification_status,evidence:n,claimFound:s.verification_status!=="neutral"}}return Ub(e)}import{readFileSync as $b,writeFileSync as Bb,mkdirSync as Hb,chmodSync as Wb}from"node:fs";import{join as nu}from"node:path";import{homedir as ru}from"node:os";function ou(){return nu(ru(),".recall","config.json")}function iu(){try{return JSON.parse($b(ou(),"utf-8"))}catch{return{}}}function qb(e){let t=ou();Hb(nu(ru(),".recall"),{recursive:!0}),Bb(t,JSON.stringify(e,null,2)+`
1633
- `,"utf-8"),Wb(t,384)}function ao(){let t=iu().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function au(e){let t=iu();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},qb(t)}var Jb=[{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 Xb(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),s=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-s.length))}${s}`}function Gb(e){let t=5381;for(let s=0;s<e.length;s++)t=(t<<5)+t+e.charCodeAt(s)|0;return(t>>>0).toString(36)}function qe(e){if(!e)return{redacted:e,count:0};let t=e,s=0,n=new Set;for(let r of Jb)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${Gb(o)}`;return n.has(a)||(n.add(a),s+=1),`[REDACTED ${r.name}: ${Xb(o)}]`});return{redacted:t,count:s}}var sS=5e3,_o={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},co=0,lo=_o,uo=null;async function nS(){return Date.now()-co>=sS&&!uo&&(uo=Ds().then(s=>(lo=s,co=Date.now(),s)).catch(()=>(co=Date.now(),lo=_o,_o)).finally(()=>{uo=null})),lo}var rS=2e3,oS=6,En=new Map;function Qt(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function iS(e,t){let s=Date.now(),n=(En.get(e)??[]).filter(a=>s-a.ts<rS);return En.set(e,n),n.length<2||n[n.length-1].name===t?!1:n.slice(0,-1).some(a=>a.name===t)}function aS(e,t){let s=En.get(e)??[];for(s.push({name:t,ts:Date.now()});s.length>oS;)s.shift();En.set(e,s)}function lu(e,t){let s=t.trim();if(!s)return 0;if(ne(s)){let o=tt(s);if(!o)return 0;s=o}if(re(s))return 0;if(iS(e,s))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${s}", flap signature (competing editor sync sources)`),0;aS(e,s);let n=I.sessionsFor(e),r=0;for(let o of n)try{if(Te(o)===s)continue;me(o,s),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${s}" propagated to ${r} session(s)`),r}var du=(()=>{try{let e=bn(Eo(bo(import.meta.url)),"..","..","package.json");return JSON.parse(hn(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),po=!1,mo=!1,go=!1,lS=Eo(bo(import.meta.url)),fo=bn(lS,"..","web"),ho=bn(fo,"index.html"),dS=zb(ho);function uu(){return f().prepare(`SELECT
1767
+ ORDER BY timestamp DESC LIMIT 5`).all(e),n=!1;for(let u of s)if(u.content_text&&IT.some(d=>d.test(u.content_text))){n=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1768
+ WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let u of r){let d=u.tool_names??"",m=u.content_text??"";/\bWrite\b|\bEdit\b/.test(d)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(m)&&/pass|ok|✓/i.test(m)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(m)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(m)||/tsc.*(?:0 errors|no errors)/i.test(m))&&(o.buildSuccess=!0)}return n?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:n}:{status:"neutral",evidence:o,claimFound:n}}function DT(e){let t=MT(e);return f().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function Qd(e){let s=f().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(s?.verification_status&&s.verification_computed_at&&Date.now()-s.verification_computed_at<jT){let n={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:s.verification_status,evidence:n,claimFound:s.verification_status!=="neutral"}}return DT(e)}import{readFileSync as FT,writeFileSync as PT,mkdirSync as UT,chmodSync as $T}from"node:fs";import{join as ep}from"node:path";import{homedir as tp}from"node:os";function sp(){return ep(tp(),".recall","config.json")}function np(){try{return JSON.parse(FT(sp(),"utf-8"))}catch{return{}}}function BT(e){let t=sp();UT(ep(tp(),".recall"),{recursive:!0}),PT(t,JSON.stringify(e,null,2)+`
1769
+ `,"utf-8"),$T(t,384)}function $o(){let t=np().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function rp(e){let t=np();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},BT(t)}var KT=5e3,Go={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},Bo=0,Ho=Go,Wo=null;async function VT(){return Date.now()-Bo>=KT&&!Wo&&(Wo=cn().then(s=>(Ho=s,Bo=Date.now(),s)).catch(()=>(Bo=Date.now(),Ho=Go,Go)).finally(()=>{Wo=null})),Ho}var ZT=2e3,QT=6,jn=new Map;function cs(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function ey(e,t){let s=Date.now(),n=(jn.get(e)??[]).filter(a=>s-a.ts<ZT);return jn.set(e,n),n.length<2||n[n.length-1].name===t?!1:n.slice(0,-1).some(a=>a.name===t)}function ty(e,t){let s=jn.get(e)??[];for(s.push({name:t,ts:Date.now()});s.length>QT;)s.shift();jn.set(e,s)}function ip(e,t){let s=t.trim();if(!s)return 0;if(ie(s)){let o=lt(s);if(!o)return 0;s=o}if(ce(s))return 0;if(ey(e,s))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${s}", flap signature (competing editor sync sources)`),0;ty(e,s);let n=v.sessionsFor(e),r=0;for(let o of n)try{if(Ae(o)===s)continue;be(o,s),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${s}" propagated to ${r} session(s)`),r}var ap=(()=>{try{let e=Mn(Ko(Vo(import.meta.url)),"..","..","package.json");return JSON.parse(Yo(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),qo=!1,Xo=!1,Jo=!1,ny=Ko(Vo(import.meta.url)),zo=Mn(ny,"..","web"),up=Mn(zo,"index.html"),ry=qT(up);function cp(){return f().prepare(`SELECT
1634
1770
  (SELECT COUNT(*) FROM projects) AS projects,
1635
1771
  (SELECT COUNT(*) FROM sessions) AS sessions,
1636
1772
  (SELECT COUNT(*) FROM messages) AS messages,
1637
1773
  (SELECT MIN(started_at) FROM sessions WHERE started_at IS NOT NULL) AS earliest,
1638
- (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var uS=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,pS=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function es(e,t){if((await Tt()).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 fn=new Map,mu=0,pu=0,gu=null,mS=6e4;function gS(){mu+=1;let e=Date.now();e-pu<mS||(pu=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 _S(){gu=new Date().toISOString()}function fS(){return{silentTerminalRejections:mu,lastTerminalSyncAt:gu}}function hS(e){let t=new Yb;if(t.use("*",cS({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let p=i.req.raw.headers.get("host")??"";if(!uS.test(p))return i.text("Forbidden: invalid Host header",403);let m=i.req.raw.headers.get("origin");if(m&&!pS.test(m))return i.text("Forbidden: cross-origin request rejected",403);await l()}),e){let i=Buffer.from(e,"utf8");t.use("/api/*",async(l,p)=>{if(l.req.method==="GET"&&l.req.path==="/api/health")return p();let m=l.req.raw.headers.get("x-recall-token")??"",_=!1;if(m.length===e.length)try{_=eS(Buffer.from(m,"utf8"),i)}catch{_=!1}return _?p():(l.req.path.startsWith("/api/terminal/")&&gS(),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:du,uptimeSeconds:Math.round(process.uptime()),pipeline:fS()})),t.get("/api/stats",i=>i.json(uu())),t.get("/api/stats/session/:id",i=>{let l=Cd(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=Id(i.req.param("name"));return l?i.json(l):i.json({error:"project not found"},404)}),t.get("/api/stats/overview",i=>{let l=i.req.query("range"),p=l==="7d"?"7d":l==="30d"?"30d":"all";return i.json(vd(p))}),t.post("/api/stats/backfill",async i=>{let l=await i.req.json().catch(()=>({})),p=l.limit?Math.max(1,Math.min(1e5,Number(l.limit))):5e3;if(p>5e3){let _=Nd({limit:p});return i.json({mode:"background",started:_,alreadyRunning:!_&&Od(),limit:p,lastRun:Zr()})}let m=xd({limit:p});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:p,result:m,lastRun:Zr()})}),t.get("/api/stats/health",i=>i.json(jd())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),p=to(l);return p?i.json(p):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:ao()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&au(l.enabled),i.json({enabled:ao()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),p=su(l);return i.json(p)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),m=f().prepare(`SELECT tool_names, raw_json FROM messages
1639
- WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''`).all(l),_=m.length,E=new Set;for(let T of m){if(!/Read|Write|Edit/.test(T.tool_names))continue;let R=T.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(R)for(let A of R){let x=A.match(/":\s*"([^"]+)"/);x&&E.add(x[1])}}return i.json({filesReferenced:E.size,toolCallCount:_})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),p=no(l);if(p.length>0||i.req.query("refresh")!=="1")return i.json({commits:p});let m=await so(l);return i.json({commits:no(l),status:m.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:mn(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await Tt();return i.json(l)}),t.post("/api/feedback",async i=>{let l;try{l=await i.req.json()}catch{return i.json({error:"invalid json"},400)}if(!l||typeof l!="object")return i.json({error:"invalid body"},400);let p=process.env.RECALL_FEEDBACK_API??"https://clauderecall.com/api/feedback",m=p==="https://clauderecall.com/api/feedback",_=await Tt(),E=St(),T=m&&_.tier==="pro"&&E?E.license_jwt:null,R=(()=>{try{let N=bn(Eo(bo(import.meta.url)),"..","..","package.json");return JSON.parse(hn(N,"utf8")).version}catch{return"unknown"}})(),A=l,x={score:A.score,comment:A.comment??null,surface:"web",version:typeof A.version=="string"?A.version:R,os:typeof A.os=="string"?A.os:process.platform,trigger_kind:typeof A.trigger_kind=="string"?A.trigger_kind:"manual",license_jwt:T};try{let N=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(x)}),v=await N.json().catch(()=>({}));return i.json(v,N.status)}catch(N){let v=N instanceof Error?N.message:"network error";return i.json({error:"upstream_unreachable",detail:v},502)}}),t.get("/api/discover/today",es,async i=>{try{return i.json(await Ud())}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:Rr(),orphan_projects:Kc()})),t.get("/api/bug-synthesis",i=>{let l=i.req.query("scope"),p=i.req.query("target_id"),m=i.req.query("limit"),_=l==="cluster"||l==="project"?l:void 0,E=m?Math.max(1,Number(m)):50,T=nl({scope:_,target_id:p??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 p=rl(l);return i.json({counts:Array.from(p.entries()).map(([m,_])=>({target_id:m,count:_}))})}),t.get("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=kr(l);return p?i.json({result:p}):i.json({error:"not found"},404)}),t.delete("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(ol(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let s=j.object({name:j.string().min(1).max(100),description:j.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),p=s.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let m=zc({name:p.data.name,description:p.data.description??null});return i.json({macro_repo:m},201)}catch(m){let _=m instanceof Error?m.message:String(m);return _.includes("UNIQUE constraint")?i.json({error:`a macro repo named "${p.data.name}" already exists`},409):i.json({error:_},400)}});let n=j.object({name:j.string().min(1).max(100).optional(),description:j.string().max(500).nullable().optional()});t.patch("/api/macro-repos/:id",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),m=n.safeParse(p);if(!m.success)return i.json({error:"invalid request body"},400);try{let _=Vc(l,m.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)?(Zc(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=j.object({project_id:j.number().int().positive()});t.post("/api/macro-repos/:id/members",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),m=r.safeParse(p);if(!m.success)return i.json({error:"invalid request body"},400);try{return Qc(l,m.data.project_id),i.json({macro_repo:it(l)})}catch(_){let E=_ instanceof Error?_.message:String(_);return i.json({error:E},E.includes("not found")?404:400)}}),t.delete("/api/macro-repos/:id/members/:projectId",i=>{let l=Number(i.req.param("id")),p=Number(i.req.param("projectId"));return!Number.isFinite(l)||!Number.isFinite(p)?i.json({error:"invalid id"},400):(el(l,p),i.json({macro_repo:it(l)}))}),t.get("/api/projects",i=>{let l=f(),m=i.req.query("system")==="1"||i.req.query("system")==="true"?"":" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1640
- COUNT(CASE WHEN s.id IS NOT NULL${m} THEN 1 END) AS session_count,
1641
- COALESCE(SUM(CASE WHEN s.id IS NOT NULL${m} THEN s.message_count ELSE 0 END), 0) AS message_count,
1774
+ (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var oy=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,iy=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function ls(e,t){if((await xt()).tier!=="pro")return e.json({error:"pro_required",message:"This feature requires a Claude Recall Pro license.",upgrade_url:"https://clauderecall.com/pricing",activate_command:"recall activate <license-key>"},402);await t()}var In=new Map,dp=0,lp=0,pp=null,ay=6e4;function cy(){dp+=1;let e=Date.now();e-lp<ay||(lp=e,console.warn("[auth] /api/terminal/* request rejected without a valid X-Recall-Token. The VS Code / Cursor extension is likely outdated relative to the daemon \u2014 tab names will not flow through, and session titles will fall back to the heuristic first-message snippet. Reinstall: `code --install-extension extensions/vscode/clauderecall-vscode-*.vsix` and restart the extension host. Run `recall doctor` for a full pipeline view."))}function ly(){pp=new Date().toISOString()}function uy(){let e=Ba();return{silentTerminalRejections:dp,lastTerminalSyncAt:pp,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns}}}function dy(e){let t=new HT;if(t.use("*",sy({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let p=i.req.raw.headers.get("host")??"";if(!oy.test(p))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!iy.test(g))return i.text("Forbidden: cross-origin request rejected",403);await l()}),e){let i=Buffer.from(e,"utf8");t.use("/api/*",async(l,p)=>{if(l.req.method==="GET"&&l.req.path==="/api/health")return p();let g=l.req.raw.headers.get("x-recall-token")??"",_=!1;if(g.length===e.length)try{_=GT(Buffer.from(g,"utf8"),i)}catch{_=!1}return _?p():(l.req.path.startsWith("/api/terminal/")&&cy(),l.json({error:"unauthorized"},401))})}t.use("*",async(i,l)=>{await l(),i.res.headers.set("Content-Security-Policy","default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://clauderecall.com; font-src 'self' data:; connect-src 'self' data: https://clauderecall.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"),i.res.headers.set("X-Content-Type-Options","nosniff"),i.res.headers.set("X-Frame-Options","DENY"),i.res.headers.set("Referrer-Policy","no-referrer"),i.res.headers.set("Cross-Origin-Resource-Policy","same-origin")}),t.get("/api/health",i=>i.json({status:"ok",version:ap,uptimeSeconds:Math.round(process.uptime()),pipeline:uy()})),t.get("/api/stats",i=>i.json(cp())),t.get("/api/stats/session/:id",i=>{let l=Nd(i.req.param("id"));return l?i.json(l):i.json({error:"session not found"},404)}),t.get("/api/stats/project/:name",i=>{let l=Od(i.req.param("name"));return l?i.json(l):i.json({error:"project not found"},404)}),t.get("/api/stats/overview",i=>{let l=i.req.query("range"),p=l==="7d"?"7d":l==="30d"?"30d":"all";return i.json(Ld(p))}),t.post("/api/stats/backfill",async i=>{let l=await i.req.json().catch(()=>({})),p=l.limit?Math.max(1,Math.min(1e5,Number(l.limit))):5e3;if(p>5e3){let _=Ad({limit:p});return i.json({mode:"background",started:_,alreadyRunning:!_&&kd(),limit:p,lastRun:Co()})}let g=Rd({limit:p});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:p,result:g,lastRun:Co()})}),t.get("/api/stats/health",i=>i.json(Cd())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),p=jo(l);return p?i.json(p):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:$o()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&rp(l.enabled),i.json({enabled:$o()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),p=Qd(l);return i.json(p)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=f().prepare(`SELECT tool_names, raw_json FROM messages
1775
+ WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''`).all(l),_=g.length,E=new Set;for(let y of g){if(!/Read|Write|Edit/.test(y.tool_names))continue;let A=y.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(A)for(let k of A){let x=k.match(/":\s*"([^"]+)"/);x&&E.add(x[1])}}return i.json({filesReferenced:E.size,toolCallCount:_})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),p=Do(l);if(p.length>0||i.req.query("refresh")!=="1")return i.json({commits:p});let g=await Mo(l);return i.json({commits:Do(l),status:g.status})}),t.get("/api/commits/:sha/session",i=>{let l=i.req.param("sha");return/^[0-9a-fA-F]{4,40}$/.test(l)?i.json({sessions:Ln(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await xt();return i.json(l)}),t.post("/api/feedback",async i=>{let l;try{l=await i.req.json()}catch{return i.json({error:"invalid json"},400)}if(!l||typeof l!="object")return i.json({error:"invalid body"},400);let p=process.env.RECALL_FEEDBACK_API??"https://clauderecall.com/api/feedback",g=p==="https://clauderecall.com/api/feedback",_=await xt(),E=kt(),y=g&&_.tier==="pro"&&E?E.license_jwt:null,A=(()=>{try{let N=Mn(Ko(Vo(import.meta.url)),"..","..","package.json");return JSON.parse(Yo(N,"utf8")).version}catch{return"unknown"}})(),k=l,x={score:k.score,comment:k.comment??null,surface:"web",version:typeof k.version=="string"?k.version:A,os:typeof k.os=="string"?k.os:process.platform,trigger_kind:typeof k.trigger_kind=="string"?k.trigger_kind:"manual",license_jwt:y};try{let N=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(x)}),I=await N.json().catch(()=>({}));return i.json(I,N.status)}catch(N){let I=N instanceof Error?N.message:"network error";return i.json({error:"upstream_unreachable",detail:I},502)}}),t.get("/api/discover/today",ls,async i=>{try{return i.json(await Dd())}catch(l){return console.error("[discover.today]",l),i.json({rediscovered:null,expensive:null,authored:null,availability:{semantic:!1,cost:!1,git:!1},generatedAt:new Date().toISOString(),error:l.message},500)}}),t.get("/api/macro-repos",i=>i.json({macro_repos:ro(),orphan_projects:eu()})),t.get("/api/bug-synthesis",i=>{let l=i.req.query("scope"),p=i.req.query("target_id"),g=i.req.query("limit"),_=l==="cluster"||l==="project"?l:void 0,E=g?Math.max(1,Number(g)):50,y=cu({scope:_,target_id:p??void 0,limit:E});return i.json({results:y})}),t.get("/api/bug-synthesis/counts",i=>{let l=i.req.query("scope");if(l!=="cluster"&&l!=="project")return i.json({error:'scope must be "cluster" or "project"'},400);let p=lu(l);return i.json({counts:Array.from(p.entries()).map(([g,_])=>({target_id:g,count:_}))})}),t.get("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=oo(l);return p?i.json({result:p}):i.json({error:"not found"},404)}),t.delete("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(uu(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let s=j.object({name:j.string().min(1).max(100),description:j.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),p=s.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=tu({name:p.data.name,description:p.data.description??null});return i.json({macro_repo:g},201)}catch(g){let _=g instanceof Error?g.message:String(g);return _.includes("UNIQUE constraint")?i.json({error:`a macro repo named "${p.data.name}" already exists`},409):i.json({error:_},400)}});let n=j.object({name:j.string().min(1).max(100).optional(),description:j.string().max(500).nullable().optional()});t.patch("/api/macro-repos/:id",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),g=n.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{let _=su(l,g.data);return i.json({macro_repo:_})}catch(_){let E=_ instanceof Error?_.message:String(_);return E.includes("not found")?i.json({error:E},404):E.includes("UNIQUE constraint")?i.json({error:"another macro repo already has that name"},409):i.json({error:E},400)}}),t.delete("/api/macro-repos/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(nu(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=j.object({project_id:j.number().int().positive()});t.post("/api/macro-repos/:id/members",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),g=r.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{return ru(l,g.data.project_id),i.json({macro_repo:pt(l)})}catch(_){let E=_ instanceof Error?_.message:String(_);return i.json({error:E},E.includes("not found")?404:400)}}),t.delete("/api/macro-repos/:id/members/:projectId",i=>{let l=Number(i.req.param("id")),p=Number(i.req.param("projectId"));return!Number.isFinite(l)||!Number.isFinite(p)?i.json({error:"invalid id"},400):(ou(l,p),i.json({macro_repo:pt(l)}))}),t.get("/api/projects",i=>{let l=f(),g=i.req.query("system")==="1"||i.req.query("system")==="true"?"":" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1776
+ COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count,
1777
+ COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count,
1642
1778
  MAX(COALESCE(s.ended_at, s.started_at)) AS latest
1643
1779
  FROM projects p
1644
1780
  LEFT JOIN sessions s ON s.project_id = p.id
1645
1781
  GROUP BY p.id
1646
- ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all();return i.json(_)}),t.get("/api/graph/:project",i=>{let l=f(),p=i.req.param("project"),m=l.prepare(`SELECT id, name, decoded_path FROM projects
1647
- WHERE name = ? LIMIT 1`).get(p);if(!m)return i.json({error:`project "${p}" not found`},404);let _=l.prepare(`SELECT s.id,
1782
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all();return i.json(_)}),t.get("/api/graph/:project",i=>{let l=f(),p=i.req.param("project"),g=l.prepare(`SELECT id, name, decoded_path FROM projects
1783
+ WHERE name = ? LIMIT 1`).get(p);if(!g)return i.json({error:`project "${p}" not found`},404);let _=l.prepare(`SELECT s.id,
1648
1784
  s.auto_title,
1649
1785
  s.auto_title_source,
1650
1786
  s.first_user_message,
@@ -1655,11 +1791,11 @@ ${o}
1655
1791
  FROM sessions s
1656
1792
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1657
1793
  WHERE s.project_id = ?
1658
- ORDER BY s.started_at`).all(m.id),E=_.map(A=>A.id),T=[];if(E.length>0){let A=E.map(()=>"?").join(",");T=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1794
+ ORDER BY s.started_at`).all(g.id),E=_.map(k=>k.id),y=[];if(E.length>0){let k=E.map(()=>"?").join(",");y=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1659
1795
  FROM thread_edges
1660
- WHERE session_id IN (${A})
1796
+ WHERE session_id IN (${k})
1661
1797
  AND (parent_session_id IS NULL
1662
- OR parent_session_id IN (${A}))`).all(...E,...E)}let R=_.map(A=>{let x=A.alias??A.auto_title??A.first_user_message??A.id.slice(0,8),N=null,v=null;if(A.auto_title?.startsWith("/")){let C=A.auto_title.split(" \xB7 ");N=C[0],v=C.length>1?C.slice(1).join(" \xB7 "):null}return{id:A.id.slice(0,8),full_id:A.id,title:x,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:N,brand:v}});return i.json({project:m,sessions:R,thread_edges:T})});let o=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),a=new Set(["pending","approved","rejected"]),c=new Set(["L1","L2","L3","L4","user"]);t.get("/api/links",i=>{let l=i.req.query("source_id")??void 0,p=i.req.query("target_id")??void 0,m=i.req.query("type"),_=i.req.query("approved"),E=i.req.query("limit"),T;if(m){if(!o.has(m))return i.json({error:`invalid type: ${m}`},400);T=m}let R=_==="1"||_==="true",A=E?Number(E):void 0;if(A!==void 0&&(!Number.isFinite(A)||A<1))return i.json({error:"invalid limit"},400);try{let x=As({sourceSessionId:l,targetSessionId:p,linkType:T,approvedOnly:R,limit:A});return i.json({links:x})}catch(x){return i.json({error:x.message},400)}}),t.get("/api/links/suggestions",i=>{let l=i.req.query("status"),p=i.req.query("source_id")??void 0,m=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 R;if(_){if(!c.has(_))return i.json({error:`invalid inferred_by: ${_}`},400);R=_}let A=E?Number(E):void 0;if(A!==void 0&&(!Number.isFinite(A)||A<1))return i.json({error:"invalid limit"},400);try{let x=Ve({status:T,sourceSessionId:p,targetSessionId:m,inferredBy:R,limit:A}),N=new Set;for(let O of x)N.add(O.source_session_id),N.add(O.target_session_id);let v=new Map;if(N.size>0){let O=Array.from(N),F=O.map(()=>"?").join(","),Q=f().prepare(`SELECT s.id,
1798
+ OR parent_session_id IN (${k}))`).all(...E,...E)}let A=_.map(k=>{let x=k.alias??k.auto_title??k.first_user_message??k.id.slice(0,8),N=null,I=null;if(k.auto_title?.startsWith("/")){let C=k.auto_title.split(" \xB7 ");N=C[0],I=C.length>1?C.slice(1).join(" \xB7 "):null}return{id:k.id.slice(0,8),full_id:k.id,title:x,alias:k.alias,auto_title:k.auto_title,auto_title_source:k.auto_title_source,title_quality:k.title_quality,started_at:k.started_at,msgs:k.message_count,skill:N,brand:I}});return i.json({project:g,sessions:A,thread_edges:y})});let o=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),a=new Set(["pending","approved","rejected"]),c=new Set(["L1","L2","L3","L4","user"]);t.get("/api/links",i=>{let l=i.req.query("source_id")??void 0,p=i.req.query("target_id")??void 0,g=i.req.query("type"),_=i.req.query("approved"),E=i.req.query("limit"),y;if(g){if(!o.has(g))return i.json({error:`invalid type: ${g}`},400);y=g}let A=_==="1"||_==="true",k=E?Number(E):void 0;if(k!==void 0&&(!Number.isFinite(k)||k<1))return i.json({error:"invalid limit"},400);try{let x=hs({sourceSessionId:l,targetSessionId:p,linkType:y,approvedOnly:A,limit:k});return i.json({links:x})}catch(x){return i.json({error:x.message},400)}}),t.get("/api/links/suggestions",i=>{let l=i.req.query("status"),p=i.req.query("source_id")??void 0,g=i.req.query("target_id")??void 0,_=i.req.query("inferred_by"),E=i.req.query("limit"),y;if(l){if(!a.has(l))return i.json({error:`invalid status: ${l}`},400);y=l}let A;if(_){if(!c.has(_))return i.json({error:`invalid inferred_by: ${_}`},400);A=_}let k=E?Number(E):void 0;if(k!==void 0&&(!Number.isFinite(k)||k<1))return i.json({error:"invalid limit"},400);try{let x=Ke({status:y,sourceSessionId:p,targetSessionId:g,inferredBy:A,limit:k}),N=new Set;for(let L of x)N.add(L.source_session_id),N.add(L.target_session_id);let I=new Map;if(N.size>0){let L=Array.from(N),F=L.map(()=>"?").join(","),ee=f().prepare(`SELECT s.id,
1663
1799
  NULLIF(sa.alias, '') AS alias,
1664
1800
  s.auto_title,
1665
1801
  s.first_user_message,
@@ -1667,7 +1803,7 @@ ${o}
1667
1803
  FROM sessions s
1668
1804
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1669
1805
  LEFT JOIN projects p ON p.id = s.project_id
1670
- WHERE s.id IN (${F})`).all(...O);for(let V of Q){let W=V.first_user_message?V.first_user_message.slice(0,80):null,G=V.alias??V.auto_title??W??V.id.slice(0,8);v.set(V.id,{title:G,project:V.project})}}let C=x.map(O=>{let F=v.get(O.source_session_id),U=v.get(O.target_session_id);return{...O,source_title:F?.title??O.source_session_id.slice(0,8),source_project:F?.project??null,target_title:U?.title??O.target_session_id.slice(0,8),target_project:U?.project??null}});return i.json({suggestions:C})}catch(x){return i.json({error:x.message},400)}}),t.get("/api/output-index/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=Be(l);return p?i.json(p):i.json({error:`no output index for session ${l}`},404)});let d=new Set(["pagerank","embedding-rerank","hybrid"]);t.get("/api/neighborhood/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=i.req.query("budget"),m=p!==void 0?Number(p):4e3;if(!Number.isFinite(m)||m<100)return i.json({error:"budget must be a number \u2265 100"},400);let _=i.req.query("scoring")??"hybrid";if(!d.has(_))return i.json({error:`invalid scoring: ${_}; valid: pagerank, embedding-rerank, hybrid`},400);let E=_,T=i.req.query("max_depth"),R=T!==void 0?Number(T):2;if(!Number.isFinite(R)||R<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let A,x=i.req.query("edge_types");if(x){let F=x.split(",").map(U=>U.trim()).filter(Boolean);for(let U of F)if(!o.has(U))return i.json({error:`invalid edge_type: ${U}`},400);A=F}let N=i.req.query("include_wiki_links"),v=N===void 0?!0:!(N==="0"||N==="false"),C=i.req.query("include_suggestions"),O=C==="1"||C==="true";try{let F=Os(l,{budget:m,scoring:E,maxDepth:R,edgeTypes:A,includeWikiLinks:v,includeSuggestions:O});return i.json(F)}catch(F){let U=F instanceof Error?F.message:"unknown error",Q=/not found/.test(U)?404:500;return i.json({error:U},Q)}}),t.get("/api/bug-patterns",i=>{let l=i.req.query("min_count"),p=i.req.query("status"),m=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 R;if(p==="open")R=!1;else if(p==="resolved")R=!0;else if(p&&p!=="all")return i.json({error:`invalid status: ${p}; valid: open, resolved, all`},400);let A=_?Number(_):void 0;if(A!==void 0&&(!Number.isFinite(A)||A<1))return i.json({error:"invalid limit"},400);let x=E?Number(E):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<0))return i.json({error:"invalid offset"},400);try{let N=Wa({minOccurrenceCount:T,hasResolved:R,project:m,limit:A,offset:x});return i.json(N)}catch(N){return i.json({error:N.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=f(),m=l.prepare(`SELECT p.name AS project,
1806
+ WHERE s.id IN (${F})`).all(...L);for(let Z of ee){let q=Z.first_user_message?Z.first_user_message.slice(0,80):null,G=Z.alias??Z.auto_title??q??Z.id.slice(0,8);I.set(Z.id,{title:G,project:Z.project})}}let C=x.map(L=>{let F=I.get(L.source_session_id),B=I.get(L.target_session_id);return{...L,source_title:F?.title??L.source_session_id.slice(0,8),source_project:F?.project??null,target_title:B?.title??L.target_session_id.slice(0,8),target_project:B?.project??null}});return i.json({suggestions:C})}catch(x){return i.json({error:x.message},400)}}),t.get("/api/output-index/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=$e(l);return p?i.json(p):i.json({error:`no output index for session ${l}`},404)});let u=new Set(["pagerank","embedding-rerank","hybrid"]);t.get("/api/neighborhood/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=i.req.query("budget"),g=p!==void 0?Number(p):4e3;if(!Number.isFinite(g)||g<100)return i.json({error:"budget must be a number \u2265 100"},400);let _=i.req.query("scoring")??"hybrid";if(!u.has(_))return i.json({error:`invalid scoring: ${_}; valid: pagerank, embedding-rerank, hybrid`},400);let E=_,y=i.req.query("max_depth"),A=y!==void 0?Number(y):2;if(!Number.isFinite(A)||A<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let k,x=i.req.query("edge_types");if(x){let F=x.split(",").map(B=>B.trim()).filter(Boolean);for(let B of F)if(!o.has(B))return i.json({error:`invalid edge_type: ${B}`},400);k=F}let N=i.req.query("include_wiki_links"),I=N===void 0?!0:!(N==="0"||N==="false"),C=i.req.query("include_suggestions"),L=C==="1"||C==="true";try{let F=en(l,{budget:g,scoring:E,maxDepth:A,edgeTypes:k,includeWikiLinks:I,includeSuggestions:L});return i.json(F)}catch(F){let B=F instanceof Error?F.message:"unknown error",ee=/not found/.test(B)?404:500;return i.json({error:B},ee)}}),t.get("/api/bug-patterns",i=>{let l=i.req.query("min_count"),p=i.req.query("status"),g=i.req.query("project")??void 0,_=i.req.query("limit"),E=i.req.query("offset"),y=l?Number(l):void 0;if(y!==void 0&&(!Number.isFinite(y)||y<1))return i.json({error:"min_count must be a positive integer"},400);let A;if(p==="open")A=!1;else if(p==="resolved")A=!0;else if(p&&p!=="all")return i.json({error:`invalid status: ${p}; valid: open, resolved, all`},400);let k=_?Number(_):void 0;if(k!==void 0&&(!Number.isFinite(k)||k<1))return i.json({error:"invalid limit"},400);let x=E?Number(E):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<0))return i.json({error:"invalid offset"},400);try{let N=Ni({minOccurrenceCount:y,hasResolved:A,project:g,limit:k,offset:x});return i.json(N)}catch(N){return i.json({error:N.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=f(),g=l.prepare(`SELECT p.name AS project,
1671
1807
  COUNT(s.id) AS total_sessions,
1672
1808
  SUM(CASE WHEN oi.session_id IS NOT NULL THEN 1 ELSE 0 END) AS extracted_sessions,
1673
1809
  MAX(oi.extracted_at) AS last_extracted_at
@@ -1675,20 +1811,20 @@ ${o}
1675
1811
  LEFT JOIN sessions s ON s.project_id = p.id
1676
1812
  LEFT JOIN session_output_index oi ON oi.session_id = s.id
1677
1813
  GROUP BY p.id
1678
- 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})),_=m.reduce((T,R)=>(T.total_sessions+=R.total_sessions,T.extracted_sessions+=R.extracted_sessions,T.remaining_sessions+=R.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:m,totals:{..._,cluster_count:E.n}})});let u=j.object({project:j.string().min(1),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:j.number().int().positive().optional(),force:j.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=we(i);if(l)return l;let p=xn();if(p>0&&p<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(p/1024**3).toFixed(2)} GB free \u2014 extract-outputs needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:p},507);let m=await i.req.json().catch(()=>null),_=u.safeParse(m);if(!_.success)return i.json({error:"invalid request body",details:_.error.format()},400);let E={project:_.data.project,model:_.data.model??We,limit:_.data.limit??200,force:_.data.force??!1},T=Cr();if(E.limit>T.sessionCeiling)return ee({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 R=Bt(E.project);if(!R)return i.json({error:`project "${E.project}" not found`},404);let x=rt({projectId:R.id,limit:E.limit,force:E.force}).eligible.length,N=Ir(x),v=Ce(x,E.model),C=at(),O=N.estimated_input_tokens_max+N.estimated_output_tokens_max>C.remaining_tokens_24h;if(ee({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:x}),x===0)return i.json({eligible_session_count:0,...N,plan_window_estimate:v,budget:C,would_exceed_budget:!1,preflight_token:null,expires_at:null,message:"No eligible sessions to extract. Pass force=true to re-extract sessions already at the current extractor version."});let{token:F,expiresAt:U}=xr(E);return i.json({preflight_token:F,expires_at:new Date(U).toISOString(),eligible_session_count:x,...N,plan_window_estimate:v,budget:C,would_exceed_budget:O})});let g=j.object({preflight_token:j.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=we(i);if(l)return l;let p=await i.req.json().catch(()=>null),m=g.safeParse(p);if(!m.success)return i.json({error:"invalid request body"},400);let _=Nr(m.data.preflight_token);if(!_)return ee({kind:"run-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let E=at(),R=(()=>{let v=Bt(_.project);return v?rt({projectId:v.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,A=Ir(R),x=A.estimated_input_tokens_max+A.estimated_output_tokens_max;if(x>E.remaining_tokens_24h)return ee({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${x} exceeds remaining 24h budget ${E.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:E,projected_spend:x},429);let N=_l({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(!Mr(l))return i.json({error:"job not found"},404);let m=Number(i.req.header("Last-Event-ID")??0);return Me(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of fl(l,m))if(E||(await _.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=Mr(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=we(i);return l||(hl(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/extract-outputs/budget",i=>i.json(at()));let h=j.object({scope:j.enum(["cluster","project"]),target_id:j.string().min(1),mode:j.enum(["synopsis","priorities","root_cause"]),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let U=xn();if(U>0&&U<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(U/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:U},507)}let l=we(i);if(l)return l;let p=await i.req.json().catch(()=>null),m=h.safeParse(p);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);let _={scope:m.data.scope,target_id:m.data.target_id,mode:m.data.mode,model:m.data.model??El},E=Js(_);if(!E)return ee({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=vr({scope:_.scope,mode:_.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),R=T.estimated_input_tokens_max+T.estimated_output_tokens_max,A=gl(R),x=Ce(A,_.model),N=at(),C=T.estimated_input_tokens_max+T.estimated_output_tokens_max>N.remaining_tokens_24h;ee({kind:"synth-preflight",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:`${_.scope}/${_.mode}/${_.target_id}`});let{token:O,expiresAt:F}=xr({project:_.target_id,model:_.model,limit:1,force:!1});return fn.set(O,_),setTimeout(()=>fn.delete(O),9e4).unref?.(),i.json({preflight_token:O,expires_at:new Date(F).toISOString(),estimated_input_tokens_max:T.estimated_input_tokens_max,estimated_output_tokens_max:T.estimated_output_tokens_max,plan_window_estimate:x,budget:N,would_exceed_budget:C,context_summary:E.context_summary})});let b=j.object({preflight_token:j.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=we(i);if(l)return l;let p=await i.req.json().catch(()=>null),m=b.safeParse(p);if(!m.success)return i.json({error:"invalid request body"},400);let _=Nr(m.data.preflight_token),E=fn.get(m.data.preflight_token)??null;if(fn.delete(m.data.preflight_token),!E||!_)return ee({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=at(),R=Js(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 A=vr({scope:E.scope,mode:E.mode,member_session_count:R.context_summary.session_count,cluster_count:R.context_summary.cluster_count}),x=A.estimated_input_tokens_max+A.estimated_output_tokens_max;if(x>T.remaining_tokens_24h)return ee({kind:"synth-rejected",job_id:null,project:E.scope==="project"?E.target_id:null,model:E.model,limit:null,origin:i.req.header("origin")??null,reason:`projected spend ${x} exceeds remaining 24h budget ${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:x},429);let N=yl({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(!Dr(l))return i.json({error:"job not found"},404);let m=Number(i.req.header("Last-Event-ID")??0);return Me(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of wl(l,m))if(E||(await _.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=Dr(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=we(i);return l||(Rl(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/bug-signatures",i=>{let l=i.req.query("project")??null,p=Math.min(Math.max(1,Number(i.req.query("limit")??100)),500),m=f(),_=["oi.bug_signatures IS NOT NULL"],E=[];l&&(_.push("p.name = ?"),E.push(l));let R=m.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1814
+ ORDER BY total_sessions DESC`).all().map(y=>({project:y.project,total_sessions:y.total_sessions??0,extracted_sessions:y.extracted_sessions??0,remaining_sessions:(y.total_sessions??0)-(y.extracted_sessions??0),last_extracted_at:y.last_extracted_at})),_=g.reduce((y,A)=>(y.total_sessions+=A.total_sessions,y.extracted_sessions+=A.extracted_sessions,y.remaining_sessions+=A.remaining_sessions,y),{total_sessions:0,extracted_sessions:0,remaining_sessions:0}),E=l.prepare("SELECT COUNT(*) AS n FROM bug_pattern_clusters").get();return i.json({projects:g,totals:{..._,cluster_count:E.n}})});let d=j.object({project:j.string().min(1),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:j.number().int().positive().optional(),force:j.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=xe(i);if(l)return l;let p=Ar();if(p>0&&p<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(p/1024**3).toFixed(2)} GB free \u2014 extract-outputs needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:p},507);let g=await i.req.json().catch(()=>null),_=d.safeParse(g);if(!_.success)return i.json({error:"invalid request body",details:_.error.format()},400);let E={project:_.data.project,model:_.data.model??qe,limit:_.data.limit??200,force:_.data.force??!1},y=po();if(E.limit>y.sessionCeiling)return ne({kind:"run-rejected",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,reason:`limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}`}),i.json({error:`requested limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}. Lower the limit or edit launcher.sessionCeiling in ~/.recall/config.json.`},400);let A=Kt(E.project);if(!A)return i.json({error:`project "${E.project}" not found`},404);let x=tt({projectId:A.id,limit:E.limit,force:E.force}).eligible.length,N=mo(x),I=Me(x,E.model),C=mt(),L=N.estimated_input_tokens_max+N.estimated_output_tokens_max>C.remaining_tokens_24h;if(ne({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:x}),x===0)return i.json({eligible_session_count:0,...N,plan_window_estimate:I,budget:C,would_exceed_budget:!1,preflight_token:null,expires_at:null,message:"No eligible sessions to extract. Pass force=true to re-extract sessions already at the current extractor version."});let{token:F,expiresAt:B}=ao(E);return i.json({preflight_token:F,expires_at:new Date(B).toISOString(),eligible_session_count:x,...N,plan_window_estimate:I,budget:C,would_exceed_budget:L})});let m=j.object({preflight_token:j.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=xe(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=m.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);let _=co(g.data.preflight_token);if(!_)return ne({kind:"run-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let E=mt(),A=(()=>{let I=Kt(_.project);return I?tt({projectId:I.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,k=mo(A),x=k.estimated_input_tokens_max+k.estimated_output_tokens_max;if(x>E.remaining_tokens_24h)return ne({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${x} exceeds remaining 24h budget ${E.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:E,projected_spend:x},429);let N=Su({project:_.project,model:_.model,limit:_.limit,force:_.force,origin:i.req.header("origin")??null});return"error"in N?i.json({error:N.error},400):i.json({jobId:N.jobId,reused:N.reused},N.reused?409:200)}),t.get("/api/extract-outputs/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!fo(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ue(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let A of Tu(l,g))if(E||(await _.writeSSE({id:String(A.id),event:A.kind,data:JSON.stringify(A.data)}),A.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=fo(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/extract-outputs/jobs/:jobId",i=>{let l=xe(i);return l||(yu(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/extract-outputs/budget",i=>i.json(mt()));let h=j.object({scope:j.enum(["cluster","project"]),target_id:j.string().min(1),mode:j.enum(["synopsis","priorities","root_cause"]),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let B=Ar();if(B>0&&B<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(B/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:B},507)}let l=xe(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=h.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _={scope:g.data.scope,target_id:g.data.target_id,mode:g.data.mode,model:g.data.model??wu},E=_n(_);if(!E)return ne({kind:"synth-rejected",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:"target not found"}),i.json({error:_.scope==="cluster"?`cluster "${_.target_id}" not found in any extracted findings`:`project "${_.target_id}" has no extracted findings to synthesize`},404);let y=go({scope:_.scope,mode:_.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),A=y.estimated_input_tokens_max+y.estimated_output_tokens_max,k=bu(A),x=Me(k,_.model),N=mt(),C=y.estimated_input_tokens_max+y.estimated_output_tokens_max>N.remaining_tokens_24h;ne({kind:"synth-preflight",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:`${_.scope}/${_.mode}/${_.target_id}`});let{token:L,expiresAt:F}=ao({project:_.target_id,model:_.model,limit:1,force:!1});return In.set(L,_),setTimeout(()=>In.delete(L),9e4).unref?.(),i.json({preflight_token:L,expires_at:new Date(F).toISOString(),estimated_input_tokens_max:y.estimated_input_tokens_max,estimated_output_tokens_max:y.estimated_output_tokens_max,plan_window_estimate:x,budget:N,would_exceed_budget:C,context_summary:E.context_summary})});let b=j.object({preflight_token:j.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=xe(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=b.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);let _=co(g.data.preflight_token),E=In.get(g.data.preflight_token)??null;if(In.delete(g.data.preflight_token),!E||!_)return ne({kind:"synth-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let y=mt(),A=_n(E);if(!A)return i.json({error:E.scope==="cluster"?`cluster "${E.target_id}" no longer exists`:`project "${E.target_id}" has no findings`},404);let k=go({scope:E.scope,mode:E.mode,member_session_count:A.context_summary.session_count,cluster_count:A.context_summary.cluster_count}),x=k.estimated_input_tokens_max+k.estimated_output_tokens_max;if(x>y.remaining_tokens_24h)return ne({kind:"synth-rejected",job_id:null,project:E.scope==="project"?E.target_id:null,model:E.model,limit:null,origin:i.req.header("origin")??null,reason:`projected spend ${x} exceeds remaining 24h budget ${y.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:y,projected_spend:x},429);let N=xu({intent:E,origin:i.req.header("origin")??null});return"error"in N?i.json({error:N.error},400):i.json({jobId:N.jobId,reused:N.reused},N.reused?409:200)}),t.get("/api/bug-patterns/synthesize/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!ho(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ue(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let A of Nu(l,g))if(E||(await _.writeSSE({id:String(A.id),event:A.kind,data:JSON.stringify(A.data)}),A.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=ho(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=xe(i);return l||(Ou(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/bug-signatures",i=>{let l=i.req.query("project")??null,p=Math.min(Math.max(1,Number(i.req.query("limit")??100)),500),g=f(),_=["oi.bug_signatures IS NOT NULL"],E=[];l&&(_.push("p.name = ?"),E.push(l));let A=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1679
1815
  s.started_at, oi.extracted_at, oi.bug_signatures
1680
1816
  FROM session_output_index oi
1681
1817
  JOIN sessions s ON s.id = oi.session_id
1682
1818
  JOIN projects p ON p.id = s.project_id
1683
1819
  WHERE ${_.join(" AND ")}
1684
1820
  ORDER BY oi.extracted_at DESC
1685
- LIMIT ?`).all(...E,p).map(C=>{let O=[];try{let F=JSON.parse(C.bug_signatures);Array.isArray(F)&&(O=F)}catch{O=[]}return{session_id:C.session_id,project:C.project,auto_title:C.auto_title,started_at:C.started_at,extracted_at:C.extracted_at,rawSignatures:O}}),A=R.flatMap(C=>C.rawSignatures.map(O=>O.message_hash).filter(O=>typeof O=="string")),x=yr(A),N=R.map(C=>({session_id:C.session_id,project:C.project,auto_title:C.auto_title,started_at:C.started_at,extracted_at:C.extracted_at,signatures:C.rawSignatures.map(O=>{let F=O.message_hash?x.get(O.message_hash)??null:null;return{...O,resolved:wr(F),resolution:F}}),signature_count:C.rawSignatures.length})),v=N.reduce((C,O)=>(C.sessions_total+=1,O.signature_count>0?(C.sessions_with_findings+=1,C.total_findings+=O.signature_count):C.sessions_empty+=1,C),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:N,totals:v})}),t.post("/api/bug-signatures/:hash/resolve",async i=>{let l=i.req.param("hash");if(!l||l.length<4)return i.json({error:"invalid message hash"},400);let p=await i.req.json().catch(()=>({})),m=Gc({messageHash:l,resolvedInSessionId:p.resolved_in_session_id??null,fixSummary:p.fix_summary??null});return i.json({resolution:m})}),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):(Yc(l),i.json({ok:!0}))}),t.get("/api/bug-patterns/graph",i=>{let l=i.req.query("project")??null,p=i.req.query("include_resolved")!=="0",m=f(),_=["oi.bug_signatures IS NOT NULL","oi.bug_signatures != '[]'"],E=[];l&&(_.push("p.name = ?"),E.push(l));let T=m.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1821
+ LIMIT ?`).all(...E,p).map(C=>{let L=[];try{let F=JSON.parse(C.bug_signatures);Array.isArray(F)&&(L=F)}catch{L=[]}return{session_id:C.session_id,project:C.project,auto_title:C.auto_title,started_at:C.started_at,extracted_at:C.extracted_at,rawSignatures:L}}),k=A.flatMap(C=>C.rawSignatures.map(L=>L.message_hash).filter(L=>typeof L=="string")),x=so(k),N=A.map(C=>({session_id:C.session_id,project:C.project,auto_title:C.auto_title,started_at:C.started_at,extracted_at:C.extracted_at,signatures:C.rawSignatures.map(L=>{let F=L.message_hash?x.get(L.message_hash)??null:null;return{...L,resolved:no(F),resolution:F}}),signature_count:C.rawSignatures.length})),I=N.reduce((C,L)=>(C.sessions_total+=1,L.signature_count>0?(C.sessions_with_findings+=1,C.total_findings+=L.signature_count):C.sessions_empty+=1,C),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:N,totals:I})}),t.post("/api/bug-signatures/:hash/resolve",async i=>{let l=i.req.param("hash");if(!l||l.length<4)return i.json({error:"invalid message hash"},400);let p=await i.req.json().catch(()=>({})),g=Zl({messageHash:l,resolvedInSessionId:p.resolved_in_session_id??null,fixSummary:p.fix_summary??null});return i.json({resolution:g})}),t.post("/api/bug-signatures/:hash/unresolve",i=>{let l=i.req.param("hash");return!l||l.length<4?i.json({error:"invalid message hash"},400):(Ql(l),i.json({ok:!0}))}),t.get("/api/bug-patterns/graph",i=>{let l=i.req.query("project")??null,p=i.req.query("include_resolved")!=="0",g=f(),_=["oi.bug_signatures IS NOT NULL","oi.bug_signatures != '[]'"],E=[];l&&(_.push("p.name = ?"),E.push(l));let y=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1686
1822
  s.started_at, oi.extracted_at, oi.bug_signatures
1687
1823
  FROM session_output_index oi
1688
1824
  JOIN sessions s ON s.id = oi.session_id
1689
1825
  JOIN projects p ON p.id = s.project_id
1690
1826
  WHERE ${_.join(" AND ")}
1691
- ORDER BY oi.extracted_at DESC`).all(...E),R=[];for(let W of T){let G=[];try{let K=JSON.parse(W.bug_signatures);Array.isArray(K)&&(G=K)}catch{continue}for(let K of G)R.push({sig:K,session_id:W.session_id,project:W.project,auto_title:W.auto_title})}let A=new Map;for(let W of R){let G=W.sig.message_hash??`nohash:${(W.sig.snippet??"").slice(0,64)}`,K=A.get(G);K?K.push(W):A.set(G,[W])}let x=Array.from(A.keys()).filter(W=>!W.startsWith("nohash:")),N=yr(x),v=[],C=new Map,O=[];for(let[W,G]of A){let K=G[0],P=K.sig.message_hash??null,q=P?N.get(P)??null:null,pe=wr(q);if(!p&&pe)continue;let Ee=Array.from(new Set(G.map(z=>z.project))).sort(),bt=Array.from(new Set(G.map(z=>z.session_id))),ts={id:P??W,message_hash:P,error_type:K.sig.error_type??null,snippet:(K.sig.snippet??"").slice(0,200),file:K.sig.file??null,occurrence_count:G.length,projects:Ee,resolved:pe,fix_summary:q?.fix_summary??null,member_session_ids:bt};v.push(ts);for(let z of G)C.has(z.session_id)||C.set(z.session_id,{session_id:z.session_id,project:z.project,auto_title:z.auto_title}),O.push({cluster_id:ts.id,session_id:z.session_id})}let F=[],U=4,Q=new Map;function V(W){let G=Q.get(W)??0;return G>=U?!1:(Q.set(W,G+1),!0)}for(let W=0;W<v.length;W+=1)for(let G=W+1;G<v.length;G+=1){let K=v[W],P=v[G],q=null;K.error_type&&K.error_type!=="unknown"&&K.error_type===P.error_type?q="same_error_type":K.file&&P.file&&K.file===P.file&&(q="same_file"),q&&(!V(K.id)||!V(P.id)||F.push({a:K.id,b:P.id,reason:q}))}return i.json({clusters:v,sessions:Array.from(C.values()),member_edges:O,related_edges:F,totals:{cluster_count:v.length,singleton_count:v.filter(W=>W.occurrence_count===1).length,recurring_count:v.filter(W=>W.occurrence_count>1).length,session_count:C.size,resolved_count:v.filter(W=>W.resolved).length}})}),t.get("/api/bug-patterns/:clusterId",i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=qa(l);return p?i.json(p):i.json({error:`cluster ${l} not found`},404)}),t.post("/api/bug-patterns/:clusterId/resolve",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let m=p.resolved_in_session_id,_=p.fix_summary;if(typeof m!="string"||m.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=Ja(l,m,_);return i.json(E)}catch(E){let T=E instanceof Error?E.message:"unknown error",R=/not found/.test(T)?404:(/not a member/.test(T),400);return i.json({error:T},R)}}),t.post("/api/bug-patterns/:clusterId/split",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let m=p.member_session_ids;if(!Array.isArray(m)||m.length===0)return i.json({error:"member_session_ids must be a non-empty array of strings"},400);let _=[];for(let E of m){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=Xa(l,_);return i.json(E)}catch(E){let T=E instanceof Error?E.message:"unknown error",R=/not found/.test(T)?404:(/cannot split|none of the supplied/.test(T),400);return i.json({error:T},R)}}),t.post("/api/links",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.source_session_id,m=l.target_session_id,_=l.link_type;if(typeof p!="string"||p.length===0)return i.json({error:"source_session_id required"},400);if(typeof m!="string"||m.length===0)return i.json({error:"target_session_id required"},400);if(typeof _!="string")return i.json({error:"link_type required"},400);if(!o.has(_))return i.json({error:`invalid link_type: ${_}`},400);if(_!=="wiki_link")return i.json({error:`link_type '${_}' is not user-writable; only wiki_link is exposed via this endpoint. Other types must go through the suggestions-queue review flow.`},403);if(p===m)return i.json({error:"a session cannot link to itself"},400);let E=f();if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(p))return i.json({error:`source session not found: ${p}`},404);if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(m))return i.json({error:`target session not found: ${m}`},404);let A=As({sourceSessionId:m,targetSessionId:p,linkType:"wiki_link"});if(A.length>0)return i.json({link:A[0]});try{let x=ya({source_session_id:p,target_session_id:m,link_type:"wiki_link",confidence:1,source:"manual",evidence:l.evidence??{created_via:"context_menu"},approved:!0});return i.json({link:x})}catch(x){return i.json({error:x.message},400)}}),t.delete("/api/links/:id",i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let m=wa(p);return m.removed===0?i.json({error:`link ${p} not found`},404):i.json(m)}),t.get("/api/sessions/:id/links",i=>{let l=i.req.param("id");if(!l)return i.json({error:"sessionId required"},400);let p=f();if(!p.prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:`session not found: ${l}`},404);let _=i.req.query("type")??"wiki_link";if(!o.has(_))return i.json({error:`invalid type: ${_}`},400);let E=_,T=Lt(l).filter(C=>C.link_type===E),R=ka(l,T);if(R.length===0)return i.json({links:[]});let A=R.map(C=>C.otherSessionId),x=A.map(()=>"?").join(","),N=p.prepare(`SELECT s.id,
1827
+ ORDER BY oi.extracted_at DESC`).all(...E),A=[];for(let q of y){let G=[];try{let K=JSON.parse(q.bug_signatures);Array.isArray(K)&&(G=K)}catch{continue}for(let K of G)A.push({sig:K,session_id:q.session_id,project:q.project,auto_title:q.auto_title})}let k=new Map;for(let q of A){let G=q.sig.message_hash??`nohash:${(q.sig.snippet??"").slice(0,64)}`,K=k.get(G);K?K.push(q):k.set(G,[q])}let x=Array.from(k.keys()).filter(q=>!q.startsWith("nohash:")),N=so(x),I=[],C=new Map,L=[];for(let[q,G]of k){let K=G[0],P=K.sig.message_hash??null,X=P?N.get(P)??null:null,fe=no(X);if(!p&&fe)continue;let we=Array.from(new Set(G.map(V=>V.project))).sort(),At=Array.from(new Set(G.map(V=>V.session_id))),us={id:P??q,message_hash:P,error_type:K.sig.error_type??null,snippet:(K.sig.snippet??"").slice(0,200),file:K.sig.file??null,occurrence_count:G.length,projects:we,resolved:fe,fix_summary:X?.fix_summary??null,member_session_ids:At};I.push(us);for(let V of G)C.has(V.session_id)||C.set(V.session_id,{session_id:V.session_id,project:V.project,auto_title:V.auto_title}),L.push({cluster_id:us.id,session_id:V.session_id})}let F=[],B=4,ee=new Map;function Z(q){let G=ee.get(q)??0;return G>=B?!1:(ee.set(q,G+1),!0)}for(let q=0;q<I.length;q+=1)for(let G=q+1;G<I.length;G+=1){let K=I[q],P=I[G],X=null;K.error_type&&K.error_type!=="unknown"&&K.error_type===P.error_type?X="same_error_type":K.file&&P.file&&K.file===P.file&&(X="same_file"),X&&(!Z(K.id)||!Z(P.id)||F.push({a:K.id,b:P.id,reason:X}))}return i.json({clusters:I,sessions:Array.from(C.values()),member_edges:L,related_edges:F,totals:{cluster_count:I.length,singleton_count:I.filter(q=>q.occurrence_count===1).length,recurring_count:I.filter(q=>q.occurrence_count>1).length,session_count:C.size,resolved_count:I.filter(q=>q.resolved).length}})}),t.get("/api/bug-patterns/:clusterId",i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=Oi(l);return p?i.json(p):i.json({error:`cluster ${l} not found`},404)}),t.post("/api/bug-patterns/:clusterId/resolve",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let g=p.resolved_in_session_id,_=p.fix_summary;if(typeof g!="string"||g.length===0)return i.json({error:"resolved_in_session_id required"},400);if(typeof _!="string"||_.trim().length===0)return i.json({error:"fix_summary required"},400);try{let E=Li(l,g,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",A=/not found/.test(y)?404:(/not a member/.test(y),400);return i.json({error:y},A)}}),t.post("/api/bug-patterns/:clusterId/split",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let g=p.member_session_ids;if(!Array.isArray(g)||g.length===0)return i.json({error:"member_session_ids must be a non-empty array of strings"},400);let _=[];for(let E of g){if(typeof E!="string"||E.length===0)return i.json({error:"member_session_ids must contain only non-empty strings"},400);_.push(E)}try{let E=Ci(l,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",A=/not found/.test(y)?404:(/cannot split|none of the supplied/.test(y),400);return i.json({error:y},A)}}),t.post("/api/links",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.source_session_id,g=l.target_session_id,_=l.link_type;if(typeof p!="string"||p.length===0)return i.json({error:"source_session_id required"},400);if(typeof g!="string"||g.length===0)return i.json({error:"target_session_id required"},400);if(typeof _!="string")return i.json({error:"link_type required"},400);if(!o.has(_))return i.json({error:`invalid link_type: ${_}`},400);if(_!=="wiki_link")return i.json({error:`link_type '${_}' is not user-writable; only wiki_link is exposed via this endpoint. Other types must go through the suggestions-queue review flow.`},403);if(p===g)return i.json({error:"a session cannot link to itself"},400);let E=f();if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(p))return i.json({error:`source session not found: ${p}`},404);if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(g))return i.json({error:`target session not found: ${g}`},404);let k=hs({sourceSessionId:g,targetSessionId:p,linkType:"wiki_link"});if(k.length>0)return i.json({link:k[0]});try{let x=Gi({source_session_id:p,target_session_id:g,link_type:"wiki_link",confidence:1,source:"manual",evidence:l.evidence??{created_via:"context_menu"},approved:!0});return i.json({link:x})}catch(x){return i.json({error:x.message},400)}}),t.delete("/api/links/:id",i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let g=zi(p);return g.removed===0?i.json({error:`link ${p} not found`},404):i.json(g)}),t.get("/api/sessions/:id/links",i=>{let l=i.req.param("id");if(!l)return i.json({error:"sessionId required"},400);let p=f();if(!p.prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:`session not found: ${l}`},404);let _=i.req.query("type")??"wiki_link";if(!o.has(_))return i.json({error:`invalid type: ${_}`},400);let E=_,y=vt(l).filter(C=>C.link_type===E),A=Kc(l,y);if(A.length===0)return i.json({links:[]});let k=A.map(C=>C.otherSessionId),x=k.map(()=>"?").join(","),N=p.prepare(`SELECT s.id,
1692
1828
  NULLIF(sa.alias, '') AS alias,
1693
1829
  s.auto_title,
1694
1830
  s.first_user_message,
@@ -1696,7 +1832,7 @@ ${o}
1696
1832
  FROM sessions s
1697
1833
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1698
1834
  LEFT JOIN projects p ON p.id = s.project_id
1699
- WHERE s.id IN (${x})`).all(...A),v=new Map(N.map(C=>[C.id,C]));return i.json({links:R.map(C=>{let O=v.get(C.otherSessionId),F=O?.alias?.trim()||O?.auto_title?.trim()||(O?.first_user_message?O.first_user_message.slice(0,80):"")||C.otherSessionId.slice(0,8);return{linkId:C.linkId,otherSessionId:C.otherSessionId,direction:C.direction,updatedAt:C.updatedAt,title:F,project:O?.project??null}})})}),t.patch("/api/links/suggestions/:id",async i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let m=await i.req.json().catch(()=>null);if(!m||typeof m.status!="string")return i.json({error:"status required (approved|rejected)"},400);if(m.status!=="approved"&&m.status!=="rejected")return i.json({error:`invalid status: ${m.status}`},400);try{let _=er(p,m.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 p=Array.isArray(l.ids)?l.ids:null;if(!p||p.length===0)return i.json({error:"ids must be a non-empty array"},400);if(p.length>1e3)return i.json({error:"bulk-decide capped at 1000 ids per call"},400);if(l.status!=="approved"&&l.status!=="rejected")return i.json({error:`invalid status: ${l.status}`},400);let m=l.status,_=0,E=0,T=[];for(let R of p){let A=Number(R);if(!Number.isInteger(A)||A<=0){T.push({id:Number.isFinite(Number(R))?Number(R):-1,error:"invalid id"});continue}try{er(A,m),_+=1}catch(x){let N=x.message;/already decided/.test(N)?E+=1:T.push({id:A,error:N})}}return i.json({decided:_,skipped:E,errors:T})}),t.get("/api/sessions",i=>{let l=f(),p=i.req.query("project"),m=i.req.query("since"),_=i.req.query("until"),E=i.req.queries("tag")??[],T=i.req.query("collection"),R=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),A=i.req.query("system")==="1"||i.req.query("system")==="true",x={limit:R},N="s.message_count > 2";if(A||(N+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'"),p&&(N+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",x.proj=`%${Qt(p)}%`),m&&(N+=" AND s.started_at >= @since",x.since=m),_&&(N+=" AND s.started_at <= @until",/^\d{4}-\d{2}-\d{2}$/.test(_)?x.until=`${_}T23:59:59.999Z`:x.until=_),E.length>0&&E.map(F=>De(F)).filter(Boolean).forEach((F,U)=>{N+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${U})`,x[`tag_${U}`]=F}),T){let O=Mn(T);if(O.length===0)return i.json([]);let F=O.map((U,Q)=>`@col_${Q}`).join(",");N+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${F}))`,O.forEach((U,Q)=>{x[`col_${Q}`]=U})}let C=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1835
+ WHERE s.id IN (${x})`).all(...k),I=new Map(N.map(C=>[C.id,C]));return i.json({links:A.map(C=>{let L=I.get(C.otherSessionId),F=L?.alias?.trim()||L?.auto_title?.trim()||(L?.first_user_message?L.first_user_message.slice(0,80):"")||C.otherSessionId.slice(0,8);return{linkId:C.linkId,otherSessionId:C.otherSessionId,direction:C.direction,updatedAt:C.updatedAt,title:F,project:L?.project??null}})})}),t.patch("/api/links/suggestions/:id",async i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let g=await i.req.json().catch(()=>null);if(!g||typeof g.status!="string")return i.json({error:"status required (approved|rejected)"},400);if(g.status!=="approved"&&g.status!=="rejected")return i.json({error:`invalid status: ${g.status}`},400);try{let _=zn(p,g.status);return i.json(_)}catch(_){let E=_.message,y=/already decided/.test(E)?409:/not found/.test(E)?404:400;return i.json({error:E},y)}}),t.post("/api/links/suggestions/bulk-decide",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=Array.isArray(l.ids)?l.ids:null;if(!p||p.length===0)return i.json({error:"ids must be a non-empty array"},400);if(p.length>1e3)return i.json({error:"bulk-decide capped at 1000 ids per call"},400);if(l.status!=="approved"&&l.status!=="rejected")return i.json({error:`invalid status: ${l.status}`},400);let g=l.status,_=0,E=0,y=[];for(let A of p){let k=Number(A);if(!Number.isInteger(k)||k<=0){y.push({id:Number.isFinite(Number(A))?Number(A):-1,error:"invalid id"});continue}try{zn(k,g),_+=1}catch(x){let N=x.message;/already decided/.test(N)?E+=1:y.push({id:k,error:N})}}return i.json({decided:_,skipped:E,errors:y})}),t.get("/api/sessions",i=>{let l=f(),p=i.req.query("project"),g=i.req.query("since"),_=i.req.query("until"),E=i.req.queries("tag")??[],y=i.req.query("collection"),A=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),k=i.req.query("system")==="1"||i.req.query("system")==="true",x={limit:A},N="s.message_count > 2";if(k||(N+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'"),p&&(N+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",x.proj=`%${cs(p)}%`),g&&(N+=" AND s.started_at >= @since",x.since=g),_&&(N+=" AND s.started_at <= @until",/^\d{4}-\d{2}-\d{2}$/.test(_)?x.until=`${_}T23:59:59.999Z`:x.until=_),E.length>0&&E.map(F=>Be(F)).filter(Boolean).forEach((F,B)=>{N+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${B})`,x[`tag_${B}`]=F}),y){let L=Nr(y);if(L.length===0)return i.json([]);let F=L.map((B,ee)=>`@col_${ee}`).join(",");N+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${F}))`,L.forEach((B,ee)=>{x[`col_${ee}`]=B})}let C=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1700
1836
  s.message_count, s.first_user_message, s.git_branch,
1701
1837
  s.auto_title, s.auto_title_source, s.verification_status,
1702
1838
  NULLIF(sa.alias, '') AS alias,
@@ -1716,15 +1852,15 @@ ${o}
1716
1852
  LEFT JOIN session_notes sn ON sn.session_id = s.id
1717
1853
  WHERE ${N}
1718
1854
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1719
- LIMIT @limit`).all(x).map(({tags_csv:O,...F})=>{let U=F.id,Q=I.getOrigin(U),V=F.alias,W=V==null?null:I.isSessionAutoLinked(U)?"auto":"manual",G=jt({auto_title:F.auto_title,auto_title_source:F.auto_title_source??null,has_alias:V!=null&&W==="manual"});return{...F,tags:O?O.split(","):[],origin:Q?{editor:Q.editor,label:Q.label}:null,alias_source:W,title_quality:G}});return i.json(C)}),t.get("/api/sessions/:id",i=>{let l=f(),p=i.req.param("id"),m=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
1855
+ LIMIT @limit`).all(x).map(({tags_csv:L,...F})=>{let B=F.id,ee=v.getOrigin(B),Z=F.alias,q=Z==null?null:v.isSessionAutoLinked(B)?"auto":"manual",G=Xt({auto_title:F.auto_title,auto_title_source:F.auto_title_source??null,has_alias:Z!=null&&q==="manual"});return{...F,tags:L?L.split(","):[],origin:ee?{editor:ee.editor,label:ee.label}:null,alias_source:q,title_quality:G}});return i.json(C)}),t.get("/api/sessions/:id",i=>{let l=f(),p=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
1720
1856
  NULLIF(sa.alias, '') AS alias
1721
1857
  FROM sessions s
1722
1858
  JOIN projects p ON p.id = s.project_id
1723
1859
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1724
- WHERE s.id = ?`).get(p);if(!m)return i.json({error:"not found"},404);let _=wt(p),E=I.getOrigin(p),T=E?{editor:E.editor,label:E.label}:null,R=m.alias==null?null:I.isSessionAutoLinked(p)?"auto":"manual",A=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1860
+ WHERE s.id = ?`).get(p);if(!g)return i.json({error:"not found"},404);let _=Mt(p),E=v.getOrigin(p),y=E?{editor:E.editor,label:E.label}:null,A=g.alias==null?null:v.isSessionAutoLinked(p)?"auto":"manual",k=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1725
1861
  FROM messages
1726
1862
  WHERE session_id = ?
1727
- ORDER BY COALESCE(timestamp, ''), rowid`).all(p);return i.json({session:{...m,tags:_,origin:T,alias_source:R},messages:A})}),t.get("/api/tags",i=>i.json(Xe())),t.get("/api/sessions/:id/tags",i=>i.json({tags:wt(i.req.param("id"))})),t.post("/api/sessions/:id/tags",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.tag!="string")return i.json({error:"tag required"},400);try{let m=Je(l,p.tag);return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.delete("/api/sessions/:id/tags/:tag",i=>{let l=i.req.param("id"),p=i.req.param("tag");return i.json(ri(l,p))}),t.get("/api/config/auto-tag",i=>i.json(gr(Re()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),p=Ps.partial().safeParse(l);if(!p.success)return i.json({error:"invalid config",issues:p.error.issues},400);let m=p.data;m.apiKey===void 0&&delete m.apiKey;let _=Lc(m);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&Qs(),i.json(gr(_))}),t.get("/api/onboarding",i=>{let p=f().prepare(`SELECT s.id,
1863
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(p);return i.json({session:{...g,tags:_,origin:y,alias_source:A},messages:k})}),t.get("/api/tags",i=>i.json(Ze())),t.get("/api/sessions/:id/tags",i=>i.json({tags:Mt(i.req.param("id"))})),t.post("/api/sessions/:id/tags",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.tag!="string")return i.json({error:"tag required"},400);try{let g=Ve(l,p.tag);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/tags/:tag",i=>{let l=i.req.param("id"),p=i.req.param("tag");return i.json(oa(l,p))}),t.get("/api/config/auto-tag",i=>i.json(Qr(Le()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),p=un.partial().safeParse(l);if(!p.success)return i.json({error:"invalid config",issues:p.error.issues},400);let g=p.data;g.apiKey===void 0&&delete g.apiKey;let _=Hl(g);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&wn(),i.json(Qr(_))}),t.get("/api/onboarding",i=>{let p=f().prepare(`SELECT s.id,
1728
1864
  p.name AS project,
1729
1865
  s.started_at,
1730
1866
  s.ended_at,
@@ -1736,7 +1872,7 @@ ${o}
1736
1872
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1737
1873
  WHERE s.message_count > 2
1738
1874
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1739
- LIMIT 1`).get();return i.json({state:tn(),mostRecentSession:p??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),p=en.partial().safeParse(l);return p.success?i.json(ud(p.data)):i.json({error:"invalid onboarding state",issues:p.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(pd())),t.get("/api/config/mcp-install",i=>i.json({...Ie(),claudeCliAvailable:oe()})),t.post("/api/config/mcp-install",i=>i.json({...rd(),claudeCliAvailable:oe()})),t.delete("/api/config/mcp-install",i=>i.json({...od(),claudeCliAvailable:oe()}));let S=j.object({scope:j.object({untaggedOnly:j.boolean().optional(),project:j.string().optional(),collectionId:j.string().optional(),sessionIds:j.array(j.string()).optional(),limit:j.number().int().min(1).max(500).optional()}).default({}),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:j.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(po)return i.json({error:"a scan is already running"},409);if(!oe())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!Ie().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),m=S.safeParse(p);if(!m.success)return i.json({error:"invalid scope",issues:m.error.issues},400);let _=m.data.scope,E=Re(),T=m.data.model??E.model,R=f(),A=()=>R.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,x=A();po=!0;let N;try{let v=m.data.scanId;N=await vn(_,{model:T,scanId:v});let C=A(),O=Math.max(0,C-x);return v&&os(v,{type:"done",result:{success:N.success,exitCode:N.exitCode,tagsAdded:O}}),i.json({success:N.success,exitCode:N.exitCode,tagsAdded:O,model:T,stdout:qe(N.stdout.slice(0,2e3)).redacted,stderrTail:qe(N.stderr.slice(-2e3)).redacted})}finally{po=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return Me(i,async p=>{let m=[],_={resolve:()=>{}},E=new Promise(x=>{_.resolve=x}),T=ai(l,x=>{m.push(x);let N=_.resolve;E=new Promise(v=>{_.resolve=v}),N()}),R=!1,A=setInterval(()=>{R||p.writeSSE({event:"heartbeat",data:""}).catch(()=>{R=!0})},15e3);try{for(;!R;){m.length===0&&await E;let x=m.shift();if(x&&(await p.writeSSE({event:x.type,data:JSON.stringify(x)}),x.type==="done"))break}}finally{R=!0,clearInterval(A),T()}})}),t.get("/api/prompts",i=>i.json({prompts:Ln.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:oe()})),t.post("/api/prompts/run",async i=>{if(!oe())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!Ie().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),_=j.object({name:j.string(),args:j.record(j.string(),j.unknown()).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).safeParse(p);if(!_.success)return i.json({error:"invalid request",issues:_.error.issues},400);let E=ii(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let T=E.build(_.data.args??{}),R=Re(),A=_.data.model??R.model,x=await Fe(T,E.allowedTools,{model:A});return i.json({success:x.success,exitCode:x.exitCode,promptName:E.name,model:A,stdout:x.stdout,stderrTail:x.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(Vt())),t.get("/api/autopilot/events",i=>Me(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(Vt())});let p=[],m=()=>{},_=new Promise(T=>m=T),E=td(T=>{p.push(T);let R=m;_=new Promise(A=>m=A),R()});try{for(;;){if(p.length===0){let R=new Promise(x=>setTimeout(()=>x("tick"),3e4));if(await Promise.race([_.then(()=>"event"),R])==="tick"){await l.writeSSE({event:"heartbeat",data:"1"});continue}}let T=p.shift();T&&await l.writeSSE({event:"state",data:JSON.stringify(T)})}}finally{E()}})),t.post("/api/autopilot/kick",i=>(Qs(),i.json({ok:!0,snapshot:Vt()})));let y=j.object({scope:j.object({untaggedOnly:j.boolean().optional(),project:j.string().optional(),collectionId:j.string().optional(),sessionIds:j.array(j.string()).optional(),limit:j.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=Re();if(!l.enabled)return i.json({error:"auto-tagging is disabled"},403);if(l.backend!=="api")return i.json({error:"api-backend scan requires backend=api in config"},400);if(!l.apiKey)return i.json({error:"no api key configured"},400);let p=await i.req.json().catch(()=>({})),m=y.safeParse(p);if(!m.success)return i.json({error:"invalid scope",issues:m.error.issues},400);let _=Ye(m.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=Yl(_.length);return Ql(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=Ys(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let{controller:p,listeners:m,..._}=l;return i.json(_)}),t.get("/api/tags/scan/:id/events",i=>{let l=Ys(i.req.param("id"));return l?Me(i,async p=>{await p.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let R of l.results)await p.writeSSE({event:"result",data:JSON.stringify(R)});let m=[],_={resolve:()=>{}},E=new Promise(R=>{_.resolve=R}),T=Kl(l,R=>{m.push(R);let A=_.resolve;E=new Promise(x=>{_.resolve=x}),A()});try{for(;l.status==="running"||l.status==="pending";){m.length===0&&await E;let R=m.shift();if(R&&(await p.writeSSE({event:R.type,data:JSON.stringify(R)}),R.type==="done"||R.type==="status"&&(R.status==="cancelled"||R.status==="failed")))break}}finally{T()}}):i.json({error:"scan not found"},404)});let k=j.object({selection:j.array(j.object({sessionId:j.string(),tags:j.array(j.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=Ys(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let p=await i.req.json().catch(()=>({})),m=k.safeParse(p);if(!m.success)return i.json({error:"invalid selection"},400);let _=ed(l,m.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return zl(l),Vl(l),i.json({ok:!0})}),t.put("/api/sessions/:id/alias",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.alias!="string")return i.json({error:"alias required"},400);try{let m=me(l,p.alias);if(p.pin===!0)I.unlinkSession(l);else{let _=f().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=_?.cwd?_.cwd.replace(/\/+$/,""):null,T=!1;if(E&&_?.started_at){let R=Date.parse(_.started_at),A=_.started_at,x=I.all().filter(N=>N.cwd&&N.cwd.replace(/\/+$/,"")===E&&yt({sessionStartedAt:A,terminalOpenedAt:N.opened_at??null}).allowed);if(Number.isFinite(R)&&x.length>0){let v=x.map(C=>({t:C,gap:R-Date.parse(C.opened_at??"")})).filter(C=>Number.isFinite(C.gap)).sort((C,O)=>C.gap-O.gap)[0];v&&(I.linkSession(l,v.t.shell_pid),T=!0)}}T||I.unlinkSession(l)}return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.delete("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return ns(l),I.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:Te(l)})}),t.get("/api/config/auto-title",i=>i.json(He())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),p=$s.partial().safeParse(l);return p.success?i.json(Dc(p.data)):i.json({error:"invalid config",issues:p.error.issues},400)}),t.get("/api/sessions/:id/auto-title",i=>{let l=i.req.param("id"),p=ye(l);return p?i.json(p):i.json({error:"session not found"},404)}),t.post("/api/sessions/:id/auto-title",async i=>{let l=i.req.param("id");if(!He().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!f().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await oc(l);return de(l,E,"agent"),i.json(ye(l))}catch(E){return i.json({error:E.message,code:"agent-title-failed"},500)}}),t.post("/api/sessions/:id/auto-title/revert",i=>{let l=i.req.param("id"),p=ye(l);if(!p)return i.json({error:"session not found"},404);let m=p.auto_title_history;if(!m||m.length===0)return i.json({error:"no prior title to revert to",code:"no-history"},422);let _=m[m.length-1];return de(l,_.title,"agent"),i.json(ye(l))}),t.post("/api/sessions/:id/regenerate-title",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),m=p.model??Bs;try{let _=await fr(l,{model:m,force:p.force===!0,budget:typeof p.budget=="number"?p.budget:void 0,signal:i.req.raw.signal}),E=ye(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 nt)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 p=l.project;if(typeof p!="string"||p.length===0)return i.json({error:"project (string) required"},400);let m=l.quality_filter;if(!Array.isArray(m)||m.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 m){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:Bs,R=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,A=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,N=f().prepare(`SELECT s.id,
1875
+ LIMIT 1`).get();return i.json({state:An(),mostRecentSession:p??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),p=Rn.partial().safeParse(l);return p.success?i.json(fd(p.data)):i.json({error:"invalid onboarding state",issues:p.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(hd())),t.get("/api/config/mcp-install",i=>i.json({...De(),claudeCliAvailable:le()})),t.post("/api/config/mcp-install",i=>i.json({...ld(),claudeCliAvailable:le()})),t.delete("/api/config/mcp-install",i=>i.json({...ud(),claudeCliAvailable:le()}));let T=j.object({scope:j.object({untaggedOnly:j.boolean().optional(),project:j.string().optional(),collectionId:j.string().optional(),sessionIds:j.array(j.string()).optional(),limit:j.number().int().min(1).max(500).optional()}).default({}),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:j.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(qo)return i.json({error:"a scan is already running"},409);if(!le())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!De().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),g=T.safeParse(p);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=g.data.scope,E=Le(),y=g.data.model??E.model,A=f(),k=()=>A.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,x=k();qo=!0;let N;try{let I=g.data.scanId;N=await rr(_,{model:y,scanId:I});let C=k(),L=Math.max(0,C-x);return I&&Ss(I,{type:"done",result:{success:N.success,exitCode:N.exitCode,tagsAdded:L}}),i.json({success:N.success,exitCode:N.exitCode,tagsAdded:L,model:y,stdout:_e(N.stdout.slice(0,2e3)).redacted,stderrTail:_e(N.stderr.slice(-2e3)).redacted})}finally{qo=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return Ue(i,async p=>{let g=[],_={resolve:()=>{}},E=new Promise(x=>{_.resolve=x}),y=ca(l,x=>{g.push(x);let N=_.resolve;E=new Promise(I=>{_.resolve=I}),N()}),A=!1,k=setInterval(()=>{A||p.writeSSE({event:"heartbeat",data:""}).catch(()=>{A=!0})},15e3);try{for(;!A;){g.length===0&&await E;let x=g.shift();if(x&&(await p.writeSSE({event:x.type,data:JSON.stringify(x)}),x.type==="done"))break}}finally{A=!0,clearInterval(k),y()}})}),t.get("/api/prompts",i=>i.json({prompts:tr.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:le()})),t.post("/api/prompts/run",async i=>{if(!le())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!De().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),_=j.object({name:j.string(),args:j.record(j.string(),j.unknown()).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).safeParse(p);if(!_.success)return i.json({error:"invalid request",issues:_.error.issues},400);let E=aa(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let y=E.build(_.data.args??{}),A=Le(),k=_.data.model??A.model,x=await He(y,E.allowedTools,{model:k});return i.json({success:x.success,exitCode:x.exitCode,promptName:E.name,model:k,stdout:x.stdout,stderrTail:x.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(is())),t.get("/api/autopilot/events",i=>Ue(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(is())});let p=[],g=()=>{},_=new Promise(y=>g=y),E=id(y=>{p.push(y);let A=g;_=new Promise(k=>g=k),A()});try{for(;;){if(p.length===0){let A=new Promise(x=>setTimeout(()=>x("tick"),3e4));if(await Promise.race([_.then(()=>"event"),A])==="tick"){await l.writeSSE({event:"heartbeat",data:"1"});continue}}let y=p.shift();y&&await l.writeSSE({event:"state",data:JSON.stringify(y)})}}finally{E()}})),t.post("/api/autopilot/kick",i=>(wn(),i.json({ok:!0,snapshot:is()})));let S=j.object({scope:j.object({untaggedOnly:j.boolean().optional(),project:j.string().optional(),collectionId:j.string().optional(),sessionIds:j.array(j.string()).optional(),limit:j.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=Le();if(!l.enabled)return i.json({error:"auto-tagging is disabled"},403);if(l.backend!=="api")return i.json({error:"api-backend scan requires backend=api in config"},400);if(!l.apiKey)return i.json({error:"no api key configured"},400);let p=await i.req.json().catch(()=>({})),g=S.safeParse(p);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=et(g.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=Qu(_.length);return rd(E,{apiKey:l.apiKey,model:l.model,minTags:l.minTagsPerSession,maxTags:l.maxTagsPerSession,sessions:_}),i.json({scanId:E.id,total:E.total})}),t.get("/api/tags/scan/:id",i=>{let l=En(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let{controller:p,listeners:g,..._}=l;return i.json(_)}),t.get("/api/tags/scan/:id/events",i=>{let l=En(i.req.param("id"));return l?Ue(i,async p=>{await p.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let A of l.results)await p.writeSSE({event:"result",data:JSON.stringify(A)});let g=[],_={resolve:()=>{}},E=new Promise(A=>{_.resolve=A}),y=ed(l,A=>{g.push(A);let k=_.resolve;E=new Promise(x=>{_.resolve=x}),k()});try{for(;l.status==="running"||l.status==="pending";){g.length===0&&await E;let A=g.shift();if(A&&(await p.writeSSE({event:A.type,data:JSON.stringify(A)}),A.type==="done"||A.type==="status"&&(A.status==="cancelled"||A.status==="failed")))break}}finally{y()}}):i.json({error:"scan not found"},404)});let w=j.object({selection:j.array(j.object({sessionId:j.string(),tags:j.array(j.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=En(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let p=await i.req.json().catch(()=>({})),g=w.safeParse(p);if(!g.success)return i.json({error:"invalid selection"},400);let _=od(l,g.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return td(l),sd(l),i.json({ok:!0})}),t.put("/api/sessions/:id/alias",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.alias!="string")return i.json({error:"alias required"},400);try{let g=be(l,p.alias);if(p.pin===!0)v.unlinkSession(l);else{let _=f().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=_?.cwd?_.cwd.replace(/\/+$/,""):null,y=!1;if(E&&_?.started_at){let A=Date.parse(_.started_at),k=_.started_at,x=v.all().filter(N=>N.cwd&&N.cwd.replace(/\/+$/,"")===E&&$t({sessionStartedAt:k,terminalOpenedAt:N.opened_at??null}).allowed);if(Number.isFinite(A)&&x.length>0){let I=x.map(C=>({t:C,gap:A-Date.parse(C.opened_at??"")})).filter(C=>Number.isFinite(C.gap)).sort((C,L)=>C.gap-L.gap)[0];I&&(v.linkSession(l,I.t.shell_pid),y=!0)}}y||v.unlinkSession(l)}return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return Is(l),v.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:Ae(l)})}),t.get("/api/config/auto-title",i=>i.json(Ge())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),p=pn.partial().safeParse(l);return p.success?i.json(Gl(p.data)):i.json({error:"invalid config",issues:p.error.issues},400)}),t.get("/api/sessions/:id/auto-title",i=>{let l=i.req.param("id"),p=ke(l);return p?i.json(p):i.json({error:"session not found"},404)}),t.post("/api/sessions/:id/auto-title",async i=>{let l=i.req.param("id");if(!Ge().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!f().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await _l(l);return me(l,E,"agent"),i.json(ke(l))}catch(E){return i.json({error:E.message,code:"agent-title-failed"},500)}}),t.post("/api/sessions/:id/auto-title/revert",i=>{let l=i.req.param("id"),p=ke(l);if(!p)return i.json({error:"session not found"},404);let g=p.auto_title_history;if(!g||g.length===0)return i.json({error:"no prior title to revert to",code:"no-history"},422);let _=g[g.length-1];return me(l,_.title,"agent"),i.json(ke(l))}),t.post("/api/sessions/:id/regenerate-title",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),g=p.model??mn;try{let _=await to(l,{model:g,force:p.force===!0,budget:typeof p.budget=="number"?p.budget:void 0,signal:i.req.raw.signal}),E=ke(l),y=E?.auto_title_history&&E.auto_title_history.length>0?E.auto_title_history[E.auto_title_history.length-1].title:null;return i.json({..._,previous_title:y})}catch(_){if(_ instanceof dt)return i.json({error:_.message,code:"no-context-available",session_id:_.sessionId},422);let E=_ instanceof Error?_.message:"unknown error",y=/not found|unknown/i.test(E)?404:500;return i.json({error:E,code:"regenerate-failed"},y)}}),t.post("/api/sessions/regenerate-titles-batch",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.project;if(typeof p!="string"||p.length===0)return i.json({error:"project (string) required"},400);let g=l.quality_filter;if(!Array.isArray(g)||g.length===0)return i.json({error:"quality_filter (non-empty array) required"},400);let _=new Set(["low_signal","recursive_meta","programmatic"]),E=[];for(let L of g){if(typeof L!="string")return i.json({error:`invalid quality_filter entry: ${L}`},400);if(!_.has(L))return i.json({error:`quality_filter must be a subset of ${[..._].join(",")}; got ${L}`},400);E.push(L)}let y=typeof l.model=="string"&&l.model.length>0?l.model:mn,A=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,k=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,N=f().prepare(`SELECT s.id,
1740
1876
  s.auto_title,
1741
1877
  s.auto_title_source,
1742
1878
  NULLIF(sa.alias, '') AS alias
@@ -1745,7 +1881,7 @@ ${o}
1745
1881
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1746
1882
  WHERE p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\'
1747
1883
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1748
- LIMIT @limit`).all({proj:`%${Qt(p)}%`,limit:R}),v=new Set(E),C=N.filter(O=>{let F=O.alias==null?null:I.isSessionAutoLinked(O.id)?"auto":"manual",U=jt({auto_title:O.auto_title,auto_title_source:O.auto_title_source??null,has_alias:O.alias!=null&&F==="manual"});return v.has(U)});return Me(i,async O=>{let F=C.length,U=[],Q=[],V=[],W=0,G=async(P,q)=>{W+=1;try{await O.writeSSE({id:String(W),event:P,data:JSON.stringify(q)})}catch{}};await G("start",{total:F,model:T});let K=0;for(let P of C){if(i.req.raw.signal.aborted)break;K+=1;try{let q=await fr(P.id,{model:T,budget:A,signal:i.req.raw.signal});q.written?(U.push(P.id),await G("progress",{sessionId:P.id,title:q.title,evidence:q.evidence,confidence:q.confidence,current:K,total:F})):(Q.push({sessionId:P.id,reason:q.skipped??"unknown"}),await G("skipped",{sessionId:P.id,reason:q.skipped??"unknown",current:K,total:F}))}catch(q){let pe=q instanceof Error?q.message:String(q),Ee=q instanceof nt?"no-context-available":"failed";V.push({sessionId:P.id,error:pe}),await G("error",{sessionId:P.id,error:pe,code:Ee,current:K,total:F})}}await G("done",{generated:U,skipped:Q,failed:V,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),p=is(l);return p?i.json(p):i.body(null,204)}),t.put("/api/sessions/:id/notes",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.content!="string")return i.json({error:"content required (string)"},400);try{let m=gi(l,p.content);return i.json(m)}catch(m){return console.error("[notes] failed to save note for session",l,m),i.json({error:"failed to save note"},500)}}),t.post("/api/sessions/:id/generate-note",async i=>{let l=i.req.param("id");if(!He().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let m=await _i(l),_=fi(l,m);return i.json(_)}catch(m){let _=m.message,E=/no messages available/i.test(_)?404:500;return i.json({error:_},E)}}),t.get("/api/semantic/status",i=>i.json(Yr())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),p=nn.partial().safeParse(l);return p.success?(rn(p.data),i.json(Yr())):i.json({error:"invalid semantic config",issues:p.error.issues},400)}),t.get("/api/semantic/config",i=>i.json(ce())),t.post("/api/semantic/backfill",async i=>{if(go)return i.json({error:"a scan is already running"},409);if(!ce().enabled)return i.json({error:"semantic search is disabled"},400);let p=await i.req.json().catch(()=>({})),m=Math.max(1,Math.min(5e3,Number(p.limit??200)));return go=!0,cn({limit:m,force:!!p.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{go=!1}),i.json({ok:!0,limit:m})}),t.post("/api/semantic/sessions/:id",async i=>{if(!ce().enabled)return i.json({error:"semantic search is disabled"},400);let p=i.req.param("id"),m=await an(p);return i.json(m)}),t.get("/api/semantic/vector-status",i=>{let l=ke(),p=Kd(),m=io();return i.json({embedder:l,worker:p,modelInstalled:m})}),t.post("/api/semantic/install",es,async i=>{if(io())return i.json({ok:!0,status:"already_installed"});if(mo)return i.json({error:"a scan is already running"},409);mo=!0;try{return await tu(),await Ws(),Yd(),i.json({ok:!0,status:"installed"})}catch(l){let p=l instanceof Error?l.message:"unknown error";return i.json({ok:!1,error:p},500)}finally{mo=!1}}),t.get("/api/sessions/:id/similar",es,async i=>{if(!ke().loaded)return i.json({error:"vector model not loaded"},503);let l=i.req.param("id"),p=Math.max(1,Math.min(50,Number(i.req.query("limit")??10)));try{let m=await Bd(l,p);return i.json({sessionId:l,similar:m})}catch(m){let _=m instanceof Error?m.message:"unknown error";return i.json({error:_},500)}}),t.get("/api/search",es,async i=>{let l=f(),p=i.req.query("q")?.trim();if(!p)return i.json({query:"",hits:[],tags:[]});if(p.length>500)return i.json({error:"query too long (max 500 chars)"},400);let m=i.req.query("project"),_=p.split(/\s+/).filter(P=>P.length>0),E=_.filter(P=>P.startsWith("#")).map(P=>De(P)).filter(Boolean),T=_.filter(P=>!P.startsWith("#")),R=T.length>20,x=(R?T.slice(0,20):T).map(P=>`"${P.replace(/"/g,"")}"`),N=x.join(" "),v=Math.max(1,Math.min(200,Number(i.req.query("limit")??30)));if(x.length===0&&E.length>0){let P=`
1884
+ LIMIT @limit`).all({proj:`%${cs(p)}%`,limit:A}),I=new Set(E),C=N.filter(L=>{let F=L.alias==null?null:v.isSessionAutoLinked(L.id)?"auto":"manual",B=Xt({auto_title:L.auto_title,auto_title_source:L.auto_title_source??null,has_alias:L.alias!=null&&F==="manual"});return I.has(B)});return Ue(i,async L=>{let F=C.length,B=[],ee=[],Z=[],q=0,G=async(P,X)=>{q+=1;try{await L.writeSSE({id:String(q),event:P,data:JSON.stringify(X)})}catch{}};await G("start",{total:F,model:y});let K=0;for(let P of C){if(i.req.raw.signal.aborted)break;K+=1;try{let X=await to(P.id,{model:y,budget:k,signal:i.req.raw.signal});X.written?(B.push(P.id),await G("progress",{sessionId:P.id,title:X.title,evidence:X.evidence,confidence:X.confidence,current:K,total:F})):(ee.push({sessionId:P.id,reason:X.skipped??"unknown"}),await G("skipped",{sessionId:P.id,reason:X.skipped??"unknown",current:K,total:F}))}catch(X){let fe=X instanceof Error?X.message:String(X),we=X instanceof dt?"no-context-available":"failed";Z.push({sessionId:P.id,error:fe}),await G("error",{sessionId:P.id,error:fe,code:we,current:K,total:F})}}await G("done",{generated:B,skipped:ee,failed:Z,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),p=js(l);return p?i.json(p):i.body(null,204)}),t.put("/api/sessions/:id/notes",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.content!="string")return i.json({error:"content required (string)"},400);try{let g=Ka(l,p.content);return i.json(g)}catch(g){return console.error("[notes] failed to save note for session",l,g),i.json({error:"failed to save note"},500)}}),t.post("/api/sessions/:id/generate-note",async i=>{let l=i.req.param("id");if(!Ge().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await Va(l),_=Za(l,g);return i.json(_)}catch(g){let _=g.message,E=/no messages available/i.test(_)?404:500;return i.json({error:_},E)}}),t.get("/api/semantic/status",i=>i.json(ar())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),p=ys.partial().safeParse(l);return p.success?(ws(p.data),i.json(ar())):i.json({error:"invalid semantic config",issues:p.error.issues},400)}),t.get("/api/semantic/config",i=>i.json(ae())),t.post("/api/semantic/backfill",async i=>{if(Jo)return i.json({error:"a scan is already running"},409);if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let p=await i.req.json().catch(()=>({})),g=Math.max(1,Math.min(5e3,Number(p.limit??200)));return Jo=!0,ks({limit:g,force:!!p.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{Jo=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",async i=>{if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let p=i.req.param("id"),g=await As(p);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=Ne(),p=Jd(),g=Uo();return i.json({embedder:l,worker:p,modelInstalled:g})}),t.post("/api/semantic/install",ls,async i=>{if(Uo())return i.json({ok:!0,status:"already_installed"});if(Xo)return i.json({error:"a scan is already running"},409);Xo=!0;try{return await Zd(),await gs(),Xd(),i.json({ok:!0,status:"installed"})}catch(l){let p=l instanceof Error?l.message:"unknown error";return i.json({ok:!1,error:p},500)}finally{Xo=!1}}),t.get("/api/sessions/:id/similar",ls,async i=>{if(!Ne().loaded)return i.json({error:"vector model not loaded"},503);let l=i.req.param("id"),p=Math.max(1,Math.min(50,Number(i.req.query("limit")??10)));try{let g=await Pd(l,p);return i.json({sessionId:l,similar:g})}catch(g){let _=g instanceof Error?g.message:"unknown error";return i.json({error:_},500)}}),t.get("/api/search",ls,async i=>{let l=f(),p=i.req.query("q")?.trim();if(!p)return i.json({query:"",hits:[],tags:[]});if(p.length>500)return i.json({error:"query too long (max 500 chars)"},400);let g=i.req.query("project"),_=p.split(/\s+/).filter(P=>P.length>0),E=_.filter(P=>P.startsWith("#")).map(P=>Be(P)).filter(Boolean),y=_.filter(P=>!P.startsWith("#")),A=y.length>20,x=(A?y.slice(0,20):y).map(P=>`"${P.replace(/"/g,"")}"`),N=x.join(" "),I=Math.max(1,Math.min(200,Number(i.req.query("limit")??30)));if(x.length===0&&E.length>0){let P=`
1749
1885
  SELECT s.id AS session_id,
1750
1886
  s.id AS message_uuid,
1751
1887
  p.name AS project,
@@ -1758,7 +1894,7 @@ ${o}
1758
1894
  JOIN projects p ON p.id = s.project_id
1759
1895
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1760
1896
  WHERE 1=1
1761
- `,q={limit:v};m&&(P+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",q.proj=`%${Qt(m)}%`),E.forEach((Ee,bt)=>{P+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${bt})`,q[`tag_${bt}`]=Ee}),P+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let pe=l.prepare(P).all(q);return i.json({query:p,hits:pe,tags:E,truncated:R})}let C=`
1897
+ `,X={limit:I};g&&(P+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",X.proj=`%${cs(g)}%`),E.forEach((we,At)=>{P+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${At})`,X[`tag_${At}`]=we}),P+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let fe=l.prepare(P).all(X);return i.json({query:p,hits:fe,tags:E,truncated:A})}let C=`
1762
1898
  SELECT m.session_id AS session_id,
1763
1899
  m.uuid AS message_uuid,
1764
1900
  p.name AS project,
@@ -1773,7 +1909,7 @@ ${o}
1773
1909
  JOIN projects p ON p.id = s.project_id
1774
1910
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1775
1911
  WHERE messages_fts MATCH @fts
1776
- `,O={fts:N,limit:v};m&&(C+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",O.proj=`%${Qt(m)}%`),E.forEach((P,q)=>{C+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${q})`,O[`tag_${q}`]=P}),C+=" ORDER BY bm25(messages_fts) LIMIT @limit";let U=l.prepare(C).all(O).map(P=>({...P,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:p,hits:U,tags:E,truncated:R});let V=[];try{let P=`
1912
+ `,L={fts:N,limit:I};g&&(C+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",L.proj=`%${cs(g)}%`),E.forEach((P,X)=>{C+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${X})`,L[`tag_${X}`]=P}),C+=" ORDER BY bm25(messages_fts) LIMIT @limit";let B=l.prepare(C).all(L).map(P=>({...P,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:p,hits:B,tags:E,truncated:A});let Z=[];try{let P=`
1777
1913
  SELECT s.id AS session_id,
1778
1914
  s.id AS message_uuid,
1779
1915
  p.name AS project,
@@ -1789,15 +1925,15 @@ ${o}
1789
1925
  JOIN projects p ON p.id = s.project_id
1790
1926
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1791
1927
  WHERE sessions_fts MATCH @fts
1792
- `,q={fts:N,limit:v};m&&(P+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",q.proj=`%${Qt(m)}%`),E.forEach((pe,Ee)=>{P+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${Ee})`,q[`tag_${Ee}`]=pe}),P+=" ORDER BY rank LIMIT @limit",V=l.prepare(P).all(q)}catch(P){console.error("[search.semantic] failed:",P)}if(ke().loaded)try{let P=await $d(p,v),q=U.map(z=>({id:String(z.session_id),data:z,lane:"bm25"})),pe=V.map(z=>({id:String(z.session_id),data:z,lane:"summary"})),Ee=P.map(z=>({id:z.sessionId,data:{session_id:z.sessionId,snippet:z.text,matched_via:"vector"},lane:"vector"})),ts=Hd([q,pe,Ee]).slice(0,v).map(z=>({...z.data,session_id:z.id,rrf_score:z.score,lanes:z.lanes,matched_via:z.lanes.length>1?"fused":z.lanes[0]}));return i.json({query:p,hits:ts,tags:E,mode:"semantic",fusion:"rrf",truncated:R})}catch(P){console.error("[search.vector] failed, falling back:",P)}let W=new Set(U.map(P=>String(P.session_id))),G=V.filter(P=>!W.has(String(P.session_id))).map(({rank:P,...q})=>({...q,matched_via:"semantic"})),K=[...U,...G].slice(0,v);return i.json({query:p,hits:K,tags:E,mode:"semantic",truncated:R})}),t.get("/api/sessions/:id/context",es,i=>{let l=f(),p=i.req.param("id"),m=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,
1928
+ `,X={fts:N,limit:I};g&&(P+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",X.proj=`%${cs(g)}%`),E.forEach((fe,we)=>{P+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${we})`,X[`tag_${we}`]=fe}),P+=" ORDER BY rank LIMIT @limit",Z=l.prepare(P).all(X)}catch(P){console.error("[search.semantic] failed:",P)}if(Ne().loaded)try{let P=await Fd(p,I),X=B.map(V=>({id:String(V.session_id),data:V,lane:"bm25"})),fe=Z.map(V=>({id:String(V.session_id),data:V,lane:"summary"})),we=P.map(V=>({id:V.sessionId,data:{session_id:V.sessionId,snippet:V.text,matched_via:"vector"},lane:"vector"})),us=Ud([X,fe,we]).slice(0,I).map(V=>({...V.data,session_id:V.id,rrf_score:V.score,lanes:V.lanes,matched_via:V.lanes.length>1?"fused":V.lanes[0]}));return i.json({query:p,hits:us,tags:E,mode:"semantic",fusion:"rrf",truncated:A})}catch(P){console.error("[search.vector] failed, falling back:",P)}let q=new Set(B.map(P=>String(P.session_id))),G=Z.filter(P=>!q.has(String(P.session_id))).map(({rank:P,...X})=>({...X,matched_via:"semantic"})),K=[...B,...G].slice(0,I);return i.json({query:p,hits:K,tags:E,mode:"semantic",truncated:A})}),t.get("/api/sessions/:id/context",ls,i=>{let l=f(),p=i.req.param("id"),g=i.req.query("mode")==="full"?"full":"condensed",_=i.req.query("subagents")==="1",E=i.req.query("prelude")??null,y=l.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
1793
1929
  s.started_at, s.ended_at, s.message_count, s.git_branch
1794
1930
  FROM sessions s JOIN projects p ON p.id = s.project_id
1795
- WHERE s.id = ?`).get(p);if(!T)return i.json({error:"not found"},404);let R=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1931
+ WHERE s.id = ?`).get(p);if(!y)return i.json({error:"not found"},404);let A=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1796
1932
  FROM messages
1797
1933
  WHERE session_id = ?
1798
- ORDER BY COALESCE(timestamp, ''), rowid`).all(p),A=si(T,R,{mode:m,includeSidechain:_,prelude:E});return i.text(A)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:Ei(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),p=Ne(l);if(!p)return i.json({error:"not found"},404);let m=bi(l,!0);return i.json({collection:p,members:m})}),t.post("/api/collections",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=At({name:l.name,description:l.description??null,icon:l.icon??null,color:l.color??null,parent_id:l.parent_id??null,sort_key:l.sort_key});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/collections/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let m=Ti(l,p);return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.post("/api/collections/:id/archive",i=>{let l=i.req.param("id");try{let p=yi(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/restore",i=>{let l=i.req.param("id");try{let p=wi(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/members",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.session_id!="string")return i.json({error:"session_id required"},400);try{let m=xt(l,p.session_id,p.note??null);return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.delete("/api/collections/:id/members/:sid",i=>{let l=i.req.param("id"),p=i.req.param("sid");try{let m=Ri(l,p);return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.get("/api/sessions/:id/collections",i=>{let l=i.req.param("id");return i.json({collections:Si(l)})});let w=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:Oi()})),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||!w.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let p=Bn({name:l.name,type:l.type,pattern:l.pattern,collection_id:l.collection_id,parent_collection_id:l.parent_collection_id,priority:l.priority,enabled:l.enabled});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/auto-collections/rules/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let m=Li(l,p);return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.delete("/api/auto-collections/rules/:id",i=>{let l=i.req.param("id");try{let p=Ci(l);return i.json(p)}catch(p){return i.json({error:p.message},400)}}),t.get("/api/auto-collections/suggestions",i=>{let l=i.req.query("dismissed")==="1";return i.json({suggestions:gs({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let p=vi(l);return i.json({rule:p})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/suggestions/:id/dismiss",i=>{let l=i.req.param("id");try{return Ii(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=_s();return i.json({suggestions:l})}),t.get("/api/auto-collections/suggestions/:id/preview",i=>{let l=i.req.param("id"),p=Math.max(1,Math.min(20,Number(i.req.query("limit"))||3)),_=gs({includeDismissed:!1}).find(T=>T.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=xi(_.type,_.pattern,p);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(ji());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Jn({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),p=te(l);if(!p)return i.json({error:"thread not found"},404);let m=p.edges.map(_=>({..._,alias_source:_.alias==null?null:I.isSessionAutoLinked(_.session_id)?"auto":"manual"}));return i.json({thread:{...p,edges:m}})}),t.post("/api/threads",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name)return i.json({error:"name required"},400);try{let p=hs({name:l.name,summary:l.summary??null,originSessionId:l.originSessionId});return i.json({thread:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/threads/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{p.name&&qi(l,p.name),p.close&&Ji(l),p.reopen&&Xi(l),p.archive&&Gi(l),"folder_id"in p&&ma(l,p.folder_id??null);let m=te(l);return m?i.json({thread:m}):i.json({error:"thread not found"},404)}catch(m){return i.json({error:m.message},400)}}),t.get("/api/thread-folders",i=>i.json({folders:Kn()})),t.post("/api/thread-folders",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=aa({name:l.name,parentFolderId:l.parent_folder_id??null,projectScope:l.project_scope??null});return i.json({folder:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/thread-folders/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{let m;return p.name&&(m=la(l,p.name)),"parent_folder_id"in p&&(m=da(l,p.parent_folder_id??null)),m?i.json({folder:m}):i.json({error:"no patch fields"},400)}catch(m){return i.json({error:m.message},400)}}),t.delete("/api/thread-folders/:id",i=>{let l=i.req.param("id");try{return pa(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/thread-folders/reorder",async i=>{let l=await i.req.json().catch(()=>({})),p=l.ordered_ids;if(!Array.isArray(p))return i.json({error:"ordered_ids must be an array"},400);try{return ua(l.parent_folder_id??null,l.project_scope??null,p),i.json({ok:!0})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/threads/:id/sessions",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionId)return i.json({error:"sessionId required"},400);try{let m=Es({threadId:l,sessionId:p.sessionId,parentSessionId:p.parentSessionId??null,role:p.role});return i.json({edge:m})}catch(m){return i.json({error:m.message},400)}}),t.delete("/api/threads/:id/sessions/:sessionId",i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),m=Wi(l,p);return i.json(m)}),t.patch("/api/threads/:id/sessions/:sessionId",async i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),m=await i.req.json().catch(()=>({}));try{let _=Nt(l,p,m.parentSessionId??null);return i.json({edge:_})}catch(_){return i.json({error:_.message},400)}}),t.post("/api/threads/:id/merge",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sourceId)return i.json({error:"sourceId required"},400);try{let m=Yi(p.sourceId,l);return i.json({thread:m})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/threads/:id/split",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionIds?.length||!p.newThreadName)return i.json({error:"sessionIds and newThreadName required"},400);try{let m=Ki({threadId:l,sessionIds:p.sessionIds,newThreadName:p.newThreadName});return i.json({thread:m})}catch(m){return i.json({error:m.message},400)}}),t.get("/api/sessions/:id/threads",i=>{let l=i.req.param("id");return i.json({threads:Hi(l)})});let D=j.object({enabled:j.boolean(),band_lo:j.number().min(0).max(1).optional(),band_hi:j.number().min(0).max(1).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),L=j.object({project:j.string().min(1),threshold:j.number().min(0).max(1).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:D});t.post("/api/threads/scan/preflight",async i=>{let l=we(i);if(l)return l;let p=await i.req.json().catch(()=>null),m=L.safeParse(p);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);let _=Wl({project:m.data.project,threshold:m.data.threshold,model:m.data.model,llm_rescore:m.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json(_)});let X=j.object({project:j.string().min(1),threshold:j.number().min(0).max(1).optional(),llm_names:j.boolean().optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:D});t.post("/api/threads/scan/apply",async i=>{let l=we(i);if(l)return l;let p=await i.req.json().catch(()=>null),m=X.safeParse(p);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);let _=Jl({project:m.data.project,threshold:m.data.threshold,llm_names:m.data.llm_names,model:m.data.model,llm_rescore:m.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(!Br(l))return i.json({error:"job not found"},404);let m=Number(i.req.header("Last-Event-ID")??0);return Me(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of Xl(l,m))if(E||(await _.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=Br(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=we(i);return l||(Gl(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let M=j.object({project_id:j.number().int().positive(),mode:j.enum(["preflight","apply"]),window_hours:j.number().min(.5).max(168).optional(),score_threshold:j.number().min(0).max(1).optional(),use_live_pids:j.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),p=M.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let m=await ra(p.data.project_id,{windowHours:p.data.window_hours,scoreThreshold:p.data.score_threshold,useLivePids:p.data.use_live_pids});if(p.data.mode==="preflight")return i.json({plan:m});let _=oa(m);return i.json({plan:m,result:_})}catch(m){return i.json({error:m.message},400)}}),t.get("/api/threads/:id/titles/preflight",i=>{let l=i.req.param("id"),p=te(l);if(!p)return i.json({error:"thread not found"},404);let m=f(),_=0;for(let E of p.edges)m.prepare("SELECT auto_title_source FROM sessions WHERE id = ?").get(E.session_id)?.auto_title_source==="agent"&&(_+=1);return i.json({total:p.edges.length,alreadyTitled:_,untitled:p.edges.length-_})}),t.post("/api/threads/:id/titles/generate",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),m=te(l);if(!m)return i.json({error:"thread not found"},404);if(m.edges.length===0)return i.json({error:"thread has no sessions"},400);let _=Re(),E=p.model??_.model,T=mc({threadId:l,force:p.force??!1,model:E});return i.json({jobId:T})}),t.get("/api/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!dr(l))return i.json({error:"job not found"},404);let m=Number(i.req.header("Last-Event-ID")??0);return Me(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let R of gc(l,m))if(E||(await _.writeSSE({id:String(R.id),event:R.kind,data:JSON.stringify(R.data)}),R.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/jobs/:jobId",i=>{let l=dr(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>_c(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404)),t.post("/api/terminal/opened",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=I.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance",count:I.size()});let m=I.upsert({shell_pid:l.shell_pid,tab_name:l.tab_name,cwd:l.cwd??null,opened_at:l.opened_at??new Date().toISOString()});return i.json({ok:!0,ownership:p,count:I.size(),entry:m})}),t.post("/api/terminal/renamed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=I.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance"});let m=I.rename(l.shell_pid,l.tab_name);if(!m)return i.json({error:"unknown shell_pid"},404);let _=lu(l.shell_pid,l.tab_name);return i.json({ok:!0,ownership:p,entry:m,propagated:_})}),t.post("/api/terminal/closed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number")return i.json({error:"shell_pid required"},400);let p=I.remove(l.shell_pid);return i.json({ok:!0,removed:p,count:I.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):(I.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:I.pendingSize()}))}),t.post("/api/terminal/output",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.text!="string")return i.json({error:"shell_pid and text required"},400);let p=l.text.length>8192?l.text.slice(-8192):l.text,m=typeof l.captured_at=="string"?l.captured_at:new Date().toISOString();return I.setOutputTail(l.shell_pid,p,m),i.json({ok:!0})}),t.post("/api/terminal/sync",async i=>{let l=await i.req.json().catch(()=>null);if(!l||!Array.isArray(l.terminals))return i.json({error:"terminals array required"},400);let p=new Map;for(let O of I.all())p.set(O.shell_pid,O.tab_name);let m=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=m.filter(O=>{let F=I.claimPidOwnership(O.shell_pid,_);return E.push({shell_pid:O.shell_pid,ownership:F}),F!=="rejected"}),R=I.sync(T),A=0;for(let O of T){let F=p.get(O.shell_pid),U=I.get(O.shell_pid)?.tab_name??O.tab_name,V=!!U&&!re(U)&&!ne(U)?U:O.tab_name;F!==void 0&&F!==V&&(A+=lu(O.shell_pid,V))}let x=E.filter(O=>O.ownership==="rejected").length;x>0&&console.log(`[terminal/sync] dropped ${x} tab_name update(s), pid(s) owned by a different extension instance`);let N=await nS(),v={resolved:0,expired:0};try{v=Rc()}catch{}let C={rebound:0,ghosts:0,ambiguous:0};try{C=wc()}catch{}return _S(),i.json({ok:!0,count:I.size(),diff:R,propagated:A,live_sweep:N,deferred_resolved:v,rebound:C})}),t.get("/api/terminal/registry",i=>i.json({terminals:I.all(),count:I.size()})),t.get("/api/terminal/sessions/:shellPid",i=>{let l=i.req.param("shellPid"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"shellPid must be a positive integer"},400);let m=I.sessionsFor(p);return i.json({shell_pid:p,sessions:m})}),t.get("/api/sessions/:id/linked-terminal",i=>{let l=i.req.param("id"),p=I.all().find(_=>I.sessionsFor(_.shell_pid).includes(l)),m=[];if(!p){let _=f().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l);if(_?.cwd&&_.started_at){let E=Date.parse(_.started_at);if(Number.isFinite(E)){let T=_.cwd.replace(/\/+$/,""),R=300*1e3;for(let A of I.all()){if(!A.cwd||A.cwd.replace(/\/+$/,"")!==T||re(A.tab_name))continue;let x=Date.parse(A.opened_at),N=Date.parse(A.last_seen_at);!Number.isFinite(x)||!Number.isFinite(N)||x>E||N+R<E||m.push({shell_pid:A.shell_pid,tab_name:A.tab_name,cwd:A.cwd,opened_at:A.opened_at,last_seen_at:A.last_seen_at,reason:"time-overlap"})}m.sort((A,x)=>Date.parse(x.last_seen_at)-Date.parse(A.last_seen_at))}}}return p?i.json({linked:{shell_pid:p.shell_pid,tab_name:p.tab_name,cwd:p.cwd},suggested:[]}):i.json({linked:null,suggested:m})}),t.post("/api/sessions/:id/auto-relink",async i=>{let l=i.req.param("id");if(Te(l))return i.json({applied:!1,reason:"has-alias"});if(I.all().some(T=>I.sessionsFor(T.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let m=f().prepare("SELECT cwd, git_branch, started_at FROM sessions WHERE id = ?").get(l);if(!m?.cwd)return i.json({applied:!1,reason:"no-cwd"});let _=m.cwd.replace(/\/+$/,""),E=I.all().filter(T=>T.cwd&&T.cwd.replace(/\/+$/,"")===_&&!re(T.tab_name));if(E.length===1){let T=E[0],R=yt({sessionStartedAt:m.started_at??null,terminalOpenedAt:T.opened_at??null});if(!R.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:R.reason});let A=I.getOrigin(l),x=st({tabName:T.tab_name,origin:A??null,cwd:m.cwd??null,gitBranch:m.git_branch??null});return x?(me(l,x),I.linkSession(l,T.shell_pid),i.json({applied:!0,alias:x,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 yc(l);if(T){let A=I.get(T.shell_pid),x=yt({sessionStartedAt:m.started_at??null,terminalOpenedAt:A?.opened_at??null});if(!x.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:x.reason});let N=I.getOrigin(l),v=st({tabName:T.tab_name,origin:N??null,cwd:m.cwd??null,gitBranch:m.git_branch??null});if(v)return me(l,v),I.linkSession(l,T.shell_pid),i.json({applied:!0,alias:v,linked_pid:T.shell_pid,linked_tab_name:T.tab_name,matched_fingerprints:T.matched_fingerprints,method:"content-match"})}let R=6e4;if(m.started_at){let A=Date.parse(m.started_at);if(Number.isFinite(A)){let x=E.filter(v=>yt({sessionStartedAt:m.started_at,terminalOpenedAt:v.opened_at??null}).allowed).map(v=>({t:v,gap:A-Date.parse(v.opened_at??"")})).filter(v=>Number.isFinite(v.gap)&&v.gap>=0&&v.gap<=R);if(x.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:x.length});let N=x[0];if(N){let v=I.getOrigin(l),C=st({tabName:N.t.tab_name,origin:v??null,cwd:m.cwd??null,gitBranch:m.git_branch??null});if(C)return me(l,C),I.linkSession(l,N.t.shell_pid),i.json({applied:!0,alias:C,linked_pid:N.t.shell_pid,linked_tab_name:N.t.tab_name,method:"closest-before-temporal",gap_ms:N.gap})}}}return i.json({applied:!1,reason:"ambiguous",candidate_count:E.length})}return i.json({applied:!1,reason:"no-candidates"})}),t.post("/api/sessions/:id/relink",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(p?.clear)return I.unlinkSession(l),ns(l),i.json({ok:!0,alias:null,linked_pid:null});if(!p||typeof p.shell_pid!="number")return i.json({error:"shell_pid required"},400);let m=I.get(p.shell_pid);if(!m)return i.json({error:"terminal not registered"},404);let _=I.getOrigin(l),E=f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),T=null,R=m.tab_name?.trim()??"";if(R&&!re(R)&&!ne(R))T=R;else if(R&&ne(R)){let A=tt(R);A&&!re(A)&&(T=A)}return T?(I.unlinkSession(l),me(l,T),i.json({ok:!0,alias:T,linked_pid:p.shell_pid,linked_tab_name:m.tab_name})):i.json({error:"terminal has no usable name, name the tab in your editor first, then retry the relink"},422)}),t.post("/api/sessions/:id/recorrelate",async i=>{let l=i.req.param("id"),p=f().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!p?.file_path)return i.json({error:"session not found"},404);I.unlinkSession(l),ns(l),await Ms(p.file_path);let m=Te(l);return i.json({ok:!0,alias:m,linked_pid:I.all().find(_=>I.sessionsFor(_.shell_pid).includes(l))?.shell_pid??null})}),t.get("/api/paste-expand",async i=>{let l=i.req.query("session"),p=i.req.query("message"),m=i.req.query("path");if(!l||!p||!m)return i.json({error:"session, message and path are required"},400);let _=f(),E=_.prepare("SELECT rowid, content_text FROM messages WHERE uuid = ? AND session_id = ?").get(p,l);if(!E)return i.json({error:"message not found in session"},404);let T=m.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 A=_.prepare(`SELECT content_text FROM messages
1934
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(p),k=qa(y,A,{mode:g,includeSidechain:_,prelude:E});return i.text(k)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:ec(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),p=Ie(l);if(!p)return i.json({error:"not found"},404);let g=tc(l,!0);return i.json({collection:p,members:g})}),t.post("/api/collections",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=Bt({name:l.name,description:l.description??null,icon:l.icon??null,color:l.color??null,parent_id:l.parent_id??null,sort_key:l.sort_key});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/collections/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let g=nc(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.post("/api/collections/:id/archive",i=>{let l=i.req.param("id");try{let p=rc(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/restore",i=>{let l=i.req.param("id");try{let p=oc(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/members",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.session_id!="string")return i.json({error:"session_id required"},400);try{let g=Ht(l,p.session_id,p.note??null);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/collections/:id/members/:sid",i=>{let l=i.req.param("id"),p=i.req.param("sid");try{let g=ic(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/collections",i=>{let l=i.req.param("id");return i.json({collections:sc(l)})});let R=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:dc()})),t.post("/api/auto-collections/rules",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string"||typeof l.pattern!="string"||!l.type||!R.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let p=jr({name:l.name,type:l.type,pattern:l.pattern,collection_id:l.collection_id,parent_collection_id:l.parent_collection_id,priority:l.priority,enabled:l.enabled});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/auto-collections/rules/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let g=pc(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/auto-collections/rules/:id",i=>{let l=i.req.param("id");try{let p=mc(l);return i.json(p)}catch(p){return i.json({error:p.message},400)}}),t.get("/api/auto-collections/suggestions",i=>{let l=i.req.query("dismissed")==="1";return i.json({suggestions:Hs({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let p=_c(l);return i.json({rule:p})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/suggestions/:id/dismiss",i=>{let l=i.req.param("id");try{return gc(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=Ws();return i.json({suggestions:l})}),t.get("/api/auto-collections/suggestions/:id/preview",i=>{let l=i.req.param("id"),p=Math.max(1,Math.min(20,Number(i.req.query("limit"))||3)),_=Hs({includeDismissed:!1}).find(y=>y.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=lc(_.type,_.pattern,p);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(fc());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Pr({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),p=re(l);if(!p)return i.json({error:"thread not found"},404);let g=p.edges.map(_=>({..._,alias_source:_.alias==null?null:v.isSessionAutoLinked(_.session_id)?"auto":"manual"}));return i.json({thread:{...p,edges:g}})}),t.post("/api/threads",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name)return i.json({error:"name required"},400);try{let p=Xs({name:l.name,summary:l.summary??null,originSessionId:l.originSessionId});return i.json({thread:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/threads/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{p.name&&kc(l,p.name),p.close&&xc(l),p.reopen&&Nc(l),p.archive&&Oc(l),"folder_id"in p&&zc(l,p.folder_id??null);let g=re(l);return g?i.json({thread:g}):i.json({error:"thread not found"},404)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/thread-folders",i=>i.json({folders:Hr()})),t.post("/api/thread-folders",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=Wc({name:l.name,parentFolderId:l.parent_folder_id??null,projectScope:l.project_scope??null});return i.json({folder:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/thread-folders/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{let g;return p.name&&(g=Xc(l,p.name)),"parent_folder_id"in p&&(g=Jc(l,p.parent_folder_id??null)),g?i.json({folder:g}):i.json({error:"no patch fields"},400)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/thread-folders/:id",i=>{let l=i.req.param("id");try{return Gc(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/thread-folders/reorder",async i=>{let l=await i.req.json().catch(()=>({})),p=l.ordered_ids;if(!Array.isArray(p))return i.json({error:"ordered_ids must be an array"},400);try{return Yc(l.parent_folder_id??null,l.project_scope??null,p),i.json({ok:!0})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/sessions",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionId)return i.json({error:"sessionId required"},400);try{let g=Js({threadId:l,sessionId:p.sessionId,parentSessionId:p.parentSessionId??null,role:p.role});return i.json({edge:g})}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/threads/:id/sessions/:sessionId",i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),g=Ac(l,p);return i.json(g)}),t.patch("/api/threads/:id/sessions/:sessionId",async i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),g=await i.req.json().catch(()=>({}));try{let _=Wt(l,p,g.parentSessionId??null);return i.json({edge:_})}catch(_){return i.json({error:_.message},400)}}),t.post("/api/threads/:id/merge",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sourceId)return i.json({error:"sourceId required"},400);try{let g=Lc(p.sourceId,l);return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/split",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionIds?.length||!p.newThreadName)return i.json({error:"sessionIds and newThreadName required"},400);try{let g=Cc({threadId:l,sessionIds:p.sessionIds,newThreadName:p.newThreadName});return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/threads",i=>{let l=i.req.param("id");return i.json({threads:Rc(l)})});let D=j.object({enabled:j.boolean(),band_lo:j.number().min(0).max(1).optional(),band_hi:j.number().min(0).max(1).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),O=j.object({project:j.string().min(1),threshold:j.number().min(0).max(1).optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:D});t.post("/api/threads/scan/preflight",async i=>{let l=xe(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=O.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Gu({project:g.data.project,threshold:g.data.threshold,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json(_)});let Y=j.object({project:j.string().min(1),threshold:j.number().min(0).max(1).optional(),llm_names:j.boolean().optional(),model:j.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:D});t.post("/api/threads/scan/apply",async i=>{let l=xe(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=Y.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Ku({project:g.data.project,threshold:g.data.threshold,llm_names:g.data.llm_names,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json({jobId:_.jobId,reused:_.reused},_.reused?409:200)}),t.get("/api/threads/scan/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!yo(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ue(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let A of Vu(l,g))if(E||(await _.writeSSE({id:String(A.id),event:A.kind,data:JSON.stringify(A.data)}),A.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=yo(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/threads/scan/jobs/:jobId",i=>{let l=xe(i);return l||(Zu(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let M=j.object({project_id:j.number().int().positive(),mode:j.enum(["preflight","apply"]),window_hours:j.number().min(.5).max(168).optional(),score_threshold:j.number().min(0).max(1).optional(),use_live_pids:j.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),p=M.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=await $c(p.data.project_id,{windowHours:p.data.window_hours,scoreThreshold:p.data.score_threshold,useLivePids:p.data.use_live_pids});if(p.data.mode==="preflight")return i.json({plan:g});let _=Bc(g);return i.json({plan:g,result:_})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/threads/:id/titles/preflight",i=>{let l=i.req.param("id"),p=re(l);if(!p)return i.json({error:"thread not found"},404);let g=f(),_=0;for(let E of p.edges)g.prepare("SELECT auto_title_source FROM sessions WHERE id = ?").get(E.session_id)?.auto_title_source==="agent"&&(_+=1);return i.json({total:p.edges.length,alreadyTitled:_,untitled:p.edges.length-_})}),t.post("/api/threads/:id/titles/generate",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),g=re(l);if(!g)return i.json({error:"thread not found"},404);if(g.edges.length===0)return i.json({error:"thread has no sessions"},400);let _=Le(),E=p.model??_.model,y=wl({threadId:l,force:p.force??!1,model:E});return i.json({jobId:y})}),t.get("/api/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!zr(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ue(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let A of Rl(l,g))if(E||(await _.writeSSE({id:String(A.id),event:A.kind,data:JSON.stringify(A.data)}),A.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/jobs/:jobId",i=>{let l=zr(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>Al(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404)),t.post("/api/terminal/opened",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=v.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance",count:v.size()});let g=v.upsert({shell_pid:l.shell_pid,tab_name:l.tab_name,cwd:l.cwd??null,opened_at:l.opened_at??new Date().toISOString()});return i.json({ok:!0,ownership:p,count:v.size(),entry:g})}),t.post("/api/terminal/renamed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=v.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance"});let g=v.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let _=ip(l.shell_pid,l.tab_name);return i.json({ok:!0,ownership:p,entry:g,propagated:_})}),t.post("/api/terminal/closed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number")return i.json({error:"shell_pid required"},400);let p=v.remove(l.shell_pid);return i.json({ok:!0,removed:p,count:v.size()})}),t.post("/api/terminal/claude-started",async i=>{let l=await i.req.json().catch(()=>null);return!l||typeof l.shell_pid!="number"?i.json({error:"shell_pid required"},400):(v.pushPending({shell_pid:l.shell_pid,tab_name:typeof l.tab_name=="string"?l.tab_name:"",cwd:typeof l.cwd=="string"?l.cwd:null,started_at:typeof l.started_at=="string"?l.started_at:new Date().toISOString()}),i.json({ok:!0,pending:v.pendingSize()}))}),t.post("/api/terminal/output",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.text!="string")return i.json({error:"shell_pid and text required"},400);let p=l.text.length>8192?l.text.slice(-8192):l.text,g=typeof l.captured_at=="string"?l.captured_at:new Date().toISOString();return v.setOutputTail(l.shell_pid,p,g),i.json({ok:!0})}),t.post("/api/terminal/sync",async i=>{let l=await i.req.json().catch(()=>null);if(!l||!Array.isArray(l.terminals))return i.json({error:"terminals array required"},400);let p=new Map;for(let L of v.all())p.set(L.shell_pid,L.tab_name);let g=l.terminals.filter(L=>!!L&&typeof L.shell_pid=="number"&&typeof L.tab_name=="string").map(L=>({shell_pid:L.shell_pid,tab_name:L.tab_name,cwd:L.cwd??null,opened_at:L.opened_at??new Date().toISOString()})),_=l.extension_instance_id??null,E=[],y=g.filter(L=>{let F=v.claimPidOwnership(L.shell_pid,_);return E.push({shell_pid:L.shell_pid,ownership:F}),F!=="rejected"}),A=v.sync(y),k=0;for(let L of y){let F=p.get(L.shell_pid),B=v.get(L.shell_pid)?.tab_name??L.tab_name,Z=!!B&&!ce(B)&&!ie(B)?B:L.tab_name;F!==void 0&&F!==Z&&(k+=ip(L.shell_pid,Z))}let x=E.filter(L=>L.ownership==="rejected").length;x>0&&console.log(`[terminal/sync] dropped ${x} tab_name update(s), pid(s) owned by a different extension instance`);let N=await VT(),I={resolved:0,expired:0};try{I=Dl()}catch{}let C={rebound:0,ghosts:0,ambiguous:0};try{C=Ml()}catch{}return ly(),i.json({ok:!0,count:v.size(),diff:A,propagated:k,live_sweep:N,deferred_resolved:I,rebound:C})}),t.get("/api/terminal/registry",i=>i.json({terminals:v.all(),count:v.size()})),t.get("/api/terminal/sessions/:shellPid",i=>{let l=i.req.param("shellPid"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"shellPid must be a positive integer"},400);let g=v.sessionsFor(p);return i.json({shell_pid:p,sessions:g})}),t.get("/api/sessions/:id/linked-terminal",i=>{let l=i.req.param("id"),p=v.all().find(_=>v.sessionsFor(_.shell_pid).includes(l)),g=[];if(!p){let _=f().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l);if(_?.cwd&&_.started_at){let E=Date.parse(_.started_at);if(Number.isFinite(E)){let y=_.cwd.replace(/\/+$/,""),A=300*1e3;for(let k of v.all()){if(!k.cwd||k.cwd.replace(/\/+$/,"")!==y||ce(k.tab_name))continue;let x=Date.parse(k.opened_at),N=Date.parse(k.last_seen_at);!Number.isFinite(x)||!Number.isFinite(N)||x>E||N+A<E||g.push({shell_pid:k.shell_pid,tab_name:k.tab_name,cwd:k.cwd,opened_at:k.opened_at,last_seen_at:k.last_seen_at,reason:"time-overlap"})}g.sort((k,x)=>Date.parse(x.last_seen_at)-Date.parse(k.last_seen_at))}}}return p?i.json({linked:{shell_pid:p.shell_pid,tab_name:p.tab_name,cwd:p.cwd},suggested:[]}):i.json({linked:null,suggested:g})}),t.post("/api/sessions/:id/auto-relink",async i=>{let l=i.req.param("id");if(Ae(l))return i.json({applied:!1,reason:"has-alias"});if(v.all().some(y=>v.sessionsFor(y.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=f().prepare("SELECT cwd, git_branch, started_at FROM sessions WHERE id = ?").get(l);if(!g?.cwd)return i.json({applied:!1,reason:"no-cwd"});let _=g.cwd.replace(/\/+$/,""),E=v.all().filter(y=>y.cwd&&y.cwd.replace(/\/+$/,"")===_&&!ce(y.tab_name));if(E.length===1){let y=E[0],A=$t({sessionStartedAt:g.started_at??null,terminalOpenedAt:y.opened_at??null});if(!A.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:A.reason});let k=v.getOrigin(l),x=ut({tabName:y.tab_name,origin:k??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return x?(be(l,x),v.linkSession(l,y.shell_pid),i.json({applied:!0,alias:x,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,method:"cwd-singleton"})):i.json({applied:!1,reason:"no-usable-name"})}if(E.length>1){let y=await jl(l);if(y){let k=v.get(y.shell_pid),x=$t({sessionStartedAt:g.started_at??null,terminalOpenedAt:k?.opened_at??null});if(!x.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:x.reason});let N=v.getOrigin(l),I=ut({tabName:y.tab_name,origin:N??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(I)return be(l,I),v.linkSession(l,y.shell_pid),i.json({applied:!0,alias:I,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,matched_fingerprints:y.matched_fingerprints,method:"content-match"})}let A=6e4;if(g.started_at){let k=Date.parse(g.started_at);if(Number.isFinite(k)){let x=E.filter(I=>$t({sessionStartedAt:g.started_at,terminalOpenedAt:I.opened_at??null}).allowed).map(I=>({t:I,gap:k-Date.parse(I.opened_at??"")})).filter(I=>Number.isFinite(I.gap)&&I.gap>=0&&I.gap<=A);if(x.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:x.length});let N=x[0];if(N){let I=v.getOrigin(l),C=ut({tabName:N.t.tab_name,origin:I??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(C)return be(l,C),v.linkSession(l,N.t.shell_pid),i.json({applied:!0,alias:C,linked_pid:N.t.shell_pid,linked_tab_name:N.t.tab_name,method:"closest-before-temporal",gap_ms:N.gap})}}}return i.json({applied:!1,reason:"ambiguous",candidate_count:E.length})}return i.json({applied:!1,reason:"no-candidates"})}),t.post("/api/sessions/:id/relink",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(p?.clear)return v.unlinkSession(l),Is(l),i.json({ok:!0,alias:null,linked_pid:null});if(!p||typeof p.shell_pid!="number")return i.json({error:"shell_pid required"},400);let g=v.get(p.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let _=v.getOrigin(l),E=f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),y=null,A=g.tab_name?.trim()??"";if(A&&!ce(A)&&!ie(A))y=A;else if(A&&ie(A)){let k=lt(A);k&&!ce(k)&&(y=k)}return y?(v.unlinkSession(l),be(l,y),i.json({ok:!0,alias:y,linked_pid:p.shell_pid,linked_tab_name:g.tab_name})):i.json({error:"terminal has no usable name, name the tab in your editor first, then retry the relink"},422)}),t.post("/api/sessions/:id/recorrelate",async i=>{let l=i.req.param("id"),p=f().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!p?.file_path)return i.json({error:"session not found"},404);v.unlinkSession(l),Is(l),await an(p.file_path);let g=Ae(l);return i.json({ok:!0,alias:g,linked_pid:v.all().find(_=>v.sessionsFor(_.shell_pid).includes(l))?.shell_pid??null})}),t.get("/api/paste-expand",async i=>{let l=i.req.query("session"),p=i.req.query("message"),g=i.req.query("path");if(!l||!p||!g)return i.json({error:"session, message and path are required"},400);let _=f(),E=_.prepare("SELECT rowid, content_text FROM messages WHERE uuid = ? AND session_id = ?").get(p,l);if(!E)return i.json({error:"message not found in session"},404);let y=g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(!new RegExp(`\\[Pasted text #\\d+ \\+\\d+ lines\\]\\s*${y}`).test(E.content_text??""))return i.json({error:"path not referenced by this message"},403);let k=_.prepare(`SELECT content_text FROM messages
1799
1935
  WHERE session_id = ? AND rowid > ?
1800
- ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let x of A){let N=x.content_text??"";if(N.includes("**Tool result**")&&N.includes(m))return i.json({source:"tool-result",content:N});if(/^\s*1\t/.test(N)&&N.length>200)return i.json({source:"tool-result",content:N})}try{let x=await Qb(m),N=tS();if(!x.startsWith(N+"/")&&!x.startsWith(N+"\\"))return i.json({error:"path outside allowed root"},403);let v=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],O=x.slice(N.length+1).split("/")[0].split("\\")[0];if(v.includes(O))return i.json({error:"path inside sensitive directory"},403);let F=await Vb(x),U=2*1024*1024;if(F.size>U)return i.json({error:"file too large",size:F.size,max:U},413);let Q=await Zb(x,"utf8");return i.json({source:"disk",content:Q})}catch(x){return i.json({source:"missing",error:x.message})}}),t.get("/api/projects/:name/stats",i=>{let l=f(),p=i.req.param("name"),m=l.prepare(`SELECT
1936
+ ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let x of k){let N=x.content_text??"";if(N.includes("**Tool result**")&&N.includes(g))return i.json({source:"tool-result",content:N});if(/^\s*1\t/.test(N)&&N.length>200)return i.json({source:"tool-result",content:N})}try{let x=await YT(g),N=zT();if(!x.startsWith(N+"/")&&!x.startsWith(N+"\\"))return i.json({error:"path outside allowed root"},403);let I=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],L=x.slice(N.length+1).split("/")[0].split("\\")[0];if(I.includes(L))return i.json({error:"path inside sensitive directory"},403);let F=await XT(x),B=2*1024*1024;if(F.size>B)return i.json({error:"file too large",size:F.size,max:B},413);let ee=await JT(x,"utf8");return i.json({source:"disk",content:ee})}catch(x){return i.json({source:"missing",error:x.message})}}),t.get("/api/projects/:name/stats",i=>{let l=f(),p=i.req.param("name"),g=l.prepare(`SELECT
1801
1937
  (SELECT COUNT(*) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=? AND s.message_count > 2) AS sessions,
1802
1938
  (SELECT COALESCE(SUM(s.message_count), 0) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=?) AS messages,
1803
1939
  (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,
@@ -1805,10 +1941,10 @@ ${o}
1805
1941
  JOIN projects p ON p.id = s.project_id
1806
1942
  WHERE p.name = ? AND s.git_branch IS NOT NULL
1807
1943
  ORDER BY s.git_branch
1808
- LIMIT 20`).all(p).map(E=>E.git_branch);return i.json({...m,branches:_})});function B(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function se(i){if(!e)return i;let l=`<meta name="recall-token" content="${B(e)}" />`,p=i.indexOf("</head>");return p!==-1?i.slice(0,p)+l+i.slice(p):l+i}return dS?(t.use("/assets/*",cu({root:fo})),t.get("/favicon.svg",cu({root:fo})),t.get("/",i=>(i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0"),i.html(se(hn(ho,"utf8"))))),t.get("*",i=>i.req.path.startsWith("/api/")?i.notFound():(i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0"),i.html(se(hn(ho,"utf8")))))):t.get("/",i=>{let l=uu();return i.html(ti({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:du}))}),t}function ES(){if(Qs(),!!He().heuristicEnabled){try{let{updated:e}=ic();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}=ac();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}=cc();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}=lc();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 _u(e,t){let s=hS(t);return new Promise((n,r)=>{try{let o=Kb({fetch:s.fetch,port:e,hostname:"127.0.0.1"},()=>{n(o),setImmediate(()=>{try{ES()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}Z();H();import{watch as bS}from"chokidar";import{statSync as hu}from"node:fs";import{basename as SS}from"node:path";var TS=1500,So=new Map;function yS(e){let t=e.split("/"),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}async function wS(e){let t=0;try{t=hu(e).mtimeMs}catch{return}let s=yS(e);if(!s)return;let n=f(),r=n.prepare("SELECT id, file_mtime FROM sessions WHERE file_path = ? LIMIT 1").get(e);if(r&&r.file_mtime>=t)return;let o=new Map,a=null;for await(let k of yd(e)){let w=o.get(k.sessionId);if(w||(w={sessionId:k.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},o.set(k.sessionId,w)),w.entries.push(k),k.timestamp&&((!w.earliest||k.timestamp<w.earliest)&&(w.earliest=k.timestamp),(!w.latest||k.timestamp>w.latest)&&(w.latest=k.timestamp)),k.role==="user"&&!k.isSidechain){if(w.users+=1,!w.firstUser&&k.contentText){let D=k.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();D&&!/^<local-command-caveat>/.test(D)&&(w.firstUser=qe(D).redacted.slice(0,2e3))}}else k.role==="assistant"&&!k.isSidechain&&(w.assistants+=1);!w.cwd&&k.cwd&&(w.cwd=k.cwd),!w.branch&&k.gitBranch&&(w.branch=k.gitBranch),!w.version&&k.version&&(w.version=k.version),!a&&k.cwd&&(a=k.cwd)}let c=a?SS(a)||a:s,d=a??s.replace(/^-/,"/").replace(/-/g,"/"),u=n.prepare(`INSERT INTO projects (encoded_path, decoded_path, name)
1944
+ LIMIT 20`).all(p).map(E=>E.git_branch);return i.json({...g,branches:_})});function W(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function oe(i){if(!e)return i;let l=`<meta name="recall-token" content="${W(e)}" />`,p=i.indexOf("</head>");return p!==-1?i.slice(0,p)+l+i.slice(p):l+i}function $(i){let l=cp();return i.html(Wa({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:ap}))}function se(){try{return Yo(up,"utf8")}catch{return null}}return ry?(t.use("/assets/*",op({root:zo})),t.get("/favicon.svg",op({root:zo})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=se();return l===null?$(i):i.html(oe(l))}),t.get("*",i=>{if(i.req.path.startsWith("/api/"))return i.notFound();i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=se();return l===null?$(i):i.html(oe(l))})):t.get("/",i=>$(i)),t}function py(){if(wn(),!!Ge().heuristicEnabled){try{let{updated:e}=fl();e>0&&console.log(`[auto-title] backfilled heuristic title on ${e} sessions`)}catch(e){console.error("[auto-title] backfill failed:",e)}try{let{scanned:e,updated:t}=hl();t>0&&console.log(`[auto-title] refreshed templated heuristic title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] templated-title refresh failed:",e)}try{let{scanned:e,updated:t}=El();t>0&&console.log(`[auto-title] refreshed recursive_meta title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] recursive_meta refresh failed:",e)}try{let{scanned:e,updated:t}=bl();t>0&&console.log(`[auto-title] canonicalized brand on ${t}/${e} session titles`)}catch(e){console.error("[auto-title] brand canonicalization failed:",e)}}}async function mp(e,t){let s=dy(t);return new Promise((n,r)=>{try{let o=WT({fetch:s.fetch,port:e,hostname:"127.0.0.1"},()=>{n(o),setImmediate(()=>{try{py()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}Q();H();import{watch as my}from"chokidar";import{readdirSync as gy,statSync as Qo}from"node:fs";import{basename as _y,join as fy}from"node:path";var hy=1500,Zo=new Map;function Ey(e){let t=e.split("/"),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}async function _p(e){let t=0;try{t=Qo(e).mtimeMs}catch{return}let s=Ey(e);if(!s)return;let n=f(),r=n.prepare("SELECT id, file_mtime FROM sessions WHERE file_path = ? LIMIT 1").get(e);if(r&&r.file_mtime>=t)return;let o=new Map,a=null;for await(let w of bd(e)){let R=o.get(w.sessionId);if(R||(R={sessionId:w.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},o.set(w.sessionId,R)),R.entries.push(w),w.timestamp&&((!R.earliest||w.timestamp<R.earliest)&&(R.earliest=w.timestamp),(!R.latest||w.timestamp>R.latest)&&(R.latest=w.timestamp)),w.role==="user"&&!w.isSidechain){if(R.users+=1,!R.firstUser&&w.contentText){let D=w.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();D&&!/^<local-command-caveat>/.test(D)&&(R.firstUser=_e(D).redacted.slice(0,2e3))}}else w.role==="assistant"&&!w.isSidechain&&(R.assistants+=1);!R.cwd&&w.cwd&&(R.cwd=w.cwd),!R.branch&&w.gitBranch&&(R.branch=w.gitBranch),!R.version&&w.version&&(R.version=w.version),!a&&w.cwd&&(a=w.cwd)}let c=a?_y(a)||a:s,u=a??s.replace(/^-/,"/").replace(/-/g,"/"),d=n.prepare(`INSERT INTO projects (encoded_path, decoded_path, name)
1809
1945
  VALUES (?, ?, ?)
1810
1946
  ON CONFLICT(encoded_path) DO UPDATE SET decoded_path = excluded.decoded_path, name = excluded.name
1811
- RETURNING id`),g=n.prepare(`
1947
+ RETURNING id`),m=n.prepare(`
1812
1948
  INSERT INTO sessions (
1813
1949
  id, project_id, file_path, file_mtime,
1814
1950
  started_at, ended_at, message_count,
@@ -1836,38 +1972,4 @@ ${o}
1836
1972
  VALUES (@uuid, @session_id, @parent_uuid, @type, @role, @timestamp,
1837
1973
  @is_sidechain, @content_text, @tool_names, @raw_json)
1838
1974
  ON CONFLICT(uuid) DO NOTHING
1839
- `),b=n.prepare("DELETE FROM messages WHERE session_id = ?"),S=new Date().toISOString();if(n.transaction(()=>{let{id:k}=u.get(s,d,c);for(let w of o.values()){g.run({id:w.sessionId,project_id:k,file_path:e,file_mtime:t,started_at:w.earliest,ended_at:w.latest,message_count:w.entries.length,user_message_count:w.users,assistant_message_count:w.assistants,first_user_message:w.firstUser,cwd:w.cwd,git_branch:w.branch,version:w.version,indexed_at:S}),b.run(w.sessionId);for(let D of w.entries)h.run(RS(D));wd(n,w.sessionId,w.entries),dn(n,w.sessionId)}})(),He().heuristicEnabled)for(let k of o.values()){let w=et(k.firstUser);w&&de(k.sessionId,w,"heuristic")}}function RS(e){let t=qe(e.contentText).redacted,s=qe(e.raw).redacted;return{uuid:e.uuid,session_id:e.sessionId,parent_uuid:e.parentUuid,type:e.type,role:e.role,timestamp:e.timestamp,is_sidechain:e.isSidechain?1:0,content_text:t,tool_names:e.toolNames.join(","),raw_json:s}}function fu(e){let t=So.get(e);t?.timer&&clearTimeout(t.timer);let s={timer:null};s.timer=setTimeout(()=>{So.delete(e),wS(e).then(async()=>{Ms(e);try{let r=f().prepare("SELECT id FROM sessions WHERE file_path = ?").all(e);for(let o of r){Sd(o.id),Fd(o.id);try{Ni(o.id)}catch(a){console.error("[watcher] auto-collections apply failed:",a)}}}catch(n){console.error("[watcher] semantic dispatch failed:",n)}}).catch(n=>{console.error(`[watcher] reindex failed for ${e}:`,n)})},TS),So.set(e,s)}function Eu(){let e=bS(Mo,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:t=>{if(t.endsWith(".jsonl"))return!1;try{if(hu(t).isDirectory())return!1}catch{}return!0}});return e.on("add",t=>t.endsWith(".jsonl")&&fu(t)),e.on("change",t=>t.endsWith(".jsonl")&&fu(t)),e}import{createServer as Su}from"node:net";function bu(e){return new Promise(t=>{let s=Su();s.once("error",()=>t(!1)),s.once("listening",()=>{s.close(()=>t(!0))}),s.listen(e,"127.0.0.1")})}async function Tu(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await bu(t))return t;for(let s=0;s<50;s++){let n=49152+Math.floor(Math.random()*16383);if(!e.has(n)&&await bu(n))return n}return new Promise((s,n)=>{let r=Su();r.once("error",n),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>s(a))}else r.close(),n(new Error("could not determine a free port"))})})}Z();import{existsSync as NS,readFileSync as cO,writeFileSync as wu,unlinkSync as OS}from"node:fs";import{join as yo}from"node:path";Z();import{randomBytes as kS}from"node:crypto";import{writeFileSync as AS,readFileSync as sO,existsSync as nO}from"node:fs";import{join as xS}from"node:path";var To=xS($,"daemon.token");function yu(){J();let e=kS(32).toString("hex");return AS(To,e,{encoding:"utf8",mode:384}),e}var Ru=yo($,"daemon.pid"),ku=yo($,"daemon.port"),pO=yo($,"daemon.log");function Au(e){J(),wu(Ru,JSON.stringify(e),{encoding:"utf8",mode:384}),wu(ku,String(e.port),{encoding:"utf8",mode:384})}function wo(){for(let e of[Ru,ku,To])if(NS(e))try{OS(e)}catch{}}H();H();import{createHash as LS}from"node:crypto";var CS=/\b0x[0-9a-fA-F]+\b/g,IS=/\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,vS=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,jS=/:\d+:\d+/g,MS=/\bline\s+\d+\b/gi,DS=/\bcolumn\s+\d+\b/gi,FS=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,PS=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,US=/\b\d{4,}\b/g,$S=/(['"`])[^'"`\n]{1,128}\1/g;function BS(e){if(!e)return"";let t=String(e);return t=t.replace(CS,"<hex>"),t=t.replace(IS,"<uuid>"),t=t.replace(vS,"<ts>"),t=t.replace(jS,":<line>:<col>"),t=t.replace(MS,"line <n>"),t=t.replace(DS,"column <n>"),t=t.replace(FS,"pid <n>"),t=t.replace(PS,"port <n>"),t=t.replace(US,"<num>"),t=t.replace($S,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function HS(e){let t=(e.error_type??"unknown").toLowerCase().trim(),s=BS(e.snippet??e.message_hash??""),n=`${t}|${s}`;return LS("sha256").update(n).digest("hex").slice(0,16)}function WS(e){let t=f(),s=["oi.bug_signatures IS NOT NULL"],n=[];e&&(s.push("p.name = ?"),n.push(e));let r=`WHERE ${s.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
1840
- p.name AS project,
1841
- s.started_at AS started_at,
1842
- oi.bug_signatures AS bug_signatures
1843
- FROM session_output_index oi
1844
- LEFT JOIN sessions s ON s.id = oi.session_id
1845
- LEFT JOIN projects p ON p.id = s.project_id
1846
- ${r}`).all(...n),a=[];for(let c of o){if(!c.bug_signatures)continue;let d=[];try{let u=JSON.parse(c.bug_signatures);Array.isArray(u)&&(d=u)}catch{continue}for(let u of d){if(!u||typeof u!="object"||!(u.snippet??"").trim())continue;let h=HS(u);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:u,fingerprint:h})}}return a}function qS(e){let t=new Map;for(let n of e){let r=t.get(n.fingerprint);r||(r=[],t.set(n.fingerprint,r)),r.push(n)}let s=[];for(let[n,r]of t){let o=new Set,a=[];for(let g of r)o.has(g.session_id)||(o.add(g.session_id),a.push(g));let c=[...a].sort((g,h)=>{let b=g.started_at??"",S=h.started_at??"";return b&&S?b<S?-1:b>S?1:0:b?-1:S?1:0}),d=c.find(g=>g.started_at),u=[...c].reverse().find(g=>g.started_at);s.push({fingerprint:n,example_message:a[0].signature.snippet??a[0].signature.message_hash??"",members:a,first_seen_at:d?.started_at??new Date().toISOString(),last_seen_at:u?.started_at??new Date().toISOString()})}return s}function JS(e,t){if(e.length!==t.length)return 0;let s=0,n=0,r=0;for(let a=0;a<e.length;a++)s+=e[a]*t[a],n+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(n)*Math.sqrt(r);return o>0?s/o:0}function XS(e){let{records:t,vectors:s,epsilon:n,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let g=0;g<o;g++){let h=[];for(let b=0;b<o;b++){if(g===b)continue;1-JS(s[g],s[b])<=n&&h.push(b)}a.push(h)}let c=new Array(o).fill(!1),d=new Array(o).fill(-1),u=[];for(let g=0;g<o;g++){if(c[g])continue;c[g]=!0;let h=a[g];if(h.length<r)continue;let b=u.length;u.push({members:[t[g]]}),d[g]=b;let S=[...h];for(;S.length>0;){let y=S.shift();if(!c[y]&&(c[y]=!0,a[y].length>=r))for(let k of a[y])(!c[k]||d[k]===-1)&&S.push(k);d[y]===-1&&(d[y]=b,u[b].members.push(t[y]))}}return u}async function GS(e,t,s,n){if(e.length===0)return[];let r=e.map(d=>{let u=d.signature.snippet??d.signature.message_hash??"";return`${d.signature.error_type??""}: ${u}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let a=XS({records:e,vectors:o,epsilon:s,minPts:n}),c=[];for(let d of a){if(d.members.length===0)continue;let u=new Set,g=[];for(let w of d.members)u.has(w.session_id)||(u.add(w.session_id),g.push(w));if(g.length===0)continue;let b=`sem:${[...g.map(w=>w.fingerprint)].sort()[0]}`,S=[...g].sort((w,D)=>{let L=w.started_at??"",X=D.started_at??"";return L<X?-1:L>X?1:0}),y=S.find(w=>w.started_at),k=[...S].reverse().find(w=>w.started_at);c.push({fingerprint:b,example_message:g[0].signature.snippet??g[0].signature.message_hash??"",members:g,first_seen_at:y?.started_at??new Date().toISOString(),last_seen_at:k?.started_at??new Date().toISOString()})}return c}function xu(e,t){let s={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let n of e){if(n.members.length<t)continue;let r=Ga(n.fingerprint),o=Ya(n.fingerprint),a=n.members.map(u=>u.session_id).filter(u=>!o.has(u));if(r.length===0){let u=$a({signature_hash:n.fingerprint,example_message:n.example_message.slice(0,256),member_session_ids:n.members.map(g=>g.session_id),first_seen_at:n.first_seen_at,last_seen_at:n.last_seen_at});s.clusters_created+=1,s.members_added+=u.members.length,s.cluster_ids.push(u.cluster.id);continue}if(a.length===0){s.cluster_ids.push(r[0].id);continue}let c=r[0],d=Ha(c.id,a);d.added>0&&(s.clusters_merged+=1,s.members_added+=d.added),s.cluster_ids.push(c.id)}return s}async function Nu(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),s=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),n=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=WS(e.project),a=new Set(o.map(L=>L.session_id)),c=qS(o),d=[],u=!1;if(e.semantic){let L=e.embedder??null;if(!L)try{L=await zS()}catch(X){let B=(X instanceof Error?X.message:String(X)).split(`
1847
- `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${B}
1848
- Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),u=!0}if(L){let X=[];for(let M of c)M.members.length===1&&X.push(M.members[0]);X.length>=2&&(d=await GS(X,L,n,r))}}let g=c.filter(L=>L.members.length>=t),h=d.filter(L=>L.members.length>=t),b=xu(g,t),S=xu(h,t),y=[...b.cluster_ids,...S.cluster_ids],k=Array.from(new Set(y)),w=[];if(k.length>0){let L=f(),X=k.map(()=>"?").join(","),M=L.prepare(`SELECT * FROM bug_pattern_clusters
1849
- WHERE id IN (${X})
1850
- ORDER BY occurrence_count DESC, last_seen_at DESC
1851
- LIMIT ?`).all(...k,s);for(let B of M)w.push({id:B.id,signature_hash:B.signature_hash,example_message:B.example_message,occurrence_count:B.occurrence_count,first_seen_at:B.first_seen_at,last_seen_at:B.last_seen_at,resolved_in_session_id:B.resolved_in_session_id,fix_summary:B.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:g.length,semantic_groups:h.length,clusters_created:b.clusters_created+S.clusters_created,clusters_merged:b.clusters_merged+S.clusters_merged,members_added:b.members_added+S.members_added,semantic_skipped:u},clusters:w}}var YS=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:s}=await Promise.resolve().then(()=>(ot(),Xc));return s().loaded||await t(),e},KS=YS;async function zS(){return KS()}H();var Ro={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},VS=2,ZS=.25,QS=5,eT=60,tT=25;function Sn(e){return e.trim().toLowerCase()}function sT(e){let t=new Set;for(let s of e.files_written)t.add(`file:${Sn(s)}`);for(let s of e.brands_mentioned)t.add(`brand:${Sn(s)}`);for(let s of e.terms_introduced)t.add(`term:${Sn(s)}`);for(let s of e.plan_ids_referenced)t.add(`plan:${Sn(s)}`);return t}function nT(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/eT);return Math.max(.2,t)}function rT(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function oT(e){let t=Be(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(s=>s.term),plan_ids_referenced:t.plan_ids_referenced}:null}function iT(e){return f().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
1852
- FROM sessions s
1853
- JOIN session_output_index oi ON oi.session_id = s.id
1854
- WHERE s.project_id = ?
1855
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function aT(e,t){let s=new Map,n=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let a=t.get(o.id);if(!a)continue;let c=sT(a);if(c.size!==0){n.set(o.id,c);for(let d of c){let u=s.get(d);u?u.push(o.id):s.set(d,[o.id])}}}return{posting:s,vocab:n,startedAt:r}}function cT(e,t){let s=t.vocab.get(e),n=t.startedAt.get(e)??null;if(!s||!n)return[];let r=new Map;for(let a of s){let c=t.posting.get(a);if(c)for(let d of c){if(d===e)continue;let u=t.startedAt.get(d);if(!u||u>=n)continue;let g=r.get(d);g?g.push(a):r.set(d,[a])}}let o=[];for(let[a,c]of r){let d=c.length;if(d<VS)continue;let u=t.startedAt.get(a)??null,g=rT(n,u),h=nT(g),b=Math.min(1,d/QS*h);if(b<ZS)continue;let S=c.slice(0,12);o.push({target_session_id:a,matched_terms:S,overlap:d,days_apart:Math.round(g*10)/10,recency:Math.round(h*1e3)/1e3,confidence:Math.round(b*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,tT)}async function Ou(e){if(Ro.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=iT(e.projectId),s=new Map;for(let a of t){let c=oT(a.id);c&&s.set(a.id,c)}let n=aT(t,s),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let a of t){if(e.signal?.aborted)break;r.current_session_id=a.id,e.onProgress?.({...r});let c=cT(a.id,n);for(let d of c)try{let u=Ct({source_session_id:a.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"});o.push(u.id),r.suggestions_created+=1}catch(u){console.error("[citation-inference] createSuggestion failed:",u)}r.processed_sessions+=1,e.onProgress?.({...r})}return r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:o}}H();var lT=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,dT=[/\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],uT=.95,pT=.85;var ko=50;function mT(e){if(!e)return[];let t=new Set,s=[],n=e.match(lT);if(!n)return s;for(let r of n){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),s.push(o),s.length>=ko))break}return s}function gT(e){if(!e)return[];let t=new Set,s=[];for(let n of dT){n.lastIndex=0;let r=e.match(n);if(r){for(let o of r){let a=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!a||a.length>64)&&!t.has(a)&&(t.add(a),s.push(a),s.length>=ko))break}if(s.length>=ko)break}}return s}function Lu(e){let t=f();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function Cu(e){let t=f();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
1856
- s.project_id
1857
- FROM messages m
1858
- JOIN sessions s ON s.id = m.session_id
1859
- WHERE s.project_id = ?
1860
- AND m.is_sidechain = 0
1861
- AND m.content_text IS NOT NULL
1862
- AND length(m.content_text) > 0`).all(e):t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
1863
- s.project_id
1864
- FROM messages m
1865
- JOIN sessions s ON s.id = m.session_id
1866
- WHERE m.is_sidechain = 0
1867
- AND m.content_text IS NOT NULL
1868
- AND length(m.content_text) > 0`).all()}function _T(e){let t=f(),s=typeof e=="number"?"WHERE s.project_id = ?":"",n=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
1869
- s.project_id AS project_id,
1870
- oi.plan_ids_referenced AS plan_ids_json
1871
- FROM session_output_index oi
1872
- JOIN sessions s ON s.id = oi.session_id
1873
- ${s}`).all(...n)}function fT(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>typeof s=="string")}catch{}return[]}function Iu(e={}){if(Ro.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Lu(e.projectId),s=new Set(t.map(o=>o.id)),n=new Map;for(let o of t)n.set(o.id,o.project_id);let r=0;for(let o of Cu(e.projectId)){if(e.signal?.aborted)break;let a=mT(o.content_text);if(a.length===0)continue;let c=n.get(o.session_id);if(c!==void 0)for(let d of a){if(d===o.session_id)continue;let u=n.get(d);if(!(u===void 0&&!s.has(d))&&!(u!==void 0&&c!==void 0&&u!==c))try{Ct({source_session_id:o.session_id,target_session_id:d,link_type:"citation",confidence:uT,evidence:{matched_uuid:d,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function vu(e={}){let t=_T(e.projectId);if(t.length===0)return{created:0};let s=new Map;for(let c of t){let d=fT(c.plan_ids_json);for(let u of d){let g=u.trim().toLowerCase();if(!g)continue;let h=s.get(g);h?h.push({id:c.session_id,project_id:c.project_id}):s.set(g,[{id:c.session_id,project_id:c.project_id}])}}if(s.size===0)return{created:0};let n=Lu(e.projectId),r=new Map;for(let c of n)r.set(c.id,c.project_id);let o=0,a=new Map;for(let c of Cu(e.projectId)){if(e.signal?.aborted)break;let d=gT(c.content_text);if(d.length===0)continue;let u=r.get(c.session_id);if(u!==void 0)for(let g of d){let h=s.get(g);if(h)for(let b of h){if(b.id===c.session_id||b.project_id!==u)continue;let S=a.get(c.session_id);S||(S=new Map,a.set(c.session_id,S));let y=S.get(b.id);y||(y=new Set,S.set(b.id,y)),y.add(g)}}}for(let[c,d]of a)for(let[u,g]of d)try{Ct({source_session_id:c,target_session_id:u,link_type:"citation",confidence:pT,evidence:{matched_plan_ids:Array.from(g).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}ge();var le="[daemon:inference]",Ao=!1,xo=!1,No=!1,Oo=!1,Lo=!1,ju=0,Co=!1,Io=!1;function hT(){return f().prepare("SELECT id, name FROM projects").all()}async function Mu(){if(Ao)return;Ao=!0;let e=Date.now();try{let s=(await Nu({minClusterSize:2})).progress;(s.clusters_created||s.clusters_merged||s.members_added)&&console.log(`${le} bug-patterns: created=${s.clusters_created} merged=${s.clusters_merged} members_added=${s.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${le} bug-patterns failed:`,t)}finally{Ao=!1}}async function Du(){if(xo)return;xo=!0;let e=Date.now();try{let t=0,s=0;for(let n of hT())try{let r=await Ou({projectId:n.id});t+=r.progress.suggestions_created,s+=1}catch(r){console.error(`${le} citations failed for project "${n.name}":`,r)}t>0&&console.log(`${le} citations: ${t} suggestion(s) across ${s} project(s) (${Date.now()-e}ms)`)}catch(t){console.error(`${le} citations failed:`,t)}finally{xo=!1}}function Fu(){if(No)return;No=!0;let e=Date.now();try{let t=Iu({}),s=vu({});t.created+s.created>0&&console.log(`${le} l1: uuid=${t.created} plan=${s.created} (${Date.now()-e}ms)`)}catch(t){console.error(`${le} l1 failed:`,t)}finally{No=!1}}async function Pu(){if(Oo)return;let e=ce();if(!e.enabled||e.backfillPaused)return;Oo=!0;let t=Date.now();try{let s=await cn({limit:25});s.processed>0&&console.log(`${le} backfill: processed=${s.processed} ok=${s.ok} failed=${s.failed} (${Date.now()-t}ms)`)}catch(s){console.error(`${le} backfill failed:`,s)}finally{Oo=!1}}async function Uu(){if(Lo)return;let e=ce();if(!e.autoExtractEnabled)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-ju<t)return;if(!oe()){Co||(console.log(`${le} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),Co=!0);return}if(Co=!1,!await Go().catch(()=>!1)){Io||(console.log(`${le} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),Io=!0);return}Io=!1,Lo=!0,ju=Date.now();let n=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=rt({limit:r});if(o.length===0)return;let a=0,c=0,d=0,u=0;for(let g of o){let h=await Sr(g.id,{model:We});h.ok?a+=1:h.skipped||(c+=1),h.usage?.input_tokens&&(d+=h.usage.input_tokens),h.usage?.output_tokens&&(u+=h.usage.output_tokens)}console.log(`${le} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${d}+${u} (${Date.now()-n}ms)`)}catch(r){console.error(`${le} auto-extract failed:`,r)}finally{Lo=!1}}function $u(){let g=[],h=[];return g.push(setTimeout(()=>{Mu()},9e4)),h.push(setInterval(()=>{Mu()},18e5)),g.push(setTimeout(()=>{Du()},18e4)),h.push(setInterval(()=>{Du()},36e5)),g.push(setTimeout(()=>Fu(),12e4)),h.push(setInterval(()=>Fu(),18e5)),g.push(setTimeout(()=>{Pu()},24e4)),h.push(setInterval(()=>{Pu()},9e5)),g.push(setTimeout(()=>{Uu()},3e5)),h.push(setInterval(()=>{Uu()},9e5)),console.log(`${le} scheduled: bug-patterns (30m), citations (60m), l1 (30m), backfill (15m, when enabled), auto-extract (60m, when enabled)`),{startupTimers:g,intervalTimers:h,stop:()=>{for(let b of g)clearTimeout(b);for(let b of h)clearInterval(b)}}}var ET=360*60*1e3,bT=60*1e3,ST=1440*60*1e3,TT=300*1e3,yT=300*1e3,wT=10*1e3,RT=1440*60*1e3,kT=30*1e3,AT=500;async function xT(){let e=await Tu(),t=yu(),s=await _u(e,t);Au({pid:process.pid,port:e,startedAt:new Date().toISOString()});let n=Eu(),r=()=>{try{_s()}catch(L){console.error("[daemon] suggestion scan failed:",L)}},o=setTimeout(r,bT),a=setInterval(r,ET),c=()=>{try{let L=I.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)}},d=setTimeout(c,TT),u=setInterval(c,ST),g=()=>{try{let L=I.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)}},h=setTimeout(g,wT),b=setInterval(g,yT),S=()=>{Xo().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)})},y=setTimeout(S,kT),k=setInterval(S,RT),w=$u(),D=L=>{console.log(`[daemon] received ${L}, shutting down`),clearTimeout(o),clearInterval(a),clearTimeout(d),clearInterval(u),clearTimeout(h),clearInterval(b),clearTimeout(y),clearInterval(k),w.stop(),n.close(),s.close(),wo(),process.exit(0)};process.on("SIGTERM",()=>D("SIGTERM")),process.on("SIGINT",()=>D("SIGINT")),process.on("SIGHUP",()=>D("SIGHUP")),console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{Ds().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)})},AT)}xT().catch(e=>{console.error("[daemon] fatal:",e),wo(),process.exit(1)});
1975
+ `),b=n.prepare("DELETE FROM messages WHERE session_id = ?"),T=new Date().toISOString();if(n.transaction(()=>{let{id:w}=d.get(s,u,c);for(let R of o.values()){m.run({id:R.sessionId,project_id:w,file_path:e,file_mtime:t,started_at:R.earliest,ended_at:R.latest,message_count:R.entries.length,user_message_count:R.users,assistant_message_count:R.assistants,first_user_message:R.firstUser,cwd:R.cwd,git_branch:R.branch,version:R.version,indexed_at:T}),b.run(R.sessionId);for(let D of R.entries)h.run(by(D));Sd(n,R.sessionId,R.entries),xn(n,R.sessionId)}})(),Ge().heuristicEnabled)for(let w of o.values()){let R=ct(w.firstUser);R&&me(w.sessionId,R,"heuristic")}}function by(e){let t=_e(e.contentText).redacted,s=_e(e.raw).redacted;return{uuid:e.uuid,session_id:e.sessionId,parent_uuid:e.parentUuid,type:e.type,role:e.role,timestamp:e.timestamp,is_sidechain:e.isSidechain?1:0,content_text:t,tool_names:e.toolNames.join(","),raw_json:s}}function gp(e){let t=Zo.get(e);t?.timer&&clearTimeout(t.timer);let s={timer:null};s.timer=setTimeout(()=>{Zo.delete(e),_p(e).then(async()=>{an(e);try{let r=f().prepare("SELECT id FROM sessions WHERE file_path = ?").all(e);for(let o of r){Ea(o.id),jd(o.id);try{uc(o.id)}catch(a){console.error("[watcher] auto-collections apply failed:",a)}}}catch(n){console.error("[watcher] semantic dispatch failed:",n)}}).catch(n=>{console.error(`[watcher] reindex failed for ${e}:`,n)})},hy),Zo.set(e,s)}function fp(){let e=my(Fn,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:t=>{if(t.endsWith(".jsonl"))return!1;try{if(Qo(t).isDirectory())return!1}catch{}return!0}});return e.on("add",t=>t.endsWith(".jsonl")&&gp(t)),e.on("change",t=>t.endsWith(".jsonl")&&gp(t)),e}var Sy=4;function*hp(e){let t;try{t=gy(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let s of t){let n=fy(e,s.name);s.isDirectory()?yield*hp(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}async function Ep(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},s=f(),n=[],r=s.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1");for(let d of hp(Fn)){if(t.scanned+=1,d.includes("/subagents/")){t.skipped+=1;continue}let m;try{m=Qo(d).mtimeMs}catch{t.errors+=1;continue}let h=r.get(d);if(h&&h.file_mtime>=m){t.upToDate+=1;continue}n.push(d)}if(n.length===0)return t.durationMs=Date.now()-e,t;let o=n.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await _p(d),t.reindexed+=1}catch(m){t.errors+=1;let h=m instanceof Error?m.message:String(m);console.error(`[ingestion-sweep] failed for ${d}: ${h}`)}}},c=Math.min(Sy,n.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}import{createServer as Sp}from"node:net";function bp(e){return new Promise(t=>{let s=Sp();s.once("error",()=>t(!1)),s.once("listening",()=>{s.close(()=>t(!0))}),s.listen(e,"127.0.0.1")})}async function Tp(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await bp(t))return t;for(let s=0;s<50;s++){let n=49152+Math.floor(Math.random()*16383);if(!e.has(n)&&await bp(n))return n}return new Promise((s,n)=>{let r=Sp();r.once("error",n),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>s(a))}else r.close(),n(new Error("could not determine a free port"))})})}Q();import{existsSync as Ry,readFileSync as sC,writeFileSync as wp,unlinkSync as Ay}from"node:fs";import{join as ti}from"node:path";Q();import{randomBytes as Ty}from"node:crypto";import{writeFileSync as yy,readFileSync as KL,existsSync as VL}from"node:fs";import{join as wy}from"node:path";var ei=wy(U,"daemon.token");function yp(){J();let e=Ty(32).toString("hex");return yy(ei,e,{encoding:"utf8",mode:384}),e}var Rp=ti(U,"daemon.pid"),Ap=ti(U,"daemon.port"),iC=ti(U,"daemon.log");function kp(e){J(),wp(Rp,JSON.stringify(e),{encoding:"utf8",mode:384}),wp(Ap,String(e.port),{encoding:"utf8",mode:384})}function si(){for(let e of[Rp,Ap,ei])if(Ry(e))try{Ay(e)}catch{}}_r();var ky=360*60*1e3,xy=60*1e3,Ny=1440*60*1e3,Oy=300*1e3,Ly=300*1e3,Cy=10*1e3,vy=1440*60*1e3,Iy=30*1e3,jy=500,My=1500;async function Dy(){let e=await Tp(),t=yp(),s=await mp(e,t);kp({pid:process.pid,port:e,startedAt:new Date().toISOString()}),or(ae().enabled);try{Ut();let O=Oa();O>0&&console.log(`[daemon] archive: migrated ${O} hot row(s) into archive.sqlite`)}catch(O){let Y=O instanceof Error?O.message:String(O);console.error(`[daemon] archive migration failed: ${Y}`)}let n=fp(),r=()=>{try{Ws()}catch(O){console.error("[daemon] suggestion scan failed:",O)}},o=setTimeout(r,xy),a=setInterval(r,ky),c=()=>{try{let O=v.reapStaleLinks();(O.pruned_pids||O.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${O.pruned_pids} pid${O.pruned_pids===1?"":"s"}, ${O.pruned_sessions} session link${O.pruned_sessions===1?"":"s"}`)}catch(O){console.error("[daemon] stale-link reaper failed:",O)}},u=setTimeout(c,Oy),d=setInterval(c,Ny),m=()=>{try{let O=v.gcDeadPids();(O.pruned_pids||O.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${O.pruned_pids} pid${O.pruned_pids===1?"":"s"}, ${O.pruned_sessions} session link${O.pruned_sessions===1?"":"s"}`)}catch(O){console.error("[daemon] dead-pid gc failed:",O)}},h=setTimeout(m,Cy),b=setInterval(m,Ly),T=()=>{gi().then(O=>{O.ran&&O.revoked&&console.log(`[daemon] license check: REVOKED${O.reason?` (${O.reason})`:""}`)}).catch(O=>{console.error("[daemon] license check failed:",O)})},S=setTimeout(T,Iy),w=setInterval(T,vy),R=Ha(),D=O=>{console.log(`[daemon] received ${O}, shutting down`),clearTimeout(o),clearInterval(a),clearTimeout(u),clearInterval(d),clearTimeout(h),clearInterval(b),clearTimeout(S),clearInterval(w),R.stop(),n.close(),s.close(),si(),process.exit(0)};process.on("SIGTERM",()=>D("SIGTERM")),process.on("SIGINT",()=>D("SIGINT")),process.on("SIGHUP",()=>D("SIGHUP")),console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{cn().then(O=>{console.log(`[daemon] boot sweep: scanned ${O.scanned} live claude(s), linked ${O.linked}, renamed ${O.renamed}, ambiguous_cwd ${O.ambiguous_cwd}`)}).catch(O=>{console.error("[daemon] boot sweep failed:",O)})},jy),setTimeout(()=>{Ep().then(O=>{console.log(`[daemon] ingestion sweep: scanned=${O.scanned} reindexed=${O.reindexed} up-to-date=${O.upToDate} skipped=${O.skipped} errors=${O.errors} (${O.durationMs}ms)`)}).catch(O=>{console.error("[daemon] ingestion sweep failed:",O)})},My)}Dy().catch(e=>{console.error("[daemon] fatal:",e),si(),process.exit(1)});