@clauderecallhq/cli 0.93.0 → 0.95.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /* Claude Recall (proprietary). See LICENSE for terms. */
3
- var go=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var be=(e,t)=>{for(var n in t)go(e,n,{get:t[n],enumerable:!0})};import{createRequire as fo}from"node:module";var _o,ho,Eo,gt,ft,_t,ht=b(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...n){let s=n[0];return t==="warning"&&s instanceof Error&&s.name==="ExperimentalWarning"&&/SQLite/i.test(s.message)?!1:e(t,...n)}}_o=fo(import.meta.url),ho=["node","sqlite"].join(":"),Eo=_o(ho),gt=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let n=t.length===0?this.inner.run():this.inner.run(...t);return{changes:n.changes,lastInsertRowid:n.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},ft=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new Eo.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new gt(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,n={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(n.simple){let s=this.inner.prepare(`PRAGMA ${t}`).get();return s&&typeof s=="object"?Object.values(s)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...s)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...s);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,n){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),n===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,n)}},_t=ft});import{homedir as Dn}from"node:os";import{join as Et,basename as lp}from"node:path";import{existsSync as So,mkdirSync as To,chmodSync as bo,readdirSync as up,statSync as pp}from"node:fs";function v(){So(y)||To(y,{recursive:!0,mode:448}),process.platform!=="win32"&&bo(y,448)}var St,y,ge,I=b(()=>{"use strict";St=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Et(Dn(),".claude","projects"),y=process.env.RECALL_HOME?process.env.RECALL_HOME:Et(Dn(),".recall"),ge=Et(y,"db.sqlite")});function Pn(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(u=>u.name)),s=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[u,f]of s)n.has(u)||e.exec(`ALTER TABLE sessions ADD COLUMN ${u} ${f}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),i=new Set(r.map(u=>u.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[u,f]of a)i.has(u)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${u} ${f}`);let o=e.prepare("PRAGMA table_info(session_notes)").all(),c=new Set(o.map(u=>u.name)),l=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[u,f]of l)c.has(u)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${u} ${f}`);let d=e.prepare("PRAGMA table_info(threads)").all();new Set(d.map(u=>u.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 m=e.prepare("PRAGMA table_info(thread_folders)").all(),p=new Set(m.map(u=>u.name));p.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)")),p.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
3
+ var vo=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var ye=(e,t)=>{for(var n in t)vo(e,n,{get:t[n],enumerable:!0})};import{createRequire as Io}from"node:module";var Co,Do,Mo,ft,_t,ht,Et=b(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...n){let s=n[0];return t==="warning"&&s instanceof Error&&s.name==="ExperimentalWarning"&&/SQLite/i.test(s.message)?!1:e(t,...n)}}Co=Io(import.meta.url),Do=["node","sqlite"].join(":"),Mo=Co(Do),ft=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let n=t.length===0?this.inner.run():this.inner.run(...t);return{changes:n.changes,lastInsertRowid:n.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},_t=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new Mo.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new ft(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,n={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(n.simple){let s=this.inner.prepare(`PRAGMA ${t}`).get();return s&&typeof s=="object"?Object.values(s)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...s)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...s);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,n){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),n===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,n)}},ht=_t});import{homedir as $n}from"node:os";import{join as St,basename as Op}from"node:path";import{existsSync as Po,mkdirSync as Fo,chmodSync as $o,readdirSync as Ip,statSync as Cp}from"node:fs";function v(){Po(y)||Fo(y,{recursive:!0,mode:448}),process.platform!=="win32"&&$o(y,448)}var Tt,y,fe,I=b(()=>{"use strict";Tt=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:St($n(),".claude","projects"),y=process.env.RECALL_HOME?process.env.RECALL_HOME:St($n(),".recall"),fe=St(y,"db.sqlite")});function jn(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(u=>u.name)),s=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[u,f]of s)n.has(u)||e.exec(`ALTER TABLE sessions ADD COLUMN ${u} ${f}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),i=new Set(r.map(u=>u.name)),o=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[u,f]of o)i.has(u)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${u} ${f}`);let a=e.prepare("PRAGMA table_info(session_notes)").all(),c=new Set(a.map(u=>u.name)),l=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[u,f]of l)c.has(u)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${u} ${f}`);let d=e.prepare("PRAGMA table_info(threads)").all();new Set(d.map(u=>u.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 m=e.prepare("PRAGMA table_info(thread_folders)").all(),p=new Set(m.map(u=>u.name));p.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)")),p.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 go=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var be=(e,t)=>{f
12
12
  );
13
13
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_session ON message_embeddings(session_id);
14
14
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_generated ON message_embeddings(generated_at DESC);
15
- `);let g=e.prepare("PRAGMA table_info(projects)").all(),h=new Set(g.map(u=>u.name)),S=[["repo_root","TEXT"],["main_repo","TEXT"],["is_repo","INTEGER NOT NULL DEFAULT 0"],["is_worktree","INTEGER NOT NULL DEFAULT 0"]];for(let[u,f]of S)h.has(u)||e.exec(`ALTER TABLE projects ADD COLUMN ${u} ${f}`)}var Mn,Fn=b(()=>{"use strict";Mn=`
15
+ `);let g=e.prepare("PRAGMA table_info(projects)").all(),h=new Set(g.map(u=>u.name)),S=[["repo_root","TEXT"],["main_repo","TEXT"],["is_repo","INTEGER NOT NULL DEFAULT 0"],["is_worktree","INTEGER NOT NULL DEFAULT 0"]];for(let[u,f]of S)h.has(u)||e.exec(`ALTER TABLE projects ADD COLUMN ${u} ${f}`)}var Un,Hn=b(()=>{"use strict";Un=`
16
16
  PRAGMA journal_mode = WAL;
17
17
  PRAGMA synchronous = NORMAL;
18
18
  PRAGMA foreign_keys = ON;
@@ -228,10 +228,11 @@ END;
228
228
  -- segments of messages, embedded via local bge-base-en-v1.5 (768d). The vector
229
229
  -- data is a derived cache; if lost, it is recomputable from messages.
230
230
  -- Tables stay empty on Free tier (no model downloaded, no worker started).
231
-
232
- CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
233
- embedding float[768] distance_metric=cosine
234
- );
231
+ --
232
+ -- NOTE: the vec_chunks vec0 virtual table is intentionally NOT created here.
233
+ -- It lives in src/db/vecLoader.ts (ensureVecChunksTable), loaded lazily so a
234
+ -- lean MCP child that only does FTS/metadata never instantiates the native
235
+ -- vec0 module. The daemon/worker/CLI call ensureVecChunksTable at open.
235
236
 
236
237
  CREATE TABLE IF NOT EXISTS chunk_meta (
237
238
  rowid INTEGER PRIMARY KEY,
@@ -351,9 +352,9 @@ INSERT OR IGNORE INTO app_settings(key, value) VALUES ('semantic_enabled', '0');
351
352
  -- still threw "trigger messages_vec_ai already exists". That threw all the
352
353
  -- way out of getDb(), which caused syncSemanticEnabledToDb to silently
353
354
  -- skip the gate flip \u2014 and the live (still-present) triggers fired with
354
- -- the prior semantic_enabled='1' value, enqueueing 90k+ chunk_queue rows
355
- -- in minutes on a daemon the operator had explicitly disabled. Belt and
356
- -- suspenders: drop-then-create-if-not-exists is fully idempotent.
355
+ -- the prior semantic_enabled='1' value, causing spurious chunk_queue
356
+ -- inserts even when semantic search was disabled. Belt and suspenders:
357
+ -- drop-then-create-if-not-exists is fully idempotent.
357
358
  DROP TRIGGER IF EXISTS messages_vec_ai;
358
359
  DROP TRIGGER IF EXISTS messages_vec_ad;
359
360
  DROP TRIGGER IF EXISTS messages_vec_au;
@@ -730,8 +731,29 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_target
730
731
  ON bug_synthesis_results(scope, target_id, created_at DESC);
731
732
  CREATE INDEX IF NOT EXISTS idx_synth_results_created
732
733
  ON bug_synthesis_results(created_at DESC);
733
- `});import*as $n from"sqlite-vec";function E(){if(M)return M;v(),M=new _t(ge),$n.load(M),M.pragma("cache_size = -64000"),M.pragma("mmap_size = 268435456"),M.pragma("temp_store = MEMORY"),M.pragma("busy_timeout = 5000"),M.pragma("journal_size_limit = 67108864"),M.pragma("wal_autocheckpoint = 1000"),M.exec(Mn),Pn(M);try{M.exec("PRAGMA optimize")}catch{}return M}function Un(){if(M){try{M.exec("PRAGMA optimize")}catch{}try{M.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{M.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{M.pragma("wal_checkpoint(TRUNCATE)")}catch{}M.close(),M=null}}var M,x=b(()=>{"use strict";ht();I();Fn();M=null});function fe(e){if(e<60)return`${e}s`;if(e<3600)return`${Math.floor(e/60)}m`;if(e<86400)return`${Math.floor(e/3600)}h`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600);return n>0?`${t}d ${n}h`:`${t}d`}function ne(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var ye=b(()=>{"use strict"});import{writeFileSync as Do}from"node:fs";import{join as Mo}from"node:path";function ue(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Pe(e,t){let n=ue(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=E(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?{tag:n,added:!1}:(s.transaction(()=>{s.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,n,r),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,n,r)})(),zn(),{tag:n,added:!0})}function Xn(e,t){let n=ue(t);if(!n)return{tag:"",removed:!1};let s=E(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?(s.transaction(()=>{s.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,n),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,n,r)})(),zn(),{tag:n,removed:!0}):{tag:n,removed:!1}}function Fe(e){return E().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function Gn(){return E().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
734
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function zn(){try{v();let e=E(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),n=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),s={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:n};Do(Po,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var Po,$e=b(()=>{"use strict";x();I();Po=Mo(y,"tags.json")});function Fo(e,t){let n=e.filter(i=>i.content_text&&i.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let i=1;i<t-1;i++)s.add(Math.floor(i*r));return Array.from(s).sort((i,a)=>i-a).slice(0,t).map(i=>n[i])}function Ue(e){let t=E(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let a=e.sessionIds.map((o,c)=>`@sid_${c}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((o,c)=>{n[`sid_${c}`]=o})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
734
+
735
+ -- Incremental-indexing parse cursor. One row per JSONL file (a file may
736
+ -- hold multiple sessions, so the cursor is per-FILE, not per-session).
737
+ -- Lets the watcher stream only newly-appended bytes instead of reparsing
738
+ -- from byte 0. Derived/recomputable state \u2014 NOT subject to the three-layer
739
+ -- durability rule (a wrong/missing cursor just forces a full reparse).
740
+ -- size_bytes and line_count are diagnostic only: decideReadStrategy compares
741
+ -- the LIVE stat size against byte_offset, never the stored size_bytes.
742
+ CREATE TABLE IF NOT EXISTS file_cursor (
743
+ file_path TEXT PRIMARY KEY,
744
+ byte_offset INTEGER NOT NULL,
745
+ size_bytes INTEGER NOT NULL,
746
+ line_count INTEGER NOT NULL DEFAULT 0,
747
+ inode INTEGER,
748
+ prefix_hash TEXT,
749
+ mtime REAL,
750
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
751
+ );
752
+ `});function Wn(e,t){let n=t.RECALL_DB_PROFILE;if(n&&Uo.has(n))return n;let s=(e??"").split(/[\\/]/).pop()??"";return s==="mcp-server.js"||s==="claude-recall-mcp"?"light":s==="query-worker.js"?"worker":"full"}function Bn(e){let t=[["busy_timeout",15e3],["journal_size_limit",67108864],["wal_autocheckpoint",1e3]];return e==="full"?[["cache_size",-64e3],["mmap_size",268435456],...t]:e==="worker"?[["cache_size",-16e3],["mmap_size",0],...t]:[["cache_size",-4e3],["mmap_size",0],...t]}var Uo,Xn=b(()=>{"use strict";Uo=new Set(["light","full","worker"])});import*as zn from"sqlite-vec";function oe(e){let t=e;Gn.has(t)||(zn.load(e),Gn.add(t))}function Yn(e){e.prepare("SELECT 1 FROM sqlite_master WHERE name = 'vec_chunks'").get()||(oe(e),e.exec(jo))}var Gn,jo,Pe=b(()=>{"use strict";Gn=new WeakSet;jo=`
753
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
754
+ embedding float[768] distance_metric=cosine
755
+ );`});function E(){if(M)return M;v();let e=Wn(process.argv[1],process.env);Kn=e,M=new ht(fe);for(let[t,n]of Bn(e))M.pragma(`${t} = ${n}`);M.pragma("temp_store = MEMORY"),e!=="light"&&oe(M),M.exec(Un),jn(M),Yn(M);try{M.exec("PRAGMA optimize")}catch{}return M}function Jn(){if(M){if(Kn==="light"){try{M.pragma("wal_checkpoint(PASSIVE)")}catch{}M.close(),M=null;return}try{M.exec("PRAGMA optimize")}catch{}try{M.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{M.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{M.pragma("wal_checkpoint(TRUNCATE)")}catch{}M.close(),M=null}}var M,Kn,x=b(()=>{"use strict";Et();I();Hn();Xn();Pe();M=null,Kn="full"});function _e(e){if(e<60)return`${e}s`;if(e<3600)return`${Math.floor(e/60)}m`;if(e<86400)return`${Math.floor(e/3600)}h`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600);return n>0?`${t}d ${n}h`:`${t}d`}function ne(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var we=b(()=>{"use strict"});import{writeFileSync as Zo}from"node:fs";import{join as ea}from"node:path";function pe(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Fe(e,t){let n=pe(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=E(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?{tag:n,added:!1}:(s.transaction(()=>{s.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,n,r),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,n,r)})(),ns(),{tag:n,added:!0})}function es(e,t){let n=pe(t);if(!n)return{tag:"",removed:!1};let s=E(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?(s.transaction(()=>{s.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,n),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,n,r)})(),ns(),{tag:n,removed:!0}):{tag:n,removed:!1}}function $e(e){return E().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function ts(){return E().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
756
+ GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function ns(){try{v();let e=E(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),n=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),s={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:n};Zo(ta,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var ta,Ue=b(()=>{"use strict";x();I();ta=ea(y,"tags.json")});function na(e,t){let n=e.filter(i=>i.content_text&&i.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let i=1;i<t-1;i++)s.add(Math.floor(i*r));return Array.from(s).sort((i,o)=>i-o).slice(0,t).map(i=>n[i])}function je(e){let t=E(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let o=e.sessionIds.map((a,c)=>`@sid_${c}`).join(", ");r+=` AND s.id IN (${o})`,e.sessionIds.forEach((a,c)=>{n[`sid_${c}`]=a})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
735
757
  NULLIF(sa.alias, '') AS alias,
736
758
  COALESCE(s.first_user_message, '') AS first_user_message
737
759
  FROM sessions s
@@ -739,38 +761,38 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
739
761
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
740
762
  WHERE ${r}
741
763
  ORDER BY COALESCE(s.started_at, '') DESC
742
- LIMIT @limit`).all(n).map(a=>{let o=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
764
+ LIMIT @limit`).all(n).map(o=>{let a=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
743
765
  FROM messages WHERE session_id = ?
744
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),l=Fo(o,5).map(d=>`${d.role}: ${d.content_text.slice(0,400)}`).join(`
766
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(o.id),l=na(a,5).map(d=>`${d.role}: ${d.content_text.slice(0,400)}`).join(`
745
767
  ---
746
- `);return{id:a.id,project:a.project,git_branch:a.git_branch,alias:a.alias,first_user_message:a.first_user_message,message_sample:l,current_tags:Fe(a.id)}})}var bt=b(()=>{"use strict";x();$e()});import{z as G}from"zod";function Rt(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],i=[];return e.sessionId?(i.push("limit: 1"),r.push(` ${i.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&i.push("untaggedOnly: true"),e.project&&i.push(`project: "${e.project}"`),e.collectionId&&i.push(`collectionId: "${e.collectionId}"`),i.push(`limit: ${e.limit??100}`),r.push(` ${i.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
747
- `)}function Yo(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(`
748
- `)}function Jo(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(`
749
- `)}function Vo(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(`
750
- `)}var Go,zo,Ko,qo,Qo,Zo,ea,ta,Kn,At=b(()=>{"use strict";Go={project:G.string().optional().describe("Exact project name match (optional)."),collectionId:G.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:G.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:G.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:G.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:G.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:G.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};zo={sessionId:G.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:G.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};Ko={sessionId:G.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};qo={sessionId:G.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:G.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};Qo={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:Go,build:Rt,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Zo={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:zo,build:Yo,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},ea={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:Ko,build:Jo,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},ta={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:qo,build:Vo,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},Kn=[Qo,Zo,ea,ta]});import{writeFileSync as na}from"node:fs";import{join as sa}from"node:path";function Jn(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function ia(){return E().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:Jn(t.previous_aliases)}))}function je(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=E(),r=new Date().toISOString(),i=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return i&&(a=Jn(i.previous_aliases),i.alias!==n&&a.push({alias:i.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
768
+ `);return{id:o.id,project:o.project,git_branch:o.git_branch,alias:o.alias,first_user_message:o.first_user_message,message_sample:l,current_tags:$e(o.id)}})}var yt=b(()=>{"use strict";x();Ue()});import{z as G}from"zod";function At(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],i=[];return e.sessionId?(i.push("limit: 1"),r.push(` ${i.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&i.push("untaggedOnly: true"),e.project&&i.push(`project: "${e.project}"`),e.collectionId&&i.push(`collectionId: "${e.collectionId}"`),i.push(`limit: ${e.limit??100}`),r.push(` ${i.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
769
+ `)}function pa(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(`
770
+ `)}function ga(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(`
771
+ `)}function _a(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(`
772
+ `)}var da,ua,ma,fa,ha,Ea,Sa,Ta,rs,Nt=b(()=>{"use strict";da={project:G.string().optional().describe("Exact project name match (optional)."),collectionId:G.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:G.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:G.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:G.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:G.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:G.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};ua={sessionId:G.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:G.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};ma={sessionId:G.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};fa={sessionId:G.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:G.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};ha={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:da,build:At,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Ea={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:ua,build:pa,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Sa={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:ma,build:ga,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Ta={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:fa,build:_a,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},rs=[ha,Ea,Sa,Ta]});import{writeFileSync as ba}from"node:fs";import{join as ya}from"node:path";function is(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Ra(){return E().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:is(t.previous_aliases)}))}function He(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=E(),r=new Date().toISOString(),i=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),o=[];return i&&(o=is(i.previous_aliases),i.alias!==n&&o.push({alias:i.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
751
773
  VALUES (?, ?, ?, ?)
752
774
  ON CONFLICT(session_id) DO UPDATE SET
753
775
  alias = excluded.alias,
754
776
  updated_at = excluded.updated_at,
755
- previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(a)),oa(),{session_id:e,alias:n,updated_at:r,previous_aliases:a}}function oa(){try{v();let e=ia(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};na(ra,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var ra,He=b(()=>{"use strict";x();I();ra=sa(y,"aliases.json")});import{randomUUID as fa}from"node:crypto";import{writeFileSync as _a,readFileSync as tm,existsSync as nm}from"node:fs";import{join as ha}from"node:path";function Sa(e){return{...e}}function kt(e,t,n,s=null,r=new Date().toISOString()){E().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
756
- VALUES (?, ?, ?, ?, ?)`).run(e,s,t,n?JSON.stringify(n):null,r)}function Ta(e){let t=E().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function ba(e){if(!e)return 0;let t=0,n=e,s=new Set,r=E();for(;n;){if(s.has(n))throw new Error("collection cycle detected");s.add(n);let i=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!i)break;t+=1,n=i.parent_id}return t}function es(e){let t=E().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Sa(t):null}function xt(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let n=E(),s=new Date().toISOString(),r=fa();if(e.parent_id){if(!es(e.parent_id))throw new Error("parent collection not found");if(ba(e.parent_id)>=Zn-1)throw new Error(`max collection depth is ${Zn}`)}return n.transaction(()=>{n.prepare(`INSERT INTO collections
777
+ previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(o)),Aa(),{session_id:e,alias:n,updated_at:r,previous_aliases:o}}function Aa(){try{v();let e=Ra(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};ba(wa,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var wa,We=b(()=>{"use strict";x();I();wa=ya(y,"aliases.json")});import{randomUUID as Da}from"node:crypto";import{writeFileSync as Ma,readFileSync as Nm,existsSync as Lm}from"node:fs";import{join as Pa}from"node:path";function $a(e){return{...e}}function xt(e,t,n,s=null,r=new Date().toISOString()){E().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
778
+ VALUES (?, ?, ?, ?, ?)`).run(e,s,t,n?JSON.stringify(n):null,r)}function Ua(e){let t=E().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function ja(e){if(!e)return 0;let t=0,n=e,s=new Set,r=E();for(;n;){if(s.has(n))throw new Error("collection cycle detected");s.add(n);let i=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!i)break;t+=1,n=i.parent_id}return t}function ds(e){let t=E().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?$a(t):null}function Ot(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let n=E(),s=new Date().toISOString(),r=Da();if(e.parent_id){if(!ds(e.parent_id))throw new Error("parent collection not found");if(ja(e.parent_id)>=ls-1)throw new Error(`max collection depth is ${ls}`)}return n.transaction(()=>{n.prepare(`INSERT INTO collections
757
779
  (id, name, description, icon, color, parent_id, sort_key, created_at, updated_at, archived_at)
758
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",s,s),kt(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,s)})(),vt(),es(r)}function Ot(e,t,n=null,s={}){let r=E();if(Ta(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 o=s.source??"manual",c=s.rule_id??null;if(o==="auto"&&!c)throw new Error("auto membership requires a rule_id");let l=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
759
- VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,l,n,o,c),kt(e,"add",{note:n,source:o,rule_id:c},t,l)})(),vt(),{added:!0}}function ts(e,t){let n=E();if(!n.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return n.transaction(()=>{n.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),kt(e,"remove",null,t,r)})(),vt(),{removed:!0}}function ya(){return E().prepare(`SELECT id, collection_id, session_id, action, payload, at
780
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",s,s),xt(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,s)})(),It(),ds(r)}function vt(e,t,n=null,s={}){let r=E();if(Ua(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 a=s.source??"manual",c=s.rule_id??null;if(a==="auto"&&!c)throw new Error("auto membership requires a rule_id");let l=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
781
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,l,n,a,c),xt(e,"add",{note:n,source:a,rule_id:c},t,l)})(),It(),{added:!0}}function us(e,t){let n=E();if(!n.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return n.transaction(()=>{n.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),xt(e,"remove",null,t,r)})(),It(),{removed:!0}}function Ha(){return E().prepare(`SELECT id, collection_id, session_id, action, payload, at
760
782
  FROM collection_events
761
- ORDER BY at ASC, id ASC`).all()}function vt(){try{v();let e=E(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
783
+ ORDER BY at ASC, id ASC`).all()}function It(){try{v();let e=E(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
762
784
  created_at, updated_at, archived_at
763
785
  FROM collections
764
786
  ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),n=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
765
787
  FROM collection_sessions
766
- ORDER BY collection_id, added_at`).all(),s=ya(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:n,events:s};_a(Ea,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Ea,Zn,It=b(()=>{"use strict";x();I();Ea=ha(y,"collections.json"),Zn=8});function Ss(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let n=t.split(`
767
- `)[0].trim();return n.length>Es?n.slice(0,Es)+"\u2026":n}function Oa(e){return E().prepare(`SELECT s.id AS id,
788
+ ORDER BY collection_id, added_at`).all(),s=Ha(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:n,events:s};Ma(Fa,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Fa,ls,Ct=b(()=>{"use strict";x();I();Fa=Pa(y,"collections.json"),ls=8});function xs(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let n=t.split(`
789
+ `)[0].trim();return n.length>ks?n.slice(0,ks)+"\u2026":n}function Ja(e){return E().prepare(`SELECT s.id AS id,
768
790
  sa.alias AS alias,
769
791
  s.auto_title AS auto_title,
770
792
  s.first_user_message AS first_user_message
771
793
  FROM sessions s
772
794
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
773
- WHERE s.id = ?`).get(e)??null}function va(e){let t=Oa(e);return t?Ss(t):e.slice(0,8)}function Ts(e){if(!e)return null;let t=E(),n=t.prepare(`SELECT e.thread_id AS thread_id,
795
+ WHERE s.id = ?`).get(e)??null}function Va(e){let t=Ja(e);return t?xs(t):e.slice(0,8)}function Os(e){if(!e)return null;let t=E(),n=t.prepare(`SELECT e.thread_id AS thread_id,
774
796
  t.name AS thread_name,
775
797
  e.parent_session_id AS parent_session_id,
776
798
  e.added_at AS added_at
@@ -779,7 +801,7 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
779
801
  WHERE e.session_id = ?
780
802
  AND t.archived = 0
781
803
  ORDER BY e.added_at DESC
782
- LIMIT 1`).get(e);if(!n)return null;let s=n.parent_session_id?{id:n.parent_session_id,title:va(n.parent_session_id)}:null,r=n.parent_session_id?[e,n.parent_session_id]:[e],i=r.map(()=>"?").join(", "),o=t.prepare(`SELECT e.session_id AS session_id,
804
+ LIMIT 1`).get(e);if(!n)return null;let s=n.parent_session_id?{id:n.parent_session_id,title:Va(n.parent_session_id)}:null,r=n.parent_session_id?[e,n.parent_session_id]:[e],i=r.map(()=>"?").join(", "),a=t.prepare(`SELECT e.session_id AS session_id,
783
805
  s.id AS id,
784
806
  sa.alias AS alias,
785
807
  s.auto_title AS auto_title,
@@ -789,46 +811,46 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
789
811
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
790
812
  WHERE e.thread_id = ?
791
813
  AND e.session_id NOT IN (${i})
792
- ORDER BY e.added_at ASC`).all(n.thread_id,...r).map(c=>({id:c.session_id,title:c.id?Ss(c):c.session_id.slice(0,8)}));return{thread_id:n.thread_id,thread_name:n.thread_name,parent_session:s,siblings:o}}var Es,bs=b(()=>{"use strict";x();Es=80});var Ut=b(()=>{"use strict"});var ys=b(()=>{"use strict"});import{writeFileSync as Ia,mkdirSync as Ca,existsSync as Da}from"node:fs";import{join as ws}from"node:path";function Rs(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>!!n&&typeof n=="object"&&typeof n.title=="string"&&typeof n.replaced_at=="string")}catch{}return[]}function As(e){let t=E(),n=t.prepare(`SELECT rowid AS rid, content_text
814
+ ORDER BY e.added_at ASC`).all(n.thread_id,...r).map(c=>({id:c.session_id,title:c.id?xs(c):c.session_id.slice(0,8)}));return{thread_id:n.thread_id,thread_name:n.thread_name,parent_session:s,siblings:a}}var ks,vs=b(()=>{"use strict";x();ks=80});var jt=b(()=>{"use strict"});var Is=b(()=>{"use strict"});import{writeFileSync as qa,mkdirSync as Qa,existsSync as Za}from"node:fs";import{join as Cs}from"node:path";function Ds(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>!!n&&typeof n=="object"&&typeof n.title=="string"&&typeof n.replaced_at=="string")}catch{}return[]}function Ms(e){let t=E(),n=t.prepare(`SELECT rowid AS rid, content_text
793
815
  FROM messages
794
816
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
795
817
  AND content_text IS NOT NULL AND content_text != ''
796
818
  ORDER BY COALESCE(timestamp, ''), rowid ASC
797
- LIMIT ?`).all(e,Re),s=t.prepare(`SELECT rowid AS rid, content_text
819
+ LIMIT ?`).all(e,Ae),s=t.prepare(`SELECT rowid AS rid, content_text
798
820
  FROM messages
799
821
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
800
822
  AND content_text IS NOT NULL AND content_text != ''
801
823
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
802
- LIMIT ?`).all(e,ze),r=new Map;for(let d of n)r.set(d.rid,d.content_text);for(let d of s)r.set(d.rid,d.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let i=Array.from(r.entries()).sort((d,m)=>d[0]-m[0]).map(([,d])=>({content_text:d})),a=n.length===Re&&s.length===ze&&r.size===Re+ze,o=i.map((d,m)=>{let p=(d.content_text??"").slice(0,Ma);return a&&m===Re?`--- (middle of session omitted) ---
824
+ LIMIT ?`).all(e,Ye),r=new Map;for(let d of n)r.set(d.rid,d.content_text);for(let d of s)r.set(d.rid,d.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let i=Array.from(r.entries()).sort((d,m)=>d[0]-m[0]).map(([,d])=>({content_text:d})),o=n.length===Ae&&s.length===Ye&&r.size===Ae+Ye,a=i.map((d,m)=>{let p=(d.content_text??"").slice(0,ec);return o&&m===Ae?`--- (middle of session omitted) ---
803
825
  ${m+1}. ${p}`:`${m+1}. ${p}`}).join(`
804
- `),c=null;try{c=Ts(e)}catch(d){console.error("[autoTitle] thread context resolution failed:",d),c=null}let l=[];return c&&(l.push(Pa(c)),l.push("")),l.push(`You will receive a sample of user messages from a Claude Code session: the first ${Re}`,`messages (initial intent) and the last ${ze} 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:",o),l.join(`
805
- `)}function Pa(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let n=e.siblings.length;if(n>0){let r=e.siblings.slice(0,jt).map(a=>`"${a.title}"`).join(", "),i=n>jt?`, and ${n-jt} more`:"";t.push(`- Sibling sessions (${n}): ${r}${i}`)}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(`
806
- `)}function Wt(e,t,n){let s=t.trim();if(!s)return;let r=E(),i=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
807
- FROM sessions WHERE id = ?`).get(e);if(!i||n==="heuristic"&&i.auto_title_source==="agent"&&i.auto_title||i.auto_title===s&&i.auto_title_source===n)return;let a=Rs(i.auto_title_history),o=new Date().toISOString();i.auto_title&&i.auto_title_source&&a.push({title:i.auto_title,source:i.auto_title_source,replaced_at:o}),r.prepare(`UPDATE sessions
826
+ `),c=null;try{c=Os(e)}catch(d){console.error("[autoTitle] thread context resolution failed:",d),c=null}let l=[];return c&&(l.push(tc(c)),l.push("")),l.push(`You will receive a sample of user messages from a Claude Code session: the first ${Ae}`,`messages (initial intent) and the last ${Ye} 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:",a),l.join(`
827
+ `)}function tc(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let n=e.siblings.length;if(n>0){let r=e.siblings.slice(0,Ht).map(o=>`"${o.title}"`).join(", "),i=n>Ht?`, and ${n-Ht} more`:"";t.push(`- Sibling sessions (${n}): ${r}${i}`)}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(`
828
+ `)}function Bt(e,t,n){let s=t.trim();if(!s)return;let r=E(),i=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
829
+ FROM sessions WHERE id = ?`).get(e);if(!i||n==="heuristic"&&i.auto_title_source==="agent"&&i.auto_title||i.auto_title===s&&i.auto_title_source===n)return;let o=Ds(i.auto_title_history),a=new Date().toISOString();i.auto_title&&i.auto_title_source&&o.push({title:i.auto_title,source:i.auto_title_source,replaced_at:a}),r.prepare(`UPDATE sessions
808
830
  SET auto_title = ?,
809
831
  auto_title_source = ?,
810
832
  auto_title_generated_at = ?,
811
833
  auto_title_history = ?
812
- WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(a),e),$a(e,s,n,o)}function Ns(e){let t=E().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
813
- 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:Rs(t.auto_title_history)}:null}function Fa(){v(),Da(Ht)||Ca(Ht,{recursive:!0})}function $a(e,t,n,s){try{Fa();let r=ws(Ht,`${e}.txt`),i=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
814
- `;Ia(r,i+t+`
815
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var Ht,Re,ze,Ma,jt,Bt=b(()=>{"use strict";x();I();bs();Ut();ys();Ht=ws(y,"titles"),Re=5,ze=15,Ma=500;jt=5});function Ls(e,t){let n=Ua.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}var Ua,ks=b(()=>{"use strict";Ua=new Map});import{existsSync as ja,statSync as Ha}from"node:fs";import{delimiter as Wa,join as Ba}from"node:path";function xs(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(Wa).filter(Boolean),n=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let s of t)for(let r of n){let i=Ba(s,r);try{if(ja(i)&&Ha(i).isFile())return i}catch{}}return null}function Os(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var vs=b(()=>{"use strict"});var Fs={};be(Fs,{_resetClaudePathCacheForTests:()=>Ya,buildScanPrompt:()=>Ds,isClaudeCliAvailable:()=>Cs,runClaudeCliScan:()=>Qa,spawnClaudePrompt:()=>Ms});import{spawn as Xa}from"node:child_process";function Is(){if(he!==void 0&&Ae!==void 0)return{path:he,available:Ae};let e=xs("claude");return he=e??"claude",Ae=e!==null,{path:he,available:Ae}}function za(){return Is().path}function Cs(){return Is().available}function Ya(){he=void 0,Ae=void 0}function Ds(e){return Rt({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 Ka(e,t){let n=t.get(e);return n||e.slice(0,8)}function Ja(e){try{return Ue(e).map(n=>({id:n.id,label:n.alias&&n.alias.trim().length>0?n.alias:n.first_user_message&&n.first_user_message.trim().length>0?n.first_user_message.slice(0,60):n.id.slice(0,8)}))}catch{return[]}}function qa(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return i=>{let a=i.trim();if(!a.startsWith("{"))return;let o;try{o=JSON.parse(a)}catch{return}if(!o||typeof o!="object")return;let c=o;if(!(c.type!=="assistant"||!c.message?.content))for(let l of c.message.content){if(l?.type!=="tool_use"||l.name!=="mcp__recall__apply_tags")continue;let d=l.input,m=typeof d?.sessionId=="string"?d.sessionId:null;!m||r.has(m)||(r.add(m),Ls(t,{type:"progress",current:r.size,total:n,sessionId:m,sessionLabel:Ka(m,s)}))}}}function Va(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
834
+ WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(o),e),sc(e,s,n,a)}function Ps(e){let t=E().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
835
+ 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:Ds(t.auto_title_history)}:null}function nc(){v(),Za(Wt)||Qa(Wt,{recursive:!0})}function sc(e,t,n,s){try{nc();let r=Cs(Wt,`${e}.txt`),i=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
836
+ `;qa(r,i+t+`
837
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var Wt,Ae,Ye,ec,Ht,Xt=b(()=>{"use strict";x();I();vs();jt();Is();Wt=Cs(y,"titles"),Ae=5,Ye=15,ec=500;Ht=5});function Fs(e,t){let n=rc.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}var rc,$s=b(()=>{"use strict";rc=new Map});import{existsSync as ic,statSync as oc}from"node:fs";import{delimiter as ac,join as cc}from"node:path";function Us(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(ac).filter(Boolean),n=process.platform==="win32"?[`${e}.exe`,`${e}.cmd`,`${e}.bat`,e]:[e];for(let s of t)for(let r of n){let i=cc(s,r);try{if(ic(i)&&oc(i).isFile())return i}catch{}}return null}function js(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var Hs=b(()=>{"use strict"});var Ys={};ye(Ys,{_resetClaudePathCacheForTests:()=>pc,buildScanPrompt:()=>Xs,isClaudeCliAvailable:()=>Bs,runClaudeCliScan:()=>hc,spawnClaudePrompt:()=>Gs});import{spawn as lc}from"node:child_process";function Ws(){if(Ee!==void 0&&Ne!==void 0)return{path:Ee,available:Ne};let e=Us("claude");return Ee=e??"claude",Ne=e!==null,{path:Ee,available:Ne}}function uc(){return Ws().path}function Bs(){return Ws().available}function pc(){Ee=void 0,Ne=void 0}function Xs(e){return At({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 mc(e,t){let n=t.get(e);return n||e.slice(0,8)}function gc(e){try{return je(e).map(n=>({id:n.id,label:n.alias&&n.alias.trim().length>0?n.alias:n.first_user_message&&n.first_user_message.trim().length>0?n.first_user_message.slice(0,60):n.id.slice(0,8)}))}catch{return[]}}function fc(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return i=>{let o=i.trim();if(!o.startsWith("{"))return;let a;try{a=JSON.parse(o)}catch{return}if(!a||typeof a!="object")return;let c=a;if(!(c.type!=="assistant"||!c.message?.content))for(let l of c.message.content){if(l?.type!=="tool_use"||l.name!=="mcp__recall__apply_tags")continue;let d=l.input,m=typeof d?.sessionId=="string"?d.sessionId:null;!m||r.has(m)||(r.add(m),Fs(t,{type:"progress",current:r.size,total:n,sessionId:m,sessionLabel:mc(m,s)}))}}}function _c(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
816
838
  `);for(;s!==-1;){let r=t.slice(0,s);t=t.slice(s+1),r.length>0&&e(r),s=t.indexOf(`
817
- `)}}}async function Qa(e,t={},n){let s=!!t.scanId,r=s?Ja(e):[],i=new Map(r.map(c=>[c.id,c.label])),a=r.length,o;return s&&t.scanId&&(o=qa({scanId:t.scanId,total:a,labelTable:i})),Ps({prompt:Ds(e),allowedTools:Ga.split(","),opts:t,onProgress:n,onStdoutLine:o,outputFormat:s?"stream-json":"json"})}async function Ms(e,t,n={},s){return Ps({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function Ps(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:i,outputFormat:a}=e,o=["-p",t,"--output-format",a,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return a==="stream-json"&&o.push("--verbose"),s.model&&o.push("--model",s.model),new Promise(c=>{let l=za(),d=Xa(l,o,{stdio:["ignore","pipe","pipe"],shell:Os(l)||process.platform==="win32"&&he==="claude"}),m=[],p=[],g=i?Va(i):void 0;d.stdout.on("data",S=>{m.push(S),g&&g(S)}),d.stderr.on("data",S=>{if(p.push(S),r){let u=S.toString("utf8").trim();u&&r(u)}});let h=setTimeout(()=>{d.kill("SIGKILL")},1800*1e3);d.on("close",S=>{clearTimeout(h),c({success:S===0,stdout:Buffer.concat(m).toString("utf8"),stderr:Buffer.concat(p).toString("utf8"),exitCode:S})}),d.on("error",S=>{clearTimeout(h),c({success:!1,stdout:"",stderr:String(S),exitCode:null})})})}var Ga,he,Ae,Xt=b(()=>{"use strict";bt();At();ks();vs();Ga=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});import{existsSync as Cc}from"node:fs";import{dirname as pr}from"node:path";import{fileURLToPath as Dc}from"node:url";function Se(){if(Je)return Je;let e=pr(Dc(import.meta.url));for(;!Cc(`${e}/package.json`);){let t=pr(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return Je=e,Je}var Je,qe=b(()=>{"use strict";Je=null});function Mc(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) - or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
818
- `)}var Ve,Y,Ne=b(()=>{"use strict";Ve="BAAI/bge-base-en-v1.5",Y=class extends Error{kind;path;underlying;cause;constructor(t){super(Mc(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var et={};be(et,{embed:()=>Bc,embedQuery:()=>Xc,getEmbedderStatus:()=>Wc,loadEmbedder:()=>Hc,unloadEmbedder:()=>Gc});import{Worker as Pc}from"node:worker_threads";import{join as Fc}from"node:path";import{existsSync as $c}from"node:fs";function Uc(){return Fc(Se(),"dist","daemon","embedder-worker.js")}function mr(e){for(let t of Le.values())t.reject(e);Le.clear()}function jc(){if(se)return se;let e=Uc();if(!$c(e))throw new Y({kind:"bundle-missing",detail:"embedder-worker bundle not found - run `npm run build:cli` to emit it",path:e});let t=new Pc(e);return t.on("message",n=>{let s=Le.get(n.id);s&&(Le.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker] thread error:",n);let s=n instanceof Error?n:new Error(String(n));mr(s),se=null,Q=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),mr(new Error(`embedder worker exited with code ${n}`))),se=null,Q=!1}),se=t,t}function Qe(e){return new Promise((t,n)=>{let s;try{s=jc()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}Le.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function Ze(){return qt=qt+1>>>0,String(qt)}function Qt(e){if(!e.ok)throw e.unavailable?new Y({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function Hc(){if(!(Q&&se))try{Qt(await Qe({id:Ze(),type:"load"})),Q=!0}catch(e){throw Q=!1,e}}function Wc(){return{loaded:Q,modelId:Ve,dim:768}}async function Bc(e){if(!Q)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return Qt(await Qe({id:Ze(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function Xc(e){if(!Q)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=Qt(await Qe({id:Ze(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function Gc(){if(!se){Q=!1;return}try{await Qe({id:Ze(),type:"unload"})}catch{}Q=!1;let e=se;se=null;try{await e.terminate()}catch{}}var se,Le,qt,Q,gr=b(()=>{"use strict";qe();Ne();se=null,Le=new Map,qt=0,Q=!1});var tn={};be(tn,{LLAMACPP_MODEL_ID:()=>_r,MODEL_ID:()=>Ve,embed:()=>Zc,embedQuery:()=>el,getEmbedderStatus:()=>Qc,loadEmbedder:()=>Vc,unloadEmbedder:()=>tl});import{Worker as zc}from"node:worker_threads";import{join as Yc}from"node:path";import{existsSync as Kc}from"node:fs";function Jc(){return Yc(Se(),"dist","daemon","embedder-worker-llamacpp.js")}function fr(e){for(let t of ke.values())t.reject(e);ke.clear()}function qc(){if(re)return re;let e=Jc();if(!Kc(e))throw new Y({kind:"bundle-missing",detail:"embedder-worker-llamacpp bundle not found - run `npm run build:cli` to emit it",path:e});let t=new zc(e);return t.on("message",n=>{let s=ke.get(n.id);s&&(ke.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker-llamacpp] thread error:",n);let s=n instanceof Error?n:new Error(String(n));fr(s),re=null,Z=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),fr(new Error(`embedder worker exited with code ${n}`))),re=null,Z=!1}),re=t,t}function tt(e){return new Promise((t,n)=>{let s;try{s=qc()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}ke.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function nt(){return Zt=Zt+1>>>0,String(Zt)}function en(e){if(!e.ok)throw e.unavailable?new Y({kind:"platform-unsupported",detail:"embedder worker reports the llama.cpp runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function Vc(){if(!(Z&&re))try{en(await tt({id:nt(),type:"load"})),Z=!0}catch(e){throw Z=!1,e}}function Qc(){return{loaded:Z,modelId:_r,dim:768}}async function Zc(e){if(!Z)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return en(await tt({id:nt(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function el(e){if(!Z)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=en(await tt({id:nt(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function tl(){if(!re){Z=!1;return}try{await tt({id:nt(),type:"unload"})}catch{}Z=!1;let e=re;re=null;try{await e.terminate()}catch{}}var _r,re,ke,Zt,Z,hr=b(()=>{"use strict";qe();Ne();Ne();_r="BAAI/bge-base-en-v1.5-gguf-q8_0";re=null,ke=new Map,Zt=0,Z=!1});function nl(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?tn:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
819
- `),et)}var xe,st,Oe,sl,Mg,Pg,nn=b(()=>{"use strict";gr();hr();Ne();xe=nl(),st=xe.loadEmbedder,Oe=xe.getEmbedderStatus,sl=xe.embed,Mg=xe.embedQuery,Pg=xe.unloadEmbedder});var br=b(()=>{"use strict";x()});var yr=b(()=>{"use strict";x()});function wr(){try{return!!E().prepare("SELECT 1 AS held FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: migration_state"))return!1;throw e}}function El(){return E().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function Rr(){return{running:fl,queueDepth:El(),lastProcessedAt:_l,blacklistedCount:hl.size,pausedForMigration:wr()}}var fl,_l,hl,cn=b(()=>{"use strict";x();nn();br();yr();fl=!1,_l=null,hl=new Set});import ee from"chalk";import{formatDistanceToNowStrict as f_,parseISO as __}from"date-fns";var _,mn=b(()=>{"use strict";_={dim:ee.gray,bold:ee.bold,project:ee.cyan,user:ee.blue,assistant:ee.green,tool:ee.magenta,warn:ee.yellow,err:ee.red,ok:ee.green,accent:ee.hex("#f97316")}});var Gr=b(()=>{"use strict"});import{existsSync as ad,readFileSync as cd,writeFileSync as ld}from"node:fs";import{join as dd}from"node:path";import{z as W}from"zod";function _n(e){let t=e.trim();return!!(!t||Yr.test(t)||Kr.test(t))}function Jr(e){let t=e.trim();if(!t||Kr.test(t))return null;let n=t.replace(Yr,"").trim();return n.length>0?n:null}function fn(e,t){if(!_n(e))return e;let n=Jr(e);return n||(t&&!_n(t)?t:e)}function fd(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(i=>i.cwd&&i.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var gn,zr,ud,pd,md,gd,Yr,Kr,hn,_d,qr=b(()=>{"use strict";I();gn=dd(y,"terminals.json"),zr=1440*60*1e3,ud=3e4,pd=6e4,md=W.object({shell_pid:W.number(),tab_name:W.string(),cwd:W.string().nullable().optional(),opened_at:W.string(),last_seen_at:W.string()}),gd=W.object({schema:W.string().optional(),saved_at:W.string().optional(),terminals:W.array(md).max(500).default([]),sessions_by_pid:W.record(W.string(),W.array(W.string()).max(50)).optional().default({})}),Yr=/^[⠀-⣿✳\s]+/,Kr=/^\d+(\.\d+){1,3}$/;hn=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,!!ad(gn)))try{let t=cd(gn,"utf8"),n=JSON.parse(t),s=gd.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let i of r.terminals)this.entries.set(i.shell_pid,{shell_pid:i.shell_pid,tab_name:i.tab_name,cwd:i.cwd??null,opened_at:i.opened_at,last_seen_at:i.last_seen_at});for(let[i,a]of Object.entries(r.sessions_by_pid??{})){let o=Number(i);if(!Number.isFinite(o))continue;let c=new Set;for(let l of a)l.length>0&&c.add(l);c.size>0&&this.sessionsByPid.set(o,c)}this.gc()}catch{}}save(){try{v();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};ld(gn,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=fn(t.tab_name,s?.tab_name),i=s?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:i,last_seen_at:n};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=fn(n,s.tab_name),i={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,i),this.save(),i}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>pd?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let i of t){let a=this.entries.get(i.shell_pid),o=fn(i.tab_name,a?.tab_name),c=a?.opened_at??i.opened_at;this.entries.set(i.shell_pid,{...i,tab_name:o,opened_at:c,last_seen_at:n}),a?(a.tab_name!==o||a.cwd!==i.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=fd({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(n.kind==="pid-match"||n.kind==="singleton-cwd"){let s=this.pendingClaudeStarts.indexOf(n.entry);s>=0&&this.pendingClaudeStarts.splice(s,1)}return n}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,n,s,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:n,queued_at:Date.now(),cwd:s,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,n])=>({session_id:t,...n}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[n,s]of this.deferredLinks)s.queued_at<t&&this.deferredLinks.delete(n)}gcPending(){let t=Date.now()-ud;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(n=>{let s=Date.parse(n.started_at);return Number.isFinite(s)&&s>=t}))}outputTails=new Map;setOutputTail(t,n,s){this.ensureLoaded(),this.outputTails.set(t,{text:n,captured_at:s})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,n){this.ensureLoaded(),this.origins.set(t,n),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-zr;for(let[n,s]of this.entries){let r=Date.parse(s.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(n)}}gcOrigins(){let t=Date.now()-zr;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,i=0;for(let[a,o]of this.sessionsByPid){let c=this.entries.get(a);if(c){let l=Date.parse(c.last_seen_at);if(Number.isFinite(l)&&l>=s)continue}i+=o.size,this.sessionsByPid.delete(a),c&&(this.entries.delete(a),r++)}return(r||i)&&this.save(),{pruned_pids:r,pruned_sessions:i}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let i=this.sessionsByPid.get(s);i&&(n+=i.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},_d=new hn});import{execFile as hd}from"node:child_process";import{promisify as Ed}from"node:util";var N_,Vr=b(()=>{"use strict";N_=Ed(hd)});import{execFile as Td}from"node:child_process";import{promisify as bd}from"node:util";var M_,P_,Qr=b(()=>{"use strict";qr();He();x();Vr();M_=bd(Td),P_=3600*1e3});var Zr=b(()=>{"use strict"});import{existsSync as yd,mkdirSync as j_,readFileSync as wd,writeFileSync as H_}from"node:fs";import{homedir as Rd}from"node:os";import{join as ei}from"node:path";import{z as te}from"zod";function Ad(){return process.env.RECALL_HOME??ei(Rd(),".recall")}function Nd(){return ei(Ad(),"config.json")}function kd(){let e=Nd();if(!yd(e))return{};try{return JSON.parse(wd(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function Sn(){let e=kd().semantic;if(!e)return{...En};let t=Ld.safeParse({...En,...e});return t.success?t.data:{...En}}var Ld,En,Tn=b(()=>{"use strict";ht();x();I();Ld=te.object({enabled:te.boolean().default(!1),model:te.string().optional(),ratePerMinute:te.number().int().min(1).max(600).default(30),lastProcessedSessionId:te.string().nullable().default(null),backfillPaused:te.boolean().default(!1),autoExtractEnabled:te.boolean().default(!1),autoExtractIntervalMinutes:te.number().int().min(5).max(720).default(60),autoExtractBatchSize:te.number().int().min(1).max(20).default(1),autoResumeWorker:te.boolean().default(!1)}),En={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1}});var ti=b(()=>{"use strict";x();Xt();Tn()});var ni=b(()=>{"use strict"});import{execFile as xd}from"node:child_process";import{promisify as Od}from"node:util";var rh,si=b(()=>{"use strict";x();rh=Od(xd)});import{z as bn}from"zod";var ah,ri=b(()=>{"use strict";ah=bn.object({heuristicEnabled:bn.boolean().default(!0),agentEnabled:bn.boolean().default(!1)})});import{basename as uh,join as yn}from"node:path";var ii,fh,_h,oi=b(()=>{"use strict";x();I();It();ii=yn(y,"auto-rules"),fh=yn(ii,"rules.json"),_h=yn(ii,"suggestions.json")});var ai=b(()=>{"use strict"});var ci=b(()=>{"use strict"});var li=b(()=>{"use strict"});var di=b(()=>{"use strict";li()});var ui=b(()=>{"use strict"});import{watch as Hh}from"chokidar";import{readdirSync as Id,statSync as Bh}from"node:fs";import{basename as Kh,join as Cd}from"node:path";function pi(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}function mi(e){return e.replace(/\\/g,"/").includes("/subagents/")}function*wn(e){let t;try{t=Id(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=Cd(e,n.name);n.isDirectory()?yield*wn(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}var gi=b(()=>{"use strict";I();x();Gr();Qr();Zr();ti();ni();si();Bt();ri();oi();Ut();ai();He();ci();di();cn();ui()});import{execFileSync as hi}from"node:child_process";function Md(e){for(let t of Dd)if(e.includes(t))return!0;return!1}function Ie(e={}){let t=e.psOutput??Pd(),n=e.isProcessAlive??Fd,s=e.getParentCommand??$d,r=[];for(let i of t.split(`
820
- `)){let a=i.trim();if(!a||!Md(a)||Ud(a))continue;let o=a.split(/\s+/);if(o.length<5)continue;let c=Number(o[0]),l=Number(o[1]),d=o[2],m=o[3],p=o[4],g=0,h=0;if(/^\d+$/.test(m)&&(p.includes(".")||/^\d+$/.test(p))?(g=Number(m),h=Number(p)):h=Number(m),!Number.isFinite(c)||!Number.isFinite(l))continue;let u=l>1&&n(l);r.push({pid:c,ppid:l,parentAlive:u,etimeSeconds:jd(d),pcpu:Number.isFinite(h)?h:0,rssKb:Number.isFinite(g)?g:0,orphan:!u,parentCommand:u?s(l):null})}return r}function Pd(){try{return hi("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -axo failed: ${t}
821
- `),""}}function Fd(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function $d(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=hi("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return n.length?n.slice(0,200):null}catch{return null}}function Ud(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=[t[5]??"",t[4]??""];for(let s of n)if(s&&(s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"))return!0;return!1}function jd(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=fi(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(fi),i=0,a=0,o=0;return r.length===3?[i,a,o]=r:r.length===2?[a,o]=r:r.length===1&&(o=r[0]),t*86400+i*3600+a*60+o}function fi(e){let t=Number(e);return Number.isFinite(t)?t:0}function ct(e){return e.pcpu>=Hd&&e.etimeSeconds>=Wd}function Ei(e){let t=e??Ie(),n=t.length,s=t.reduce((l,d)=>l+(Number.isFinite(d.rssKb)&&d.rssKb>0?d.rssKb:0),0),r=n>_i,i=s>Bd;if(!(r||i))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,message:null};let o=[];r&&o.push(`${n} MCP children (threshold ${_i})`),i&&o.push(`${Xd(s)} aggregate RSS (threshold 1 GiB)`);let c=`Zombie MCP threshold breached: ${o.join(" + ")}. Each MCP child holds a SQLite read connection and can pin WAL checkpoints. Run \`recall mcp-prune --all\` to reap (note: pre-2026-05-23 builds may miss processes from stale install paths -- \`pkill -f mcp-server.js\` is the nuclear option).`;return{flagged:!0,severity:"high",count:n,aggregateRssKb:s,message:c}}function Xd(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}var Dd,Hd,Wd,_i,Bd,lt=b(()=>{"use strict";Dd=["dist/mcp-server.js","dist/mcp/server.js"];Hd=50,Wd=60;_i=4,Bd=1024*1024});import{execFileSync as Gd}from"node:child_process";function Yd(e){for(let t of zd)if(e.includes(t))return!0;return!1}function Kd(e){if(e.length<5)return!1;let t=[e[5]??"",e[4]??""];for(let n of t)if(n&&(n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"))return!0;return!1}function dt(e={}){let t=e.psOutput??Jd(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
822
- `)){let i=r.trim();if(!i||!Yd(i))continue;let a=i.split(/\s+/);if(Kd(a)||a.length<5)continue;let o=Number(a[0]),c=Number(a[1]),l=a[2];!Number.isFinite(o)||!Number.isFinite(c)||n.has(o)||s.push({pid:o,ppid:c,etimeSeconds:qd(l),etime:l,command:i})}return s.sort((r,i)=>r.etimeSeconds-i.etimeSeconds),s}function Jd(){try{return Gd("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] ps -axo failed: ${t}
823
- `),""}}function qd(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Si(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Si),i=0,a=0,o=0;return r.length===3?[i,a,o]=r:r.length===2?[a,o]=r:r.length===1&&(o=r[0]),t*86400+i*3600+a*60+o}function Si(e){let t=Number(e);return Number.isFinite(t)?t:0}function Rn(e,t=new Date){if(!Number.isFinite(e)||e<0)return null;let n=t.getTime()-e*1e3;if(!Number.isFinite(n))return null;let s=new Date(n),r=String(s.getHours()).padStart(2,"0"),i=String(s.getMinutes()).padStart(2,"0"),a=String(s.getSeconds()).padStart(2,"0");return`${r}:${i}:${a}`}var zd,An=b(()=>{"use strict";zd=["dist/daemon/entrypoint.js"]});import{join as Vd}from"node:path";var hE,Nn=b(()=>{"use strict";I();hE=Vd(y,"daemon.log")});import{join as Zd}from"node:path";var Ti,bi=b(()=>{"use strict";I();Nn();Ti=Zd(y,"daemon.token")});import{basename as RE,join as Ln}from"node:path";function yi(){return{pid:eu,port:tu,token:Ti}}function wi(e){return nu(e)}function nu(e){try{return process.kill(e,0),!0}catch{return!1}}var eu,tu,xE,Ri=b(()=>{"use strict";I();bi();An();Nn();eu=Ln(y,"daemon.pid"),tu=Ln(y,"daemon.port"),xE=Ln(y,"daemon.log")});var CE,Ai,Ni=b(()=>{"use strict";ye();lt();CE=5*6e4,Ai=1073741824});import{existsSync as su,readFileSync as ru,renameSync as Li,writeFileSync as iu}from"node:fs";import{join as ou}from"node:path";function ki(){return ou(y,"doctor-state.json")}function xi(){let e=ki();if(!su(e))return{chunkQueue:{samples:[]}};let t;try{t=ru(e,"utf8")}catch{return{chunkQueue:{samples:[]}}}try{let n=JSON.parse(t),s=n.chunkQueue?.samples,r=Array.isArray(s)?s.filter(c=>typeof c=="object"&&c!==null&&typeof c.ts=="string"&&typeof c.size=="number"&&typeof c.semanticEnabled=="boolean"):[],i=n.autoPruneCounters?.events,a=Array.isArray(i)?i.filter(c=>{if(!c||typeof c!="object")return!1;let l=c;return!(typeof l.ts!="string"||typeof l.pid!="number"||!Number.isFinite(l.pid)||l.action!=="would_kill"&&l.action!=="killed"&&l.action!=="failed"||l.reason!=="orphan_10min"&&l.reason!=="runaway_cpu_5min")}):[],o={chunkQueue:{samples:r}};return(a.length>0||i!==void 0)&&(o.autoPruneCounters={events:a}),o}catch{try{Li(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function Oi(e){let t=ki(),n=`${t}.tmp`;try{iu(n,JSON.stringify(e,null,2),{mode:384}),Li(n,t)}catch{}}function kn(){let e=E(),t=0;try{t=e.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let n=!1;try{n=e.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}let s=xi(),r=Date.now(),i=s.chunkQueue.samples,a=i.map(h=>({s:h,ms:Date.parse(h.ts)})).filter(h=>Number.isFinite(h.ms)&&r-h.ms<=cu).sort((h,S)=>h.ms-S.ms),o=null;a.length>0&&(o=t-a[0].s.size);let c=a.length,d={chunkQueue:{samples:[...i,{ts:new Date(r).toISOString(),size:t,semanticEnabled:n}].slice(-au)}};s.autoPruneCounters&&(d.autoPruneCounters=s.autoPruneCounters),Oi(d);let m="ok",p=`chunk_queue growth: ok (current ${t.toLocaleString()} row${t===1?"":"s"}, semantic ${n?"on":"off"}, ${c} prior sample${c===1?"":"s"} in last hour).`,g=null;return t>lu&&!n&&o!==null&&o>du?(m="critical",p=`chunk_queue growth: CRITICAL \u2014 ${t.toLocaleString()} rows with semantic disabled, grew by ${o.toLocaleString()} in the last hour. Schema-sync race shape.`,g="Schema-sync race likely \u2014 Phase 1.1 fix shipped on `feat/daemon-state-integrity`; if rows persist after a clean restart, re-investigate. Inspect with `sqlite3 ~/.recall/db.sqlite 'SELECT action, COUNT(*) FROM chunk_queue GROUP BY action;'`."):t>uu?(m="high",p=`chunk_queue growth: HIGH \u2014 ${t.toLocaleString()} rows pending (semantic ${n?"on":"off"}`+(o!==null?`, last-hour \u0394 ${o>=0?"+":""}${o.toLocaleString()}`:"")+").",g="Queue is large but stable \u2014 operator-authorized `recall semantic backfill` (or the daemon's embed worker, if intentionally on) would drain."):o!==null&&o>pu&&(m="medium",p=`chunk_queue growth: MEDIUM \u2014 grew by ${o.toLocaleString()} in the last hour (current ${t.toLocaleString()}, semantic ${n?"on":"off"}).`,g="Growth is fast even though the absolute size is small. Re-run `recall doctor` in 10\u201330 min; if growth continues without semantic on, investigate the schema-sync gate."),{status:m,currentSize:t,semanticEnabled:n,lastHourGrowth:o,priorSampleCount:c,message:p,remediation:g}}function vi(e={}){let t=e.now??Date.now(),n=xi(),s=n.autoPruneCounters?.events??[],r=s.filter(l=>{let d=Date.parse(l.ts);return Number.isFinite(d)&&t-d<=mu});if(r.length!==s.length)try{let l={chunkQueue:n.chunkQueue,autoPruneCounters:{events:r}};Oi(l)}catch{}let i={orphan_10min:0,runaway_cpu_5min:0},a=0,o=0,c=0;for(let l of r)i[l.reason]+=1,l.action==="would_kill"?a+=1:l.action==="killed"?o+=1:c+=1;return{wouldHaveKilled:a,killed:o,failed:c,byReason:i}}var au,cu,lu,du,uu,pu,mu,xn=b(()=>{"use strict";I();x();au=24,cu=3600*1e3,lu=1e3,du=1e3,uu=1e4,pu=5e3,mu=1440*60*1e3});function Ci(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return Ii;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:Ii}var Ii,Di=b(()=>{"use strict";lt();xn();Ii="dry-run"});import{existsSync as gu,readFileSync as fu,renameSync as Pi,writeFileSync as _u}from"node:fs";import{join as hu}from"node:path";function Fi(){return hu(y,"doctor-alerts.json")}function $i(){let e=Fi();if(!gu(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=fu(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return Mi(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return Mi(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter(Eu):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function Mi(e){try{Pi(e,`${e}.corrupt.${Date.now()}`)}catch{}}function Eu(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&typeof t.check=="string"&&(t.severity==="critical"||t.severity==="high"||t.severity==="medium")&&typeof t.message=="string"&&typeof t.remediation=="string"&&typeof t.firstSeenAt=="string"&&typeof t.lastSeenAt=="string"&&typeof t.seenCount=="number"&&typeof t.acknowledged=="boolean"}function Ui(e){let t=Fi(),n=`${t}.tmp`;try{_u(n,JSON.stringify(e,null,2),{mode:384}),Pi(n,t)}catch{}}function ji(e,t){let n=t.trim().toLowerCase();if(n.length<4)return{file:e,result:{outcome:"not-found"}};let s=e.alerts.filter(o=>o.id.startsWith(n));if(s.length===0)return{file:e,result:{outcome:"not-found"}};if(s.length>1)return{file:e,result:{outcome:"ambiguous",candidates:s.map(o=>o.id)}};let r=s[0];if(r.acknowledged)return{file:e,result:{outcome:"already-acknowledged",matched:r}};let i={...r,acknowledged:!0},a=e.alerts.map(o=>o.id===r.id?i:o);return{file:{...e,alerts:a},result:{outcome:"acknowledged",matched:i}}}var Hi=b(()=>{"use strict";I()});var ao={};be(ao,{WATCHER_REFLAG_CRITICAL:()=>zi,buildHealthReport:()=>Zi,buildPipelineDiagnostic:()=>qi,checkChunkQueueGrowth:()=>kn,checkDaemonSiblings:()=>eo,checkDaemonStateFiles:()=>to,checkDiskPressureAndBackups:()=>Vi,checkIngestStaleness:()=>ro,checkSemanticGateDrift:()=>oo,checkStaleClaudeJsonMcpPaths:()=>io,checkWatcherReflagLoop:()=>Yi,countBackupOrphans:()=>so,detectLabelCollisions:()=>Qi,getFreeDiskBytes:()=>Pu,renderMigrationDoctorSection:()=>no,runDoctor:()=>Mu});import{existsSync as Ce,readdirSync as Su,readFileSync as On,statSync as ut,statfsSync as Xi}from"node:fs";import{homedir as Tu}from"node:os";import{join as pt}from"node:path";import*as Gi from"node:http";function wu(e){let t=[];for(let n of e)if(n.alias){if(yu.test(n.alias)){t.push({session_id:n.session_id,alias:n.alias,violation:"fabricated-origin-label",cwd:n.cwd});continue}if(n.cwd){let s=n.cwd.replace(/\/+$/,"").split("/").pop();s&&n.alias.startsWith(`${s} \xB7 `)&&t.push({session_id:n.session_id,alias:n.alias,violation:"fabricated-cwd-branch",cwd:n.cwd})}}return t}function Yi(e,t=zi){let n=[];for(let s of e){if(s.count<=t)continue;let r=s.path.replace(/'/g,"''");n.push(`Watcher reindexed ${s.path} ${s.count.toLocaleString()} times in the last hour \u2014 reflag loop. Mark it skipped with a SQL one-liner:
824
- sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${r}';"`)}return n}function Ru(){let e=pt(y,"daemon.port");if(!Ce(e))return null;try{let t=On(e,"utf8").trim();if(t.startsWith("{")){let s=JSON.parse(t);return typeof s.port=="number"?s.port:null}let n=Number.parseInt(t,10);return Number.isFinite(n)&&n>0&&n<65536?n:null}catch{return null}}function Au(e,t,n=1500){return new Promise(s=>{let r=Gi.request({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:n,headers:{host:"127.0.0.1","user-agent":"recall-doctor"}},i=>{let a=[];i.on("data",o=>a.push(Buffer.isBuffer(o)?o:Buffer.from(o))),i.on("end",()=>{if(!i.statusCode||i.statusCode<200||i.statusCode>=300){s(null);return}try{s(JSON.parse(Buffer.concat(a).toString("utf8")))}catch{s(null)}})});r.on("error",()=>s(null)),r.on("timeout",()=>{r.destroy(),s(null)}),r.end()})}function Nu(){let e=pt(y,"terminals.json");if(!Ce(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=ut(e),n=Math.floor((Date.now()-t.mtimeMs)/1e3);return{exists:!0,mtimeMs:t.mtimeMs,ageSeconds:n}}catch{return{exists:!1,mtimeMs:null,ageSeconds:null}}}function Lu(){let e=new Date(Date.now()-vn*36e5).toISOString();try{let t=E().prepare(`SELECT
839
+ `)}}}async function hc(e,t={},n){let s=!!t.scanId,r=s?gc(e):[],i=new Map(r.map(c=>[c.id,c.label])),o=r.length,a;return s&&t.scanId&&(a=fc({scanId:t.scanId,total:o,labelTable:i})),zs({prompt:Xs(e),allowedTools:dc.split(","),opts:t,onProgress:n,onStdoutLine:a,outputFormat:s?"stream-json":"json"})}async function Gs(e,t,n={},s){return zs({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function zs(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:i,outputFormat:o}=e,a=["-p",t,"--output-format",o,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return o==="stream-json"&&a.push("--verbose"),s.model&&a.push("--model",s.model),new Promise(c=>{let l=uc(),d=lc(l,a,{stdio:["ignore","pipe","pipe"],shell:js(l)||process.platform==="win32"&&Ee==="claude"}),m=[],p=[],g=i?_c(i):void 0;d.stdout.on("data",S=>{m.push(S),g&&g(S)}),d.stderr.on("data",S=>{if(p.push(S),r){let u=S.toString("utf8").trim();u&&r(u)}});let h=setTimeout(()=>{d.kill("SIGKILL")},1800*1e3);d.on("close",S=>{clearTimeout(h),c({success:S===0,stdout:Buffer.concat(m).toString("utf8"),stderr:Buffer.concat(p).toString("utf8"),exitCode:S})}),d.on("error",S=>{clearTimeout(h),c({success:!1,stdout:"",stderr:String(S),exitCode:null})})})}var dc,Ee,Ne,Gt=b(()=>{"use strict";yt();Nt();$s();Hs();dc=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});import{existsSync as Qc}from"node:fs";import{dirname as yr}from"node:path";import{fileURLToPath as Zc}from"node:url";function Te(){if(Ve)return Ve;let e=yr(Zc(import.meta.url));for(;!Qc(`${e}/package.json`);){let t=yr(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return Ve=e,Ve}var Ve,qe=b(()=>{"use strict";Ve=null});function Qe(e){let t=null;return()=>t||(t=(async()=>{try{return await e()}finally{t=null}})(),t)}var qt=b(()=>{"use strict"});function el(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) - or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
840
+ `)}var Ze,Y,Le=b(()=>{"use strict";Ze="BAAI/bge-base-en-v1.5",Y=class extends Error{kind;path;underlying;cause;constructor(t){super(el(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var nt={};ye(nt,{embed:()=>ll,embedQuery:()=>dl,getEmbedderStatus:()=>cl,loadEmbedder:()=>al,unloadEmbedder:()=>ul});import{Worker as tl}from"node:worker_threads";import{join as nl}from"node:path";import{existsSync as sl}from"node:fs";function rl(){return nl(Te(),"dist","daemon","embedder-worker.js")}function wr(e){for(let t of ke.values())t.reject(e);ke.clear()}function il(){if(se)return se;let e=rl();if(!sl(e))throw new Y({kind:"bundle-missing",detail:"embedder-worker bundle not found - run `npm run build:cli` to emit it",path:e});let t=new tl(e);return t.on("message",n=>{let s=ke.get(n.id);s&&(ke.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker] thread error:",n);let s=n instanceof Error?n:new Error(String(n));wr(s),se=null,Q=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),wr(new Error(`embedder worker exited with code ${n}`))),se=null,Q=!1}),se=t,t}function et(e){return new Promise((t,n)=>{let s;try{s=il()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}ke.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function tt(){return Qt=Qt+1>>>0,String(Qt)}function en(e){if(!e.ok)throw e.unavailable?new Y({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function al(){if(!(Q&&se))try{await ol(),Q=!0}catch(e){throw Q=!1,e}}function cl(){return{loaded:Q,modelId:Ze,dim:768}}async function ll(e){if(!Q)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return en(await et({id:tt(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function dl(e){if(!Q)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=en(await et({id:tt(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function ul(){if(!se){Q=!1;return}try{await et({id:tt(),type:"unload"})}catch{}Q=!1;let e=se;se=null;try{await e.terminate()}catch{}}var se,ke,Qt,Q,ol,Rr=b(()=>{"use strict";qe();qt();Le();se=null,ke=new Map,Qt=0,Q=!1;ol=Qe(async()=>{en(await et({id:tt(),type:"load"}))})});var sn={};ye(sn,{LLAMACPP_MODEL_ID:()=>Nr,MODEL_ID:()=>Ze,embed:()=>Tl,embedQuery:()=>bl,getEmbedderStatus:()=>Sl,loadEmbedder:()=>El,unloadEmbedder:()=>yl});import{Worker as pl}from"node:worker_threads";import{join as ml}from"node:path";import{existsSync as gl}from"node:fs";function fl(){return ml(Te(),"dist","daemon","embedder-worker-llamacpp.js")}function Ar(e){for(let t of xe.values())t.reject(e);xe.clear()}function _l(){if(re)return re;let e=fl();if(!gl(e))throw new Y({kind:"bundle-missing",detail:"embedder-worker-llamacpp bundle not found - run `npm run build:cli` to emit it",path:e});let t=new pl(e);return t.on("message",n=>{let s=xe.get(n.id);s&&(xe.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker-llamacpp] thread error:",n);let s=n instanceof Error?n:new Error(String(n));Ar(s),re=null,Z=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),Ar(new Error(`embedder worker exited with code ${n}`))),re=null,Z=!1}),re=t,t}function st(e){return new Promise((t,n)=>{let s;try{s=_l()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}xe.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function rt(){return tn=tn+1>>>0,String(tn)}function nn(e){if(!e.ok)throw e.unavailable?new Y({kind:"platform-unsupported",detail:"embedder worker reports the llama.cpp runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function El(){if(!(Z&&re))try{await hl(),Z=!0}catch(e){throw Z=!1,e}}function Sl(){return{loaded:Z,modelId:Nr,dim:768}}async function Tl(e){if(!Z)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return nn(await st({id:rt(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function bl(e){if(!Z)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=nn(await st({id:rt(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function yl(){if(!re){Z=!1;return}try{await st({id:rt(),type:"unload"})}catch{}Z=!1;let e=re;re=null;try{await e.terminate()}catch{}}var Nr,re,xe,tn,Z,hl,Lr=b(()=>{"use strict";qe();qt();Le();Le();Nr="BAAI/bge-base-en-v1.5-gguf-q8_0";re=null,xe=new Map,tn=0,Z=!1;hl=Qe(async()=>{nn(await st({id:rt(),type:"load"}))})});function wl(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?sn:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
841
+ `),nt)}var Oe,rn,ve,Rl,df,uf,on=b(()=>{"use strict";Rr();Lr();Le();Oe=wl(),rn=Oe.loadEmbedder,ve=Oe.getEmbedderStatus,Rl=Oe.embed,df=Oe.embedQuery,uf=Oe.unloadEmbedder});var vr=b(()=>{"use strict";x()});var Ir=b(()=>{"use strict";x()});function Cr(){try{return!!E().prepare("SELECT 1 AS held FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: migration_state"))return!1;throw e}}function Ul(){return E().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function Dr(){return{running:Pl,queueDepth:Ul(),lastProcessedAt:Fl,blacklistedCount:$l.size,pausedForMigration:Cr()}}var Pl,Fl,$l,un=b(()=>{"use strict";x();on();vr();Ir();Pl=!1,Fl=null,$l=new Set});import ee from"chalk";import{formatDistanceToNowStrict as G_,parseISO as z_}from"date-fns";var _,hn=b(()=>{"use strict";_={dim:ee.gray,bold:ee.bold,project:ee.cyan,user:ee.blue,assistant:ee.green,tool:ee.magenta,warn:ee.yellow,err:ee.red,ok:ee.green,accent:ee.hex("#f97316")}});var ti=b(()=>{"use strict"});var ni=b(()=>{"use strict"});var si=b(()=>{"use strict"});import{existsSync as Ld,readFileSync as kd,writeFileSync as xd}from"node:fs";import{join as Od}from"node:path";import{z as W}from"zod";function Tn(e){let t=e.trim();return!!(!t||ii.test(t)||oi.test(t))}function ai(e){let t=e.trim();if(!t||oi.test(t))return null;let n=t.replace(ii,"").trim();return n.length>0?n:null}function Sn(e,t){if(!Tn(e))return e;let n=ai(e);return n||(t&&!Tn(t)?t:e)}function Md(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(i=>i.cwd&&i.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var En,ri,vd,Id,Cd,Dd,ii,oi,bn,Pd,ci=b(()=>{"use strict";I();En=Od(y,"terminals.json"),ri=1440*60*1e3,vd=3e4,Id=6e4,Cd=W.object({shell_pid:W.number(),tab_name:W.string(),cwd:W.string().nullable().optional(),opened_at:W.string(),last_seen_at:W.string()}),Dd=W.object({schema:W.string().optional(),saved_at:W.string().optional(),terminals:W.array(Cd).max(500).default([]),sessions_by_pid:W.record(W.string(),W.array(W.string()).max(50)).optional().default({})}),ii=/^[⠀-⣿✳\s]+/,oi=/^\d+(\.\d+){1,3}$/;bn=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,!!Ld(En)))try{let t=kd(En,"utf8"),n=JSON.parse(t),s=Dd.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let i of r.terminals)this.entries.set(i.shell_pid,{shell_pid:i.shell_pid,tab_name:i.tab_name,cwd:i.cwd??null,opened_at:i.opened_at,last_seen_at:i.last_seen_at});for(let[i,o]of Object.entries(r.sessions_by_pid??{})){let a=Number(i);if(!Number.isFinite(a))continue;let c=new Set;for(let l of o)l.length>0&&c.add(l);c.size>0&&this.sessionsByPid.set(a,c)}this.gc()}catch{}}save(){try{v();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};xd(En,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=Sn(t.tab_name,s?.tab_name),i=s?.opened_at??t.opened_at,o={...t,tab_name:r,opened_at:i,last_seen_at:n};return this.entries.set(t.shell_pid,o),this.gc(),this.save(),o}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=Sn(n,s.tab_name),i={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,i),this.save(),i}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>Id?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let i of t){let o=this.entries.get(i.shell_pid),a=Sn(i.tab_name,o?.tab_name),c=o?.opened_at??i.opened_at;this.entries.set(i.shell_pid,{...i,tab_name:a,opened_at:c,last_seen_at:n}),o?(o.tab_name!==a||o.cwd!==i.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=Md({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(n.kind==="pid-match"||n.kind==="singleton-cwd"){let s=this.pendingClaudeStarts.indexOf(n.entry);s>=0&&this.pendingClaudeStarts.splice(s,1)}return n}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,n,s,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:n,queued_at:Date.now(),cwd:s,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,n])=>({session_id:t,...n}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[n,s]of this.deferredLinks)s.queued_at<t&&this.deferredLinks.delete(n)}gcPending(){let t=Date.now()-vd;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(n=>{let s=Date.parse(n.started_at);return Number.isFinite(s)&&s>=t}))}outputTails=new Map;setOutputTail(t,n,s){this.ensureLoaded(),this.outputTails.set(t,{text:n,captured_at:s})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,n){this.ensureLoaded(),this.origins.set(t,n),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-ri;for(let[n,s]of this.entries){let r=Date.parse(s.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(n)}}gcOrigins(){let t=Date.now()-ri;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,i=0;for(let[o,a]of this.sessionsByPid){let c=this.entries.get(o);if(c){let l=Date.parse(c.last_seen_at);if(Number.isFinite(l)&&l>=s)continue}i+=a.size,this.sessionsByPid.delete(o),c&&(this.entries.delete(o),r++)}return(r||i)&&this.save(),{pruned_pids:r,pruned_sessions:i}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(o){o.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let i=this.sessionsByPid.get(s);i&&(n+=i.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},Pd=new bn});import{execFile as Fd}from"node:child_process";import{promisify as $d}from"node:util";var rh,li=b(()=>{"use strict";rh=$d(Fd)});import{execFile as jd}from"node:child_process";import{promisify as Hd}from"node:util";var mh,gh,di=b(()=>{"use strict";ci();We();x();li();mh=Hd(jd),gh=3600*1e3});var _h,ui=b(()=>{"use strict";_h=64*1024});import{existsSync as Wd,mkdirSync as Sh,readFileSync as Bd,writeFileSync as Th}from"node:fs";import{homedir as Xd}from"node:os";import{join as pi}from"node:path";import{z as te}from"zod";function Gd(){return process.env.RECALL_HOME??pi(Xd(),".recall")}function zd(){return pi(Gd(),"config.json")}function Kd(){let e=zd();if(!Wd(e))return{};try{return JSON.parse(Bd(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function wn(){let e=Kd().semantic;if(!e)return{...yn};let t=Yd.safeParse({...yn,...e});return t.success?t.data:{...yn}}var Yd,yn,Rn=b(()=>{"use strict";Et();x();I();Yd=te.object({enabled:te.boolean().default(!1),model:te.string().optional(),ratePerMinute:te.number().int().min(1).max(600).default(30),lastProcessedSessionId:te.string().nullable().default(null),backfillPaused:te.boolean().default(!1),autoExtractEnabled:te.boolean().default(!1),autoExtractIntervalMinutes:te.number().int().min(5).max(720).default(60),autoExtractBatchSize:te.number().int().min(1).max(20).default(1),autoResumeWorker:te.boolean().default(!1)}),yn={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1}});var mi=b(()=>{"use strict";x();Gt();Rn()});var gi=b(()=>{"use strict"});import{execFile as Jd}from"node:child_process";import{promisify as Vd}from"node:util";var Fh,fi=b(()=>{"use strict";x();Fh=Vd(Jd)});import{z as An}from"zod";var jh,_i=b(()=>{"use strict";jh=An.object({heuristicEnabled:An.boolean().default(!0),agentEnabled:An.boolean().default(!1)})});import{basename as Xh,join as Nn}from"node:path";var hi,Kh,Jh,Ei=b(()=>{"use strict";x();I();Ct();hi=Nn(y,"auto-rules"),Kh=Nn(hi,"rules.json"),Jh=Nn(hi,"suggestions.json")});var Si=b(()=>{"use strict"});var Ti=b(()=>{"use strict"});var bi=b(()=>{"use strict"});var yi=b(()=>{"use strict";bi()});var wi=b(()=>{"use strict"});var Ri=b(()=>{"use strict"});function Ai(e){return e.replace(/\\/g,"/").includes("/subagents/")}function Ni(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}var Li=b(()=>{"use strict"});import{watch as xE}from"chokidar";import{readdirSync as Qd,statSync as vE}from"node:fs";import{basename as UE,join as Zd}from"node:path";function*Ln(e){let t;try{t=Qd(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=Zd(e,n.name);n.isDirectory()?yield*Ln(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}var ki,xi,Oi=b(()=>{"use strict";I();x();ti();ni();si();di();ui();mi();gi();fi();Xt();_i();Ei();jt();Si();We();Ti();yi();un();wi();Ri();Li();ki=Ni,xi=Ai});import{execFileSync as Di}from"node:child_process";function tu(e){for(let t of eu)if(e.includes(t))return!0;return!1}function Ie(e={}){let t=e.psOutput??nu(),n=e.isProcessAlive??su,s=e.getParentCommand??ru,r=[];for(let i of t.split(`
842
+ `)){let o=i.trim();if(!o||!tu(o)||iu(o))continue;let a=o.split(/\s+/);if(a.length<5)continue;let c=Number(a[0]),l=Number(a[1]),d=a[2],m=a[3],p=a[4],g=0,h=0;if(/^\d+$/.test(m)&&(p.includes(".")||/^\d+$/.test(p))?(g=Number(m),h=Number(p)):h=Number(m),!Number.isFinite(c)||!Number.isFinite(l))continue;let u=l>1&&n(l);r.push({pid:c,ppid:l,parentAlive:u,etimeSeconds:ou(d),pcpu:Number.isFinite(h)?h:0,rssKb:Number.isFinite(g)?g:0,orphan:!u,parentCommand:u?s(l):null})}return r}function nu(){try{return Di("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -axo failed: ${t}
843
+ `),""}}function su(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function ru(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=Di("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return n.length?n.slice(0,200):null}catch{return null}}function iu(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=[t[5]??"",t[4]??""];for(let s of n)if(s&&(s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"))return!0;return!1}function ou(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=vi(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(vi),i=0,o=0,a=0;return r.length===3?[i,o,a]=r:r.length===2?[o,a]=r:r.length===1&&(a=r[0]),t*86400+i*3600+o*60+a}function vi(e){let t=Number(e);return Number.isFinite(t)?t:0}function lt(e){return e.pcpu>=au&&e.etimeSeconds>=cu}function Ci(e){return e.reduce((t,n)=>t+(Number.isFinite(n.rssKb)&&n.rssKb>0?n.rssKb:0),0)}function Mi(e){let t=e??Ie(),n=t.length,s=Ci(t),r=t.filter(p=>p.orphan),i=r.length,o=Ci(r),a=i>Ii,c=o>lu;if(!(a||c))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,orphanCount:i,orphanRssKb:o,message:null};let d=[];a&&d.push(`${i} orphaned MCP children (threshold ${Ii})`),c&&d.push(`${du(o)} aggregate RSS across orphaned children (threshold 1 GiB)`);let m=`Zombie MCP threshold breached: ${d.join(" + ")}. Each orphaned MCP child (its parent claude/VS Code tab has exited) still holds a SQLite read connection and can pin WAL checkpoints. Run \`recall mcp-prune --all\` to reap (note: pre-2026-05-23 builds may miss processes from stale install paths -- \`pkill -f mcp-server.js\` is the nuclear option).`;return{flagged:!0,severity:"high",count:n,aggregateRssKb:s,orphanCount:i,orphanRssKb:o,message:m}}function du(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}var eu,au,cu,Ii,lu,dt=b(()=>{"use strict";eu=["dist/mcp-server.js","dist/mcp/server.js"];au=50,cu=60;Ii=4,lu=1024*1024});import{execFileSync as uu}from"node:child_process";function mu(e){for(let t of pu)if(e.includes(t))return!0;return!1}function gu(e){if(e.length<5)return!1;let t=[e[5]??"",e[4]??""];for(let n of t)if(n&&(n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"))return!0;return!1}function ut(e={}){let t=e.psOutput??fu(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
844
+ `)){let i=r.trim();if(!i||!mu(i))continue;let o=i.split(/\s+/);if(gu(o)||o.length<5)continue;let a=Number(o[0]),c=Number(o[1]),l=o[2];!Number.isFinite(a)||!Number.isFinite(c)||n.has(a)||s.push({pid:a,ppid:c,etimeSeconds:_u(l),etime:l,command:i})}return s.sort((r,i)=>r.etimeSeconds-i.etimeSeconds),s}function fu(){try{return uu("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] ps -axo failed: ${t}
845
+ `),""}}function _u(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Pi(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Pi),i=0,o=0,a=0;return r.length===3?[i,o,a]=r:r.length===2?[o,a]=r:r.length===1&&(a=r[0]),t*86400+i*3600+o*60+a}function Pi(e){let t=Number(e);return Number.isFinite(t)?t:0}function kn(e,t=new Date){if(!Number.isFinite(e)||e<0)return null;let n=t.getTime()-e*1e3;if(!Number.isFinite(n))return null;let s=new Date(n),r=String(s.getHours()).padStart(2,"0"),i=String(s.getMinutes()).padStart(2,"0"),o=String(s.getSeconds()).padStart(2,"0");return`${r}:${i}:${o}`}var pu,xn=b(()=>{"use strict";pu=["dist/daemon/entrypoint.js"]});import{join as hu}from"node:path";var dS,On=b(()=>{"use strict";I();dS=hu(y,"daemon.log")});import{join as Su}from"node:path";var Fi,$i=b(()=>{"use strict";I();On();Fi=Su(y,"daemon.token")});import{basename as hS,join as vn}from"node:path";function Ui(){return{pid:Tu,port:bu,token:Fi}}function ji(e){return yu(e)}function yu(e){try{return process.kill(e,0),!0}catch{return!1}}var Tu,bu,yS,Hi=b(()=>{"use strict";I();$i();xn();On();Tu=vn(y,"daemon.pid"),bu=vn(y,"daemon.port"),yS=vn(y,"daemon.log")});var NS,Wi,Bi=b(()=>{"use strict";we();dt();NS=5*6e4,Wi=1073741824});import{existsSync as wu,readFileSync as Ru,renameSync as Xi,writeFileSync as Au}from"node:fs";import{join as Nu}from"node:path";function Gi(){return Nu(y,"doctor-state.json")}function zi(){let e=Gi();if(!wu(e))return{chunkQueue:{samples:[]}};let t;try{t=Ru(e,"utf8")}catch{return{chunkQueue:{samples:[]}}}try{let n=JSON.parse(t),s=n.chunkQueue?.samples,r=Array.isArray(s)?s.filter(c=>typeof c=="object"&&c!==null&&typeof c.ts=="string"&&typeof c.size=="number"&&typeof c.semanticEnabled=="boolean"):[],i=n.autoPruneCounters?.events,o=Array.isArray(i)?i.filter(c=>{if(!c||typeof c!="object")return!1;let l=c;return!(typeof l.ts!="string"||typeof l.pid!="number"||!Number.isFinite(l.pid)||l.action!=="would_kill"&&l.action!=="killed"&&l.action!=="failed"||l.reason!=="orphan_10min"&&l.reason!=="runaway_cpu_5min")}):[],a={chunkQueue:{samples:r}};return(o.length>0||i!==void 0)&&(a.autoPruneCounters={events:o}),a}catch{try{Xi(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function Yi(e){let t=Gi(),n=`${t}.tmp`;try{Au(n,JSON.stringify(e,null,2),{mode:384}),Xi(n,t)}catch{}}function In(){let e=E(),t=0;try{t=e.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let n=!1;try{n=e.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}let s=zi(),r=Date.now(),i=s.chunkQueue.samples,o=i.map(h=>({s:h,ms:Date.parse(h.ts)})).filter(h=>Number.isFinite(h.ms)&&r-h.ms<=ku).sort((h,S)=>h.ms-S.ms),a=null;o.length>0&&(a=t-o[0].s.size);let c=o.length,d={chunkQueue:{samples:[...i,{ts:new Date(r).toISOString(),size:t,semanticEnabled:n}].slice(-Lu)}};s.autoPruneCounters&&(d.autoPruneCounters=s.autoPruneCounters),Yi(d);let m="ok",p=`chunk_queue growth: ok (current ${t.toLocaleString()} row${t===1?"":"s"}, semantic ${n?"on":"off"}, ${c} prior sample${c===1?"":"s"} in last hour).`,g=null;return t>xu&&!n&&a!==null&&a>Ou?(m="critical",p=`chunk_queue growth: CRITICAL \u2014 ${t.toLocaleString()} rows with semantic disabled, grew by ${a.toLocaleString()} in the last hour. Schema-sync race shape.`,g="Schema-sync race likely \u2014 Phase 1.1 fix shipped on `feat/daemon-state-integrity`; if rows persist after a clean restart, re-investigate. Inspect with `sqlite3 ~/.recall/db.sqlite 'SELECT action, COUNT(*) FROM chunk_queue GROUP BY action;'`."):t>vu?(m="high",p=`chunk_queue growth: HIGH \u2014 ${t.toLocaleString()} rows pending (semantic ${n?"on":"off"}`+(a!==null?`, last-hour \u0394 ${a>=0?"+":""}${a.toLocaleString()}`:"")+").",g="Queue is large but stable \u2014 operator-authorized `recall semantic backfill` (or the daemon's embed worker, if intentionally on) would drain."):a!==null&&a>Iu&&(m="medium",p=`chunk_queue growth: MEDIUM \u2014 grew by ${a.toLocaleString()} in the last hour (current ${t.toLocaleString()}, semantic ${n?"on":"off"}).`,g="Growth is fast even though the absolute size is small. Re-run `recall doctor` in 10\u201330 min; if growth continues without semantic on, investigate the schema-sync gate."),{status:m,currentSize:t,semanticEnabled:n,lastHourGrowth:a,priorSampleCount:c,message:p,remediation:g}}function Ki(e={}){let t=e.now??Date.now(),n=zi(),s=n.autoPruneCounters?.events??[],r=s.filter(l=>{let d=Date.parse(l.ts);return Number.isFinite(d)&&t-d<=Cu});if(r.length!==s.length)try{let l={chunkQueue:n.chunkQueue,autoPruneCounters:{events:r}};Yi(l)}catch{}let i={orphan_10min:0,runaway_cpu_5min:0},o=0,a=0,c=0;for(let l of r)i[l.reason]+=1,l.action==="would_kill"?o+=1:l.action==="killed"?a+=1:c+=1;return{wouldHaveKilled:o,killed:a,failed:c,byReason:i}}var Lu,ku,xu,Ou,vu,Iu,Cu,Cn=b(()=>{"use strict";I();x();Lu=24,ku=3600*1e3,xu=1e3,Ou=1e3,vu=1e4,Iu=5e3,Cu=1440*60*1e3});function Vi(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return Ji;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:Ji}var Ji,qi=b(()=>{"use strict";dt();Cn();Ji="dry-run"});import{existsSync as Du,readFileSync as Mu,renameSync as Zi,writeFileSync as Pu}from"node:fs";import{join as Fu}from"node:path";function eo(){return Fu(y,"doctor-alerts.json")}function to(){let e=eo();if(!Du(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=Mu(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return Qi(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return Qi(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter($u):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function Qi(e){try{Zi(e,`${e}.corrupt.${Date.now()}`)}catch{}}function $u(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&typeof t.check=="string"&&(t.severity==="critical"||t.severity==="high"||t.severity==="medium")&&typeof t.message=="string"&&typeof t.remediation=="string"&&typeof t.firstSeenAt=="string"&&typeof t.lastSeenAt=="string"&&typeof t.seenCount=="number"&&typeof t.acknowledged=="boolean"}function no(e){let t=eo(),n=`${t}.tmp`;try{Pu(n,JSON.stringify(e,null,2),{mode:384}),Zi(n,t)}catch{}}function so(e,t){let n=t.trim().toLowerCase();if(n.length<4)return{file:e,result:{outcome:"not-found"}};let s=e.alerts.filter(a=>a.id.startsWith(n));if(s.length===0)return{file:e,result:{outcome:"not-found"}};if(s.length>1)return{file:e,result:{outcome:"ambiguous",candidates:s.map(a=>a.id)}};let r=s[0];if(r.acknowledged)return{file:e,result:{outcome:"already-acknowledged",matched:r}};let i={...r,acknowledged:!0},o=e.alerts.map(a=>a.id===r.id?i:a);return{file:{...e,alerts:o},result:{outcome:"acknowledged",matched:i}}}var ro=b(()=>{"use strict";I()});var No={};ye(No,{WATCHER_REFLAG_CRITICAL:()=>po,buildHealthReport:()=>So,buildPipelineDiagnostic:()=>_o,checkChunkQueueGrowth:()=>In,checkDaemonSiblings:()=>To,checkDaemonStateFiles:()=>bo,checkDiskPressureAndBackups:()=>ho,checkIngestStaleness:()=>wo,checkSemanticGateDrift:()=>Ao,checkStaleClaudeJsonMcpPaths:()=>Ro,checkWatcherReflagLoop:()=>mo,countBackupOrphans:()=>ep,detectLabelCollisions:()=>Eo,getFreeDiskBytes:()=>sp,renderMigrationDoctorSection:()=>yo,runDoctor:()=>np});import{existsSync as Ce,readdirSync as Uu,readFileSync as Dn,statSync as pt,statfsSync as lo}from"node:fs";import{homedir as ju}from"node:os";import{join as mt}from"node:path";import*as uo from"node:http";function Bu(e){let t=[];for(let n of e)if(n.alias){if(Wu.test(n.alias)){t.push({session_id:n.session_id,alias:n.alias,violation:"fabricated-origin-label",cwd:n.cwd});continue}if(n.cwd){let s=n.cwd.replace(/\/+$/,"").split("/").pop();s&&n.alias.startsWith(`${s} \xB7 `)&&t.push({session_id:n.session_id,alias:n.alias,violation:"fabricated-cwd-branch",cwd:n.cwd})}}return t}function mo(e,t=po){let n=[];for(let s of e){if(s.noProgressCount<=t)continue;let r=s.path.replace(/'/g,"''");n.push(`Watcher reindexed ${s.path} ${s.count.toLocaleString()} times in the last hour (${s.noProgressCount.toLocaleString()} with no new content) \u2014 reflag loop. Mark it skipped with a SQL one-liner:
846
+ sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${r}';"`)}return n}function Xu(){let e=mt(y,"daemon.port");if(!Ce(e))return null;try{let t=Dn(e,"utf8").trim();if(t.startsWith("{")){let s=JSON.parse(t);return typeof s.port=="number"?s.port:null}let n=Number.parseInt(t,10);return Number.isFinite(n)&&n>0&&n<65536?n:null}catch{return null}}function io(e,t,n){return new Promise(s=>{let r=uo.request({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:n,headers:{host:"127.0.0.1","user-agent":"recall-doctor"}},i=>{let o=[];i.on("data",a=>o.push(Buffer.isBuffer(a)?a:Buffer.from(a))),i.on("end",()=>{if(!i.statusCode||i.statusCode<200||i.statusCode>=300){s({ok:!1,reason:"http"});return}try{s({ok:!0,json:JSON.parse(Buffer.concat(o).toString("utf8"))})}catch{s({ok:!1,reason:"parse"})}})});r.on("error",()=>s({ok:!1,reason:"error"})),r.on("timeout",()=>{r.destroy(),s({ok:!1,reason:"timeout"})}),r.end()})}async function Gu(e){let t=await io(e,"/api/health",oo);if(t.ok)return t.json;if(t.reason!=="timeout")return null;let n=await io(e,"/api/health",oo);return n.ok?n.json:null}function zu(){let e=mt(y,"terminals.json");if(!Ce(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=pt(e),n=Math.floor((Date.now()-t.mtimeMs)/1e3);return{exists:!0,mtimeMs:t.mtimeMs,ageSeconds:n}}catch{return{exists:!1,mtimeMs:null,ageSeconds:null}}}function Yu(){let e=new Date(Date.now()-Mn*36e5).toISOString();try{let t=E().prepare(`SELECT
825
847
  COUNT(*) AS total,
826
848
  SUM(CASE WHEN sa.alias IS NULL OR sa.alias = '' THEN 1 ELSE 0 END) AS without_alias,
827
849
  SUM(CASE WHEN (sa.alias IS NULL OR sa.alias = '')
828
850
  AND s.auto_title_source = 'heuristic' THEN 1 ELSE 0 END) AS heuristic_only
829
851
  FROM sessions s
830
852
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
831
- WHERE s.started_at >= ?`).get(e),n=t.total??0,s=t.without_alias??0,r=t.heuristic_only??0,i=n>0?r/n:0;return{total:n,withoutAlias:s,heuristicOnly:r,fractionHeuristic:i}}catch{return{total:0,withoutAlias:0,heuristicOnly:0,fractionHeuristic:0}}}async function qi(){let e=Ru(),t=Nu(),n=Lu(),s=!1,r=null,i=null,a=null,o=null,c=null,l=[];if(e){let p=await Au(e,"/api/health");if(p){s=!0,r=typeof p.uptimeSeconds=="number"?p.uptimeSeconds:null,i=typeof p.version=="string"?p.version:null,a=typeof p.pipeline?.silentTerminalRejections=="number"?p.pipeline.silentTerminalRejections:null,o=p.pipeline?.lastTerminalSyncAt??null;let g=p.pipeline?.autoExtract;g&&(c={circuitBroken:g.circuitBroken===!0,reason:g.reason??null,brokenAt:typeof g.brokenAt=="number"?g.brokenAt:null,consecutiveZeroTokenRuns:typeof g.consecutiveZeroTokenRuns=="number"?g.consecutiveZeroTokenRuns:0});let h=p.pipeline?.watcherReindexHotFiles;Array.isArray(h)&&(l=h.filter(S=>typeof S?.path=="string"&&typeof S?.count=="number"&&typeof S?.firstSeenAt=="number").map(S=>({path:S.path,count:S.count,firstSeenAt:S.firstSeenAt})))}}let d=[];if(s||d.push("Daemon not reachable on 127.0.0.1 \u2014 start it with `recall start` before further diagnosis."),a!==null&&a>0&&d.push(`Daemon rejected ${a.toLocaleString()} /api/terminal/* request(s) without a valid X-Recall-Token. The editor extension is outdated relative to this daemon \u2014 tab names are not flowing through. Reinstall: \`code --install-extension extensions/vscode/clauderecall-vscode-*.vsix\`, then reload the extension host (Cmd/Ctrl+Shift+P \u2192 "Developer: Restart Extension Host").`),s&&r!==null&&r>30)if(!o)d.push(`Daemon has been running ${Math.round(r/60)} min but has not yet seen a single successful /api/terminal/sync. Either no editor extension is installed/active, or every attempt is being rejected (see counter above).`);else{let p=Date.now()-Date.parse(o);Number.isFinite(p)&&p>Ki&&d.push(`Last successful /api/terminal/sync was ${Math.round(p/6e4)} min ago \u2014 extension may have crashed, been disabled, or is failing auth. Run \`recall doctor\` again after restarting the extension host.`)}!s&&t.exists&&t.ageSeconds!==null&&t.ageSeconds>24*3600&&d.push(`~/.recall/terminals.json is ${Math.round(t.ageSeconds/3600)}h old \u2014 pipeline has not produced fresh data recently. Likely root cause is the same as the rejection counter would show; start the daemon and re-run.`),c?.circuitBroken&&d.push(`Auto-extract circuit breaker tripped \u2014 ${c.reason??"reason unknown"}. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`),n.total>=3&&n.fractionHeuristic>=Ji&&d.push(`${n.heuristicOnly}/${n.total} sessions in the last ${vn}h (${Math.round(n.fractionHeuristic*100)}%) fell back to the heuristic first-message title. A healthy pipeline rate is < 20%. Either the extension is not syncing tab names, or the correlator cannot disambiguate. Reinstall the extension and verify the rejection counter drops to 0.`);for(let p of Yi(l))d.push(p);return{state:s?d.length>0?"degraded":"ok":"down",flags:d,daemon:{running:s,port:e,uptimeSeconds:r,version:i},runtime:{silentTerminalRejections:a,lastTerminalSyncAt:o,autoExtract:c},terminalsJson:t,recentSessions:n,watcherReindexHotFiles:l}}function j(e){return e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}function Wi(e){try{return ut(e).size}catch{return 0}}function ku(e){return e==="db.sqlite"||e==="db.sqlite-wal"||e==="db.sqlite-shm"?!1:!!(e.startsWith("db.sqlite.")||e.endsWith(".bak")||e.includes(".bak."))}function Vi(e=y){let t=0,n=0;try{let g=Xi(e);t=Number(g.bavail)*Number(g.bsize),n=Number(g.blocks)*Number(g.bsize)}catch{}let s=n>0?t/n*100:100,r=[];try{r=Su(e)}catch{r=[]}let i=0,a=0,o=[],c=Date.now(),l=720*60*60*1e3;for(let g of r){if(!ku(g))continue;let h;try{h=ut(pt(e,g))}catch{continue}if(!h.isFile())continue;a+=1,i+=h.size;let S=Math.max(0,c-h.mtimeMs),u=Math.floor(S/(1440*60*1e3));S>=l&&o.push({name:g,sizeBytes:h.size,ageDays:u})}o.sort((g,h)=>h.sizeBytes!==g.sizeBytes?h.sizeBytes-g.sizeBytes:h.ageDays-g.ageDays);let d=2*1024**3,m=5*1024**3,p="ok";return n>0&&s<10||i>m?p="high":n>0&&s<20||i>d?p="medium":a>0&&(p="low"),{freeBytes:t,totalBytes:n,freePercent:s,backupTotalBytes:i,backupFileCount:a,oldFiles:o,severity:p}}function Bi(e){try{return E().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function Qi(){try{return E().prepare(`SELECT p.name AS project,
853
+ WHERE s.started_at >= ?`).get(e),n=t.total??0,s=t.without_alias??0,r=t.heuristic_only??0,i=n>0?r/n:0;return{total:n,withoutAlias:s,heuristicOnly:r,fractionHeuristic:i}}catch{return{total:0,withoutAlias:0,heuristicOnly:0,fractionHeuristic:0}}}async function _o(){let e=Xu(),t=zu(),n=Yu(),s=!1,r=null,i=null,o=null,a=null,c=null,l=[];if(e){let p=await Gu(e);if(p){s=!0,r=typeof p.uptimeSeconds=="number"?p.uptimeSeconds:null,i=typeof p.version=="string"?p.version:null,o=typeof p.pipeline?.silentTerminalRejections=="number"?p.pipeline.silentTerminalRejections:null,a=p.pipeline?.lastTerminalSyncAt??null;let g=p.pipeline?.autoExtract;g&&(c={circuitBroken:g.circuitBroken===!0,reason:g.reason??null,brokenAt:typeof g.brokenAt=="number"?g.brokenAt:null,consecutiveZeroTokenRuns:typeof g.consecutiveZeroTokenRuns=="number"?g.consecutiveZeroTokenRuns:0});let h=p.pipeline?.watcherReindexHotFiles;Array.isArray(h)&&(l=h.filter(S=>typeof S?.path=="string"&&typeof S?.count=="number"&&typeof S?.firstSeenAt=="number").map(S=>({path:S.path,count:S.count,firstSeenAt:S.firstSeenAt,noProgressCount:typeof S.noProgressCount=="number"?S.noProgressCount:S.count})))}}let d=[];if(s||d.push("Daemon not reachable on 127.0.0.1 \u2014 start it with `recall start` before further diagnosis."),o!==null&&o>0&&d.push(`Daemon rejected ${o.toLocaleString()} /api/terminal/* request(s) without a valid X-Recall-Token. The editor extension is outdated relative to this daemon \u2014 tab names are not flowing through. Reinstall: \`code --install-extension extensions/vscode/clauderecall-vscode-*.vsix\`, then reload the extension host (Cmd/Ctrl+Shift+P \u2192 "Developer: Restart Extension Host").`),s&&r!==null&&r>30)if(!a)d.push(`Daemon has been running ${Math.round(r/60)} min but has not yet seen a single successful /api/terminal/sync. Either no editor extension is installed/active, or every attempt is being rejected (see counter above).`);else{let p=Date.now()-Date.parse(a);Number.isFinite(p)&&p>go&&d.push(`Last successful /api/terminal/sync was ${Math.round(p/6e4)} min ago \u2014 extension may have crashed, been disabled, or is failing auth. Run \`recall doctor\` again after restarting the extension host.`)}!s&&t.exists&&t.ageSeconds!==null&&t.ageSeconds>24*3600&&d.push(`~/.recall/terminals.json is ${Math.round(t.ageSeconds/3600)}h old \u2014 pipeline has not produced fresh data recently. Likely root cause is the same as the rejection counter would show; start the daemon and re-run.`),c?.circuitBroken&&d.push(`Auto-extract circuit breaker tripped \u2014 ${c.reason??"reason unknown"}. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`),n.total>=3&&n.fractionHeuristic>=fo&&d.push(`${n.heuristicOnly}/${n.total} sessions in the last ${Mn}h (${Math.round(n.fractionHeuristic*100)}%) fell back to the heuristic first-message title. A healthy pipeline rate is < 20%. Either the extension is not syncing tab names, or the correlator cannot disambiguate. Reinstall the extension and verify the rejection counter drops to 0.`);for(let p of mo(l))d.push(p);return{state:s?d.length>0?"degraded":"ok":"down",flags:d,daemon:{running:s,port:e,uptimeSeconds:r,version:i},runtime:{silentTerminalRejections:o,lastTerminalSyncAt:a,autoExtract:c},terminalsJson:t,recentSessions:n,watcherReindexHotFiles:l}}function j(e){return e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}function ao(e){try{return pt(e).size}catch{return 0}}function Ku(e){return e==="db.sqlite"||e==="db.sqlite-wal"||e==="db.sqlite-shm"?!1:!!(e.startsWith("db.sqlite.")||e.endsWith(".bak")||e.includes(".bak."))}function ho(e=y){let t=0,n=0;try{let g=lo(e);t=Number(g.bavail)*Number(g.bsize),n=Number(g.blocks)*Number(g.bsize)}catch{}let s=n>0?t/n*100:100,r=[];try{r=Uu(e)}catch{r=[]}let i=0,o=0,a=[],c=Date.now(),l=720*60*60*1e3;for(let g of r){if(!Ku(g))continue;let h;try{h=pt(mt(e,g))}catch{continue}if(!h.isFile())continue;o+=1,i+=h.size;let S=Math.max(0,c-h.mtimeMs),u=Math.floor(S/(1440*60*1e3));S>=l&&a.push({name:g,sizeBytes:h.size,ageDays:u})}a.sort((g,h)=>h.sizeBytes!==g.sizeBytes?h.sizeBytes-g.sizeBytes:h.ageDays-g.ageDays);let d=2*1024**3,m=5*1024**3,p="ok";return n>0&&s<10||i>m?p="high":n>0&&s<20||i>d?p="medium":o>0&&(p="low"),{freeBytes:t,totalBytes:n,freePercent:s,backupTotalBytes:i,backupFileCount:o,oldFiles:a,severity:p}}function co(e){try{return E().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function Eo(){try{return E().prepare(`SELECT p.name AS project,
832
854
  COALESCE(NULLIF(sa.alias, ''), s.auto_title, substr(s.first_user_message, 1, 60)) AS label,
833
855
  COUNT(*) AS count,
834
856
  MAX(CASE WHEN sa.alias IS NOT NULL AND sa.alias != '' THEN 1 ELSE 0 END) AS any_aliased
@@ -840,46 +862,46 @@ ${m+1}. ${p}`:`${m+1}. ${p}`}).join(`
840
862
  GROUP BY p.name, label
841
863
  HAVING count >= 2
842
864
  ORDER BY count DESC, label ASC
843
- LIMIT 10`).all().map(e=>{let t=e;return{project:t.project,label:t.label,count:t.count,anyAliased:t.any_aliased===1}})}catch{return[]}}function Zi(e){let t=E(),n=t.pragma("page_size",{simple:!0})||4096,s=t.pragma("page_count",{simple:!0})||0,r=t.pragma("freelist_count",{simple:!0})||0;e?.("Checking database integrity");let i="ok";try{let H=t.pragma("quick_check").map(J=>J.quick_check);i=H.length===1&&H[0]==="ok"?"ok":H.join("; ")}catch(A){i=`check failed: ${A.message}`}let a=Wi(ge),o=Wi(`${ge}-wal`),c=Vi(),l=c.freeBytes,d=c.totalBytes;e?.("Counting rows");let m=t.prepare(`SELECT
865
+ LIMIT 10`).all().map(e=>{let t=e;return{project:t.project,label:t.label,count:t.count,anyAliased:t.any_aliased===1}})}catch{return[]}}function So(e){let t=E(),n=t.pragma("page_size",{simple:!0})||4096,s=t.pragma("page_count",{simple:!0})||0,r=t.pragma("freelist_count",{simple:!0})||0;e?.("Checking database integrity");let i="ok";try{let H=t.pragma("quick_check").map(J=>J.quick_check);i=H.length===1&&H[0]==="ok"?"ok":H.join("; ")}catch(A){i=`check failed: ${A.message}`}let o=ao(fe),a=ao(`${fe}-wal`),c=ho(),l=c.freeBytes,d=c.totalBytes;e?.("Counting rows");let m=t.prepare(`SELECT
844
866
  (SELECT COUNT(*) FROM projects) AS projects,
845
867
  (SELECT COUNT(*) FROM sessions) AS sessions,
846
868
  (SELECT COUNT(*) FROM messages) AS messages,
847
- (SELECT COUNT(*) FROM message_usage) AS message_usage`).get(),p=0;try{p=t.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get().n}catch{}e?.("Measuring FTS fragmentation");let g=[];if(l>0&&l<1*1024**3&&g.push(`Disk free is ${j(l)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`),c.severity==="high"||c.severity==="medium"){let A=c.severity==="high"?"HIGH":"MEDIUM",H=[];d>0&&H.push(`disk: ${j(l)} free of ${j(d)} (${c.freePercent.toFixed(1)}%)`),c.backupFileCount>0&&H.push(`backups: ${j(c.backupTotalBytes)} in ${c.backupFileCount} file${c.backupFileCount===1?"":"s"}`);let J=`[${A}] disk pressure \u2014 ${H.join(" \xB7 ")}`;if(c.oldFiles.length>0){let ie=c.oldFiles.map(de=>` \u2022 ${de.name} ${j(de.sizeBytes)} (${de.ageDays} days old)`);J+=`
869
+ (SELECT COUNT(*) FROM message_usage) AS message_usage`).get(),p=0;try{oe(t),p=t.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get().n}catch{}e?.("Measuring FTS fragmentation");let g=[];if(l>0&&l<1*1024**3&&g.push(`Disk free is ${j(l)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`),c.severity==="high"||c.severity==="medium"){let A=c.severity==="high"?"HIGH":"MEDIUM",H=[];d>0&&H.push(`disk: ${j(l)} free of ${j(d)} (${c.freePercent.toFixed(1)}%)`),c.backupFileCount>0&&H.push(`backups: ${j(c.backupTotalBytes)} in ${c.backupFileCount} file${c.backupFileCount===1?"":"s"}`);let J=`[${A}] disk pressure \u2014 ${H.join(" \xB7 ")}`;if(c.oldFiles.length>0){let ie=c.oldFiles.map(ue=>` \u2022 ${ue.name} ${j(ue.sizeBytes)} (${ue.ageDays} days old)`);J+=`
848
870
  Snapshots older than 30 days:
849
871
  `+ie.join(`
850
872
  `)+"\n review then delete with: `ls -la ~/.recall/*.bak* | sort -k6,7` \u2014 items older than 30 days are typically safe per memory `partial_corpus_swap_bug_20260519` (keep most recent .pre-swap for rollback)."}else c.backupFileCount>0&&(J+=`
851
- No snapshots older than 30 days \u2014 recent ones may still be rollback-critical; leave them alone unless you know what you're doing.`);g.push(J)}let h=100*1024**2,S=o>=Ai?"error":o>=h?"warn":"ok";S==="error"?g.push(`WAL is ${j(o)} \u2014 readers are pinning the checkpoint frontier. Run \`recall mcp-prune\` to release stuck MCP children, then \`recall optimize\` to truncate.`):S==="warn"&&g.push(`WAL is ${j(o)} \u2014 run \`recall optimize\` to truncate it.`);let u=Ie(),f=u.filter(A=>A.orphan);f.length>0&&g.push(`${f.length} orphaned MCP child${f.length===1?"":"ren"} (pid${f.length===1?"":"s"}: ${f.map(A=>A.pid).join(", ")}). Each holds a SQLite read connection. Run \`recall mcp-prune\` to clean up.`);let T=u.filter(ct);T.length>0&&g.push(`${T.length} MCP child${T.length===1?"":"ren"} burning CPU (pid${T.length===1?"":"s"}: ${T.map(A=>A.pid).join(", ")}). Likely runaway vec0 kNN. Run \`recall stop --all\` or \`recall mcp-prune --all\`.`),r>s*.2&&s>1e3&&g.push(`${r.toLocaleString()} free pages (${(r/s*100).toFixed(0)}% of file) \u2014 \`recall optimize --vacuum\` will reclaim them.`);let R=Bi("messages_fts"),k=Bi("sessions_fts");R>16&&g.push(`messages_fts has ${R} segments \u2014 \`recall optimize\` will merge them.`);let w=0;try{w=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let L=!1;try{L=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!L&&w>0?g.push(`${w.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):w>1e5&&g.push(`chunk_queue has ${w.toLocaleString()} pending rows \u2014 embedder is behind. \`recall semantic backfill\` to drain manually.`),{db:{sizeBytes:a,walSizeBytes:o,pageCount:s,pageSize:n,freelistCount:r,freelistBytes:r*n,integrity:i},disk:{freeBytes:l,totalBytes:d,backups:{totalBytes:c.backupTotalBytes,fileCount:c.backupFileCount,oldFiles:c.oldFiles,severity:c.severity}},fts:{messages:{fragments:R},sessions:{fragments:k}},vectors:{rows:p},rows:{projects:m.projects,sessions:m.sessions,messages:m.messages,messageUsage:m.message_usage},chunkQueue:{size:w,semanticEnabled:L},wal:{sizeBytes:o,level:S},mcpProcesses:u,warnings:g}}function xu(e){if(!e)return{stage:()=>{},done:()=>{}};let t=!!process.stderr.isTTY,n="",s=0,r=()=>{if(!n)return;let i=Date.now()-s,a=i<1e3?`${i}ms`:`${(i/1e3).toFixed(1)}s`;t?process.stderr.write(`\r\x1B[2K ${_.ok("\u2713")} ${n} ${_.dim(`(${a})`)}
852
- `):process.stderr.write(` \u2713 ${n} (${a})
873
+ No snapshots older than 30 days \u2014 recent ones may still be rollback-critical; leave them alone unless you know what you're doing.`);g.push(J)}let h=100*1024**2,S=a>=Wi?"error":a>=h?"warn":"ok";S==="error"?g.push(`WAL is ${j(a)} \u2014 readers are pinning the checkpoint frontier. Run \`recall mcp-prune\` to release stuck MCP children, then \`recall optimize\` to truncate.`):S==="warn"&&g.push(`WAL is ${j(a)} \u2014 run \`recall optimize\` to truncate it.`);let u=Ie(),f=u.filter(A=>A.orphan);f.length>0&&g.push(`${f.length} orphaned MCP child${f.length===1?"":"ren"} (pid${f.length===1?"":"s"}: ${f.map(A=>A.pid).join(", ")}). Each holds a SQLite read connection. Run \`recall mcp-prune\` to clean up.`);let T=u.filter(lt);T.length>0&&g.push(`${T.length} MCP child${T.length===1?"":"ren"} burning CPU (pid${T.length===1?"":"s"}: ${T.map(A=>A.pid).join(", ")}). Likely runaway vec0 kNN. Run \`recall stop --all\` or \`recall mcp-prune --all\`.`),r>s*.2&&s>1e3&&g.push(`${r.toLocaleString()} free pages (${(r/s*100).toFixed(0)}% of file) \u2014 \`recall optimize --vacuum\` will reclaim them.`);let R=co("messages_fts"),k=co("sessions_fts");R>16&&g.push(`messages_fts has ${R} segments \u2014 \`recall optimize\` will merge them.`);let w=0;try{w=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let L=!1;try{L=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!L&&w>0?g.push(`${w.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):w>1e5&&g.push(`chunk_queue has ${w.toLocaleString()} pending rows \u2014 embedder is behind. \`recall semantic backfill\` to drain manually.`),{db:{sizeBytes:o,walSizeBytes:a,pageCount:s,pageSize:n,freelistCount:r,freelistBytes:r*n,integrity:i},disk:{freeBytes:l,totalBytes:d,backups:{totalBytes:c.backupTotalBytes,fileCount:c.backupFileCount,oldFiles:c.oldFiles,severity:c.severity}},fts:{messages:{fragments:R},sessions:{fragments:k}},vectors:{rows:p},rows:{projects:m.projects,sessions:m.sessions,messages:m.messages,messageUsage:m.message_usage},chunkQueue:{size:w,semanticEnabled:L},wal:{sizeBytes:a,level:S},mcpProcesses:u,warnings:g}}function Ju(e){if(!e)return{stage:()=>{},done:()=>{}};let t=!!process.stderr.isTTY,n="",s=0,r=()=>{if(!n)return;let i=Date.now()-s,o=i<1e3?`${i}ms`:`${(i/1e3).toFixed(1)}s`;t?process.stderr.write(`\r\x1B[2K ${_.ok("\u2713")} ${n} ${_.dim(`(${o})`)}
874
+ `):process.stderr.write(` \u2713 ${n} (${o})
853
875
  `),n=""};return{stage(i){r(),n=i,s=Date.now(),t?process.stderr.write(` ${_.dim("\u2026")} ${n}`):process.stderr.write(` \u2026 ${n}
854
- `)},done:r}}function eo(e){let t=e??dt(),n=t.length;if(n<=1)return{flagged:!1,severity:"ok",count:n,processes:t,message:null};let s=t.map(i=>{let a=Rn(i.etimeSeconds)??i.etime;return`pid=${i.pid} started=${a} age=${fe(i.etimeSeconds)}`}),r=`${n} Recall daemons running simultaneously: ${s.join("; ")}. Two daemons write the same db.sqlite and corrupt state (chunk_queue fills, schema-sync race re-enables feature flags). Run \`recall stop\` to kill all (now nukes orphans too as of Phase 2.6), then \`recall start\`.`;return{flagged:!0,severity:"critical",count:n,processes:t,message:r}}function to(e={}){if((e.liveDaemons??dt({excludePids:[process.pid]})).length===0)return{flagged:!1,severity:"ok",daemonAlive:!1,missing:[],message:null,remediation:null};let n=e.paths??yi(),s=e.existsSync??Ce,r=e.isProcessAlive??wi,i=!1;if(s(n.pid))try{let c=JSON.parse(On(n.pid,"utf8"));typeof c.pid=="number"&&r(c.pid)&&(i=!0)}catch{}if(!i)return{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null};let a=[];return s(n.port)||a.push("daemon.port"),s(n.token)||a.push("daemon.token"),a.length===0?{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null}:{flagged:!0,severity:"critical",daemonAlive:!0,missing:a,message:`Daemon process is alive (per pidfile) but companion state file${a.length===1?"":"s"} ${a.join(", ")} missing. Web UI and authenticated CLI calls will return 401 until the 30s heal tick restores them.`,remediation:"The 30-second heal tick re-creates these files automatically. To force immediate recovery: recall stop && recall start. Inspect ~/.recall/daemon.log for [state-files-audit] entries to identify the deletion source."}}function Ou(e){e.flagged&&(console.log(""),console.log(_.dim("\u2014 Daemon state files \u2014")),console.log(_.err(` \u2717 CRITICAL: daemon process alive but missing ${e.missing.join(", ")}`)),e.remediation&&console.log(_.dim(` Remediation: ${e.remediation}`)))}function vu(e){if(e.flagged){console.log(""),console.log(_.dim("\u2014 Daemon processes \u2014")),console.log(_.err(` \u2717 CRITICAL: ${e.count} daemons running (operator rule: never two)`));for(let t of e.processes){let n=Rn(t.etimeSeconds)??t.etime;console.log(` ${_.dim("\u2022")} pid ${t.pid} started ${n} age ${fe(t.etimeSeconds)} ppid=${t.ppid}`)}e.message&&console.log(_.dim(" Remediation: `recall stop` (now nukes orphans too as of Phase 2.6), then `recall start`."))}}function Iu(e){if(console.log(_.dim("\u2014 MCP processes \u2014")),e.length===0){console.log(_.ok(" \u2713 no MCP children running"));return}let t=e.filter(a=>a.orphan),n=e.reduce((a,o)=>Math.max(a,o.etimeSeconds),0),s=e.length-t.length;if(console.log(` ${e.length} total, ${s} with live parent, ${t.length===0?_.ok("0 orphaned"):_.err(`${t.length} orphaned`)} (oldest ${fe(n)})`),t.length>0){console.log(_.dim(" Orphans hold a SQLite read connection and can stall WAL checkpoints. Run `recall mcp-prune` to kill them."));for(let a of t.slice(0,5))console.log(` ${_.dim("\u2022")} pid ${a.pid} age ${fe(a.etimeSeconds)} ppid=${a.ppid} (gone)`)}let r=e.filter(ct);if(r.length>0){console.log(_.warn(` ! ${r.length} MCP child${r.length===1?"":"ren"} burning CPU (likely runaway vec0 kNN -- see 2026-05-15 incident).`));for(let a of r){let o=a.parentCommand?`parent ${a.parentCommand}`:`ppid ${a.ppid}`;console.log(_.dim(` pid ${a.pid} ${a.pcpu.toFixed(0)}%cpu ${fe(a.etimeSeconds)} elapsed (${o})`))}console.log(_.dim(" Run `recall stop --all` for one-command recovery, or `recall mcp-prune --all` to keep the daemon alive."))}let i=Ei(e);i.flagged&&i.message&&console.log(_.warn(` ! ${i.message}`))}function Cu(){let e=Ci(),t=vi();if(console.log(_.dim("\u2014 Auto-prune (last 24h) \u2014")),console.log(` Mode: ${e}`),e==="off"){console.log(_.dim(" (auto-prune disabled \u2014 orphans + runaway MCPs will accumulate until manually reaped)"));return}e==="dry-run"?console.log(` Would-have-killed: ${t.wouldHaveKilled} ${_.dim("(dry-run only)")}`):(console.log(` Killed: ${t.killed} ${_.dim("(enabled)")}`),t.failed>0&&console.log(` Failed: ${_.warn(String(t.failed))} ${_.dim("(kill returned EPERM or similar)")}`));let n=t.byReason;console.log(` By reason: orphan_10min=${n.orphan_10min}, runaway_cpu_5min=${n.runaway_cpu_5min}`)}function no(){let e=E(),t,n,s;try{t=e.prepare("SELECT * FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get(),n=e.prepare("SELECT * FROM migration_state WHERE status = 'completed' ORDER BY id DESC LIMIT 1").get(),s=e.prepare("SELECT * FROM migration_state WHERE status IN ('failed', 'rolled_back') ORDER BY id DESC LIMIT 1").get()}catch(a){let o=a instanceof Error?a.message:String(a);return o.includes("no such table: migration_state")||process.stderr.write(`[doctor] renderMigrationDoctorSection: ${o}
855
- `),null}if(!t&&!n&&!s)return null;let r=["--- Migration status ---"];if(t&&(r.push(` Status: ${t.status}`),r.push(` Cursor: chunk_id=${t.cursor_chunk_id??"(none yet)"}`),r.push(` Lock pid: ${t.lock_taken_by_pid??"(released)"}`),r.push(` Old model: ${t.model_id_old}`),r.push(` New model: ${t.model_id_new}`)),n&&!t&&e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()){let o=e.prepare("SELECT CAST((julianday('now') - julianday(?)) AS INTEGER) AS days").get(n.completed_at),c=Math.max(0,30-o.days);r.push(` Backup retained: ${c} day(s) until auto-prune.`),r.push(` Last completed: ${n.completed_at}`),r.push(` Old model: ${n.model_id_old}`),r.push(` New model: ${n.model_id_new}`)}s&&!t&&!n&&(r.push(` ! Last migration: ${s.status}`),r.push(` Completed at: ${s.completed_at}`),r.push(` Old model: ${s.model_id_old}`),r.push(` New model: ${s.model_id_new}`),r.push(" Remediation: inspect daemon log; re-run `recall semantic migrate` to retry"));let i=so();return i>0&&r.push(` ! ${i} orphan row(s) in vec_chunks_v1_backup (deletion path missed the backup; report to maintainer)`),r.join(`
856
- `)}function so(){let e=E();return e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()?e.prepare("SELECT COUNT(*) AS n FROM vec_chunks_v1_backup b LEFT JOIN chunk_meta cm ON cm.rowid = b.rowid WHERE cm.rowid IS NULL").get().n:0}function ro(){let e=E(),t=e.prepare("SELECT encoded_path FROM projects").all(),n=new Set(t.map(l=>l.encoded_path));if(n.size===0)return{status:"ok",staleCount:0,scanned:0,sampleFiles:[],message:"Ingest freshness: ok (no projects indexed yet \u2014 nothing to monitor)."};let s=e.prepare("SELECT file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1"),r=0,i=0,a=[];for(let l of wn(St)){if(mi(l))continue;let d=pi(l);if(!d||!n.has(d))continue;r+=1;let m;try{m=ut(l).mtimeMs}catch{continue}let p=s.get(l);p&&p.skipped_reason!==null||(!p||p.file_mtime<m)&&(i+=1,a.length<5&&a.push(l))}if(i===0)return{status:"ok",staleCount:0,scanned:r,sampleFiles:[],message:`Ingest freshness: ok (scanned ${r} JSONL${r===1?"":"s"} in ${n.size} known project${n.size===1?"":"s"}, none newer than the index).`};let o=i>10?"fail":"warn",c=a.slice(0,3).map(l=>l.split(/[/\\]/).pop()??l).join(", ");return{status:o,staleCount:i,scanned:r,sampleFiles:a,message:`Ingest freshness: ${o} \u2014 ${i} JSONL${i===1?"":" files"} newer than the index (sample: ${c}). Run \`recall index\` to force a reindex, or restart the daemon if this is persistent.`}}function io(e=pt(Tu(),".claude.json")){let t={status:"ok",configPath:e,configExists:Ce(e),findings:[]};if(!t.configExists)return t;let n;try{n=On(e,"utf8")}catch{return t}let s;try{s=JSON.parse(n)}catch{return t}if(!s||typeof s!="object"||Array.isArray(s))return t;let r=s.mcpServers;if(!r||typeof r!="object"||Array.isArray(r))return t;for(let[i,a]of Object.entries(r)){if(!a||typeof a!="object")continue;let o=a.args;if(!Array.isArray(o)||o.length===0)continue;let c=o[0];if(typeof c!="string"||c.length===0||!c.startsWith("/")&&!c.startsWith("~")||Ce(c))continue;let l=i==="recall";t.findings.push({name:i,stalePath:c,severity:l?"HIGH":"MEDIUM",remediation:l?"restart daemon (`recall stop && recall start`) \u2014 auto-repoint runs on boot now":"this is a third-party MCP \u2014 the vendor's package may have moved; reinstall their package"})}return t.findings.some(i=>i.severity==="HIGH")?t.status="fail":t.findings.length>0&&(t.status="warn"),t}function oo(){let e=null;try{e=Sn().enabled}catch{return{status:"ok",configEnabled:null,dbValue:null,message:null}}let t=null;try{t=E().prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value??null}catch{return{status:"ok",configEnabled:e,dbValue:null,message:null}}if(t===null)return{status:"ok",configEnabled:e,dbValue:null,message:null};if(t==="1"===e)return{status:"ok",configEnabled:e,dbValue:t,message:null};let s=`config.json says ${String(e)} but DB gate says ${t}. Recover with \`recall stop && recall start\` (boot sync re-reads config). If this recurs, grep daemon.log for \`[semantic-config] gate flip\` to identify the runtime flipper.`;return{status:"critical",configEnabled:e,dbValue:t,message:s}}function Du(e,t){let n=$i(),{file:s,result:r}=ji(n,e);if((r.outcome==="acknowledged"||r.outcome==="already-acknowledged")&&s!==n&&Ui(s),t.json){let i=r.outcome==="acknowledged"||r.outcome==="already-acknowledged"?{outcome:r.outcome,id:r.matched?.id,check:r.matched?.check,message:r.matched?.message}:{outcome:r.outcome,candidates:r.candidates??[]};process.stdout.write(`${JSON.stringify(i)}
857
- `)}else switch(r.outcome){case"acknowledged":console.log(`acknowledged: ${r.matched?.message??"(no message)"}`);break;case"already-acknowledged":console.log(`already acknowledged: ${r.matched?.message??"(no message)"}`);break;case"not-found":console.log(`no alert matched "${e}". Run \`recall doctor\` to list current alerts.`);break;case"ambiguous":console.log(`"${e}" matched ${r.candidates?.length??0} alerts \u2014 use a longer prefix:`);for(let i of r.candidates??[])console.log(` ${i}`);break}return r.outcome==="not-found"||r.outcome==="ambiguous"?1:0}async function Mu(e={}){if(typeof e.ack=="string"&&e.ack.length>0)return Du(e.ack,{json:!!e.json});let t=xu(!e.json);t.stage("Scanning session aliases");let n=E().prepare(`SELECT sa.session_id AS session_id, sa.alias AS alias, s.cwd AS cwd
876
+ `)},done:r}}function To(e){let t=e??ut(),n=t.length;if(n<=1)return{flagged:!1,severity:"ok",count:n,processes:t,message:null};let s=t.map(i=>{let o=kn(i.etimeSeconds)??i.etime;return`pid=${i.pid} started=${o} age=${_e(i.etimeSeconds)}`}),r=`${n} Recall daemons running simultaneously: ${s.join("; ")}. Two daemons write the same db.sqlite and corrupt state (chunk_queue fills, schema-sync race re-enables feature flags). Run \`recall stop\` to kill all (now nukes orphans too as of Phase 2.6), then \`recall start\`.`;return{flagged:!0,severity:"critical",count:n,processes:t,message:r}}function bo(e={}){if((e.liveDaemons??ut({excludePids:[process.pid]})).length===0)return{flagged:!1,severity:"ok",daemonAlive:!1,missing:[],message:null,remediation:null};let n=e.paths??Ui(),s=e.existsSync??Ce,r=e.isProcessAlive??ji,i=!1;if(s(n.pid))try{let c=JSON.parse(Dn(n.pid,"utf8"));typeof c.pid=="number"&&r(c.pid)&&(i=!0)}catch{}if(!i)return{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null};let o=[];return s(n.port)||o.push("daemon.port"),s(n.token)||o.push("daemon.token"),o.length===0?{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null}:{flagged:!0,severity:"critical",daemonAlive:!0,missing:o,message:`Daemon process is alive (per pidfile) but companion state file${o.length===1?"":"s"} ${o.join(", ")} missing. Web UI and authenticated CLI calls will return 401 until the 30s heal tick restores them.`,remediation:"The 30-second heal tick re-creates these files automatically. To force immediate recovery: recall stop && recall start. Inspect ~/.recall/daemon.log for [state-files-audit] entries to identify the deletion source."}}function Vu(e){e.flagged&&(console.log(""),console.log(_.dim("\u2014 Daemon state files \u2014")),console.log(_.err(` \u2717 CRITICAL: daemon process alive but missing ${e.missing.join(", ")}`)),e.remediation&&console.log(_.dim(` Remediation: ${e.remediation}`)))}function qu(e){if(e.flagged){console.log(""),console.log(_.dim("\u2014 Daemon processes \u2014")),console.log(_.err(` \u2717 CRITICAL: ${e.count} daemons running (two daemons must never run in parallel)`));for(let t of e.processes){let n=kn(t.etimeSeconds)??t.etime;console.log(` ${_.dim("\u2022")} pid ${t.pid} started ${n} age ${_e(t.etimeSeconds)} ppid=${t.ppid}`)}e.message&&console.log(_.dim(" Remediation: `recall stop` (now nukes orphans too as of Phase 2.6), then `recall start`."))}}function Qu(e){if(console.log(_.dim("\u2014 MCP processes \u2014")),e.length===0){console.log(_.ok(" \u2713 no MCP children running"));return}let t=e.filter(o=>o.orphan),n=e.reduce((o,a)=>Math.max(o,a.etimeSeconds),0),s=e.length-t.length;if(console.log(` ${e.length} total, ${s} with live parent, ${t.length===0?_.ok("0 orphaned"):_.err(`${t.length} orphaned`)} (oldest ${_e(n)})`),t.length>0){console.log(_.dim(" Orphans hold a SQLite read connection and can stall WAL checkpoints. Run `recall mcp-prune` to kill them."));for(let o of t.slice(0,5))console.log(` ${_.dim("\u2022")} pid ${o.pid} age ${_e(o.etimeSeconds)} ppid=${o.ppid} (gone)`)}let r=e.filter(lt);if(r.length>0){console.log(_.warn(` ! ${r.length} MCP child${r.length===1?"":"ren"} burning CPU (likely a runaway vec0 kNN query).`));for(let o of r){let a=o.parentCommand?`parent ${o.parentCommand}`:`ppid ${o.ppid}`;console.log(_.dim(` pid ${o.pid} ${o.pcpu.toFixed(0)}%cpu ${_e(o.etimeSeconds)} elapsed (${a})`))}console.log(_.dim(" Run `recall stop --all` for one-command recovery, or `recall mcp-prune --all` to keep the daemon alive."))}let i=Mi(e);i.flagged&&i.message&&console.log(_.warn(` ! ${i.message}`))}function Zu(){let e=Vi(),t=Ki();if(console.log(_.dim("\u2014 Auto-prune (last 24h) \u2014")),console.log(` Mode: ${e}`),e==="off"){console.log(_.dim(" (auto-prune disabled \u2014 orphans + runaway MCPs will accumulate until manually reaped)"));return}e==="dry-run"?console.log(` Would-have-killed: ${t.wouldHaveKilled} ${_.dim("(dry-run only)")}`):(console.log(` Killed: ${t.killed} ${_.dim("(enabled)")}`),t.failed>0&&console.log(` Failed: ${_.warn(String(t.failed))} ${_.dim("(kill returned EPERM or similar)")}`));let n=t.byReason;console.log(` By reason: orphan_10min=${n.orphan_10min}, runaway_cpu_5min=${n.runaway_cpu_5min}`)}function yo(){let e=E(),t,n,s;try{t=e.prepare("SELECT * FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get(),n=e.prepare("SELECT * FROM migration_state WHERE status = 'completed' ORDER BY id DESC LIMIT 1").get(),s=e.prepare("SELECT * FROM migration_state WHERE status IN ('failed', 'rolled_back') ORDER BY id DESC LIMIT 1").get()}catch(i){let o=i instanceof Error?i.message:String(i);return o.includes("no such table: migration_state")||process.stderr.write(`[doctor] renderMigrationDoctorSection: ${o}
877
+ `),null}if(!t&&!n&&!s)return null;let r=["--- Migration status ---"];if(t&&(r.push(` Status: ${t.status}`),r.push(` Cursor: chunk_id=${t.cursor_chunk_id??"(none yet)"}`),r.push(` Lock pid: ${t.lock_taken_by_pid??"(released)"}`),r.push(` Old model: ${t.model_id_old}`),r.push(` New model: ${t.model_id_new}`)),n&&!t&&e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()){let o=e.prepare("SELECT CAST((julianday('now') - julianday(?)) AS INTEGER) AS days").get(n.completed_at),a=Math.max(0,30-o.days);r.push(` Backup retained: ${a} day(s) until auto-prune (daemon drops it automatically).`),r.push(` Last completed: ${n.completed_at}`),r.push(` Old model: ${n.model_id_old}`),r.push(` New model: ${n.model_id_new}`)}return s&&!t&&!n&&(r.push(` ! Last migration: ${s.status}`),r.push(` Completed at: ${s.completed_at}`),r.push(` Old model: ${s.model_id_old}`),r.push(` New model: ${s.model_id_new}`),r.push(" Remediation: inspect daemon log; re-run `recall semantic migrate` to retry")),r.join(`
878
+ `)}function ep(){let e=E();return e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()?e.prepare("SELECT COUNT(*) AS n FROM vec_chunks_v1_backup b LEFT JOIN chunk_meta cm ON cm.rowid = b.rowid WHERE cm.rowid IS NULL").get().n:0}function wo(){let e=E(),t=e.prepare("SELECT encoded_path FROM projects").all(),n=new Set(t.map(l=>l.encoded_path));if(n.size===0)return{status:"ok",staleCount:0,scanned:0,sampleFiles:[],message:"Ingest freshness: ok (no projects indexed yet \u2014 nothing to monitor)."};let s=e.prepare("SELECT file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1"),r=0,i=0,o=[];for(let l of Ln(Tt)){if(xi(l))continue;let d=ki(l);if(!d||!n.has(d))continue;r+=1;let m;try{m=pt(l).mtimeMs}catch{continue}let p=s.get(l);p&&p.skipped_reason!==null||(!p||p.file_mtime<m)&&(i+=1,o.length<5&&o.push(l))}if(i===0)return{status:"ok",staleCount:0,scanned:r,sampleFiles:[],message:`Ingest freshness: ok (scanned ${r} JSONL${r===1?"":"s"} in ${n.size} known project${n.size===1?"":"s"}, none newer than the index).`};let a=i>10?"fail":"warn",c=o.slice(0,3).map(l=>l.split(/[/\\]/).pop()??l).join(", ");return{status:a,staleCount:i,scanned:r,sampleFiles:o,message:`Ingest freshness: ${a} \u2014 ${i} JSONL${i===1?"":" files"} newer than the index (sample: ${c}). Run \`recall index\` to force a reindex, or restart the daemon if this is persistent.`}}function Ro(e=mt(ju(),".claude.json")){let t={status:"ok",configPath:e,configExists:Ce(e),findings:[]};if(!t.configExists)return t;let n;try{n=Dn(e,"utf8")}catch{return t}let s;try{s=JSON.parse(n)}catch{return t}if(!s||typeof s!="object"||Array.isArray(s))return t;let r=s.mcpServers;if(!r||typeof r!="object"||Array.isArray(r))return t;for(let[i,o]of Object.entries(r)){if(!o||typeof o!="object")continue;let a=o.args;if(!Array.isArray(a)||a.length===0)continue;let c=a[0];if(typeof c!="string"||c.length===0||!c.startsWith("/")&&!c.startsWith("~")||Ce(c))continue;let l=i==="recall";t.findings.push({name:i,stalePath:c,severity:l?"HIGH":"MEDIUM",remediation:l?"restart daemon (`recall stop && recall start`) \u2014 auto-repoint runs on boot now":"this is a third-party MCP \u2014 the vendor's package may have moved; reinstall their package"})}return t.findings.some(i=>i.severity==="HIGH")?t.status="fail":t.findings.length>0&&(t.status="warn"),t}function Ao(){let e=null;try{e=wn().enabled}catch{return{status:"ok",configEnabled:null,dbValue:null,message:null}}let t=null;try{t=E().prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value??null}catch{return{status:"ok",configEnabled:e,dbValue:null,message:null}}if(t===null)return{status:"ok",configEnabled:e,dbValue:null,message:null};if(t==="1"===e)return{status:"ok",configEnabled:e,dbValue:t,message:null};let s=`config.json says ${String(e)} but DB gate says ${t}. Recover with \`recall stop && recall start\` (boot sync re-reads config). If this recurs, grep daemon.log for \`[semantic-config] gate flip\` to identify the runtime flipper.`;return{status:"critical",configEnabled:e,dbValue:t,message:s}}function tp(e,t){let n=to(),{file:s,result:r}=so(n,e);if((r.outcome==="acknowledged"||r.outcome==="already-acknowledged")&&s!==n&&no(s),t.json){let i=r.outcome==="acknowledged"||r.outcome==="already-acknowledged"?{outcome:r.outcome,id:r.matched?.id,check:r.matched?.check,message:r.matched?.message}:{outcome:r.outcome,candidates:r.candidates??[]};process.stdout.write(`${JSON.stringify(i)}
879
+ `)}else switch(r.outcome){case"acknowledged":console.log(`acknowledged: ${r.matched?.message??"(no message)"}`);break;case"already-acknowledged":console.log(`already acknowledged: ${r.matched?.message??"(no message)"}`);break;case"not-found":console.log(`no alert matched "${e}". Run \`recall doctor\` to list current alerts.`);break;case"ambiguous":console.log(`"${e}" matched ${r.candidates?.length??0} alerts \u2014 use a longer prefix:`);for(let i of r.candidates??[])console.log(` ${i}`);break}return r.outcome==="not-found"||r.outcome==="ambiguous"?1:0}async function np(e={}){if(typeof e.ack=="string"&&e.ack.length>0)return tp(e.ack,{json:!!e.json});let t=Ju(!e.json);t.stage("Scanning session aliases");let n=E().prepare(`SELECT sa.session_id AS session_id, sa.alias AS alias, s.cwd AS cwd
858
880
  FROM session_aliases sa
859
881
  LEFT JOIN sessions s ON s.id = sa.session_id
860
- WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),s=wu(n),r=Zi(t.stage);t.stage("Probing daemon");let i=await qi();t.stage("Detecting label collisions");let a=Qi();t.stage("Checking ingest freshness");let o=ro();t.stage("Checking ~/.claude.json MCP paths");let c=io();t.stage("Checking semantic gate drift");let l=oo();t.stage("Sampling chunk_queue growth");let d=kn();t.stage("Scanning for sibling daemons");let m=eo();t.stage("Checking daemon state files");let p=to({liveDaemons:m.processes});if(t.done(),e.json){process.stdout.write(JSON.stringify({scanned:n.length,violations:s.length,items:s,health:r,pipeline:i,labelCollisions:a,ingestFreshness:o,staleMcpEntries:c,semanticGateDrift:l,chunkQueueGrowth:d,siblingDaemons:m,daemonStateFiles:p},null,2)),process.stdout.write(`
861
- `);let u=i.state==="degraded",f=o.status==="fail",T=c.status==="fail",R=d.status==="critical",k=m.flagged,w=p.flagged,L=l.status==="critical";return s.length===0&&r.db.integrity==="ok"&&!u&&!f&&!T&&!R&&!k&&!L&&!w?0:1}console.log(_.dim("\u2014 System health \u2014")),console.log(` Database ${j(r.db.sizeBytes)} (${r.rows.messages.toLocaleString()} messages across ${r.rows.sessions.toLocaleString()} sessions, ${r.rows.projects.toLocaleString()} projects)`);{let u=r.wal,f=u.level==="error"?_.err(j(u.sizeBytes)):u.level==="warn"?_.warn(j(u.sizeBytes)):_.ok(j(u.sizeBytes)),T=u.level==="error"?" (readers are pinning the checkpoint frontier \u2014 see warnings below)":u.level==="warn"?" (above 100 MB \u2014 daemon will WARN until it drops)":" (daemon checkpoints PASSIVE every 60s, RESTART above 5 GB)";console.log(` WAL ${f}${T}`)}console.log(` Free pages ${r.db.freelistCount.toLocaleString()} (${j(r.db.freelistBytes)} reclaimable via VACUUM)`);{let u=r.fts.messages.fragments,f=r.fts.sessions.fragments,T=u===0&&f===0;console.log(` FTS segments messages=${u}, sessions=${f}`+(T?" (merged \u2014 search uses the index)":" (lower is faster \u2014 `recall optimize` merges them)"))}if(r.chunkQueue.size>0){let f=r.chunkQueue.size>1e5?_.warn(r.chunkQueue.size.toLocaleString()):r.chunkQueue.size.toLocaleString();console.log(` Embed queue ${f}`+(r.chunkQueue.semanticEnabled?" (worker is enabled; should drain over time)":" (semantic disabled \u2014 should be 0; if not, schema migration may be stale)"))}if(console.log(` Vector rows ${r.vectors.rows.toLocaleString()}`),r.disk.totalBytes>0){let u=r.disk.freeBytes/r.disk.totalBytes*100,f=r.disk.backups.severity,T=`${u.toFixed(1)}%`,R=f==="high"?_.err(T):f==="medium"?_.warn(T):_.ok(T);console.log(` Disk free ${j(r.disk.freeBytes)} of ${j(r.disk.totalBytes)} (${R})`)}if(r.disk.backups.fileCount>0){let u=r.disk.backups,f=j(u.totalBytes),T=u.severity==="high"?_.err(f):u.severity==="medium"?_.warn(f):f;console.log(` Snapshot files ${T} across ${u.fileCount} file${u.fileCount===1?"":"s"} in ~/.recall/`+(u.oldFiles.length>0?` (${u.oldFiles.length} older than 30 days)`:" (all recent \u2014 likely rollback-critical)"))}if(console.log(` Integrity ${r.db.integrity==="ok"?_.ok("ok"):_.err(r.db.integrity)}`),r.warnings.length>0){console.log("");for(let u of r.warnings)console.log(` ${_.warn("!")} ${u}`)}console.log(""),vu(m),Ou(p),Iu(r.mcpProcesses),console.log(""),Cu();let g=no();if(g!==null&&(console.log(""),console.log(g)),console.log(""),console.log(_.dim("\u2014 Ingest freshness \u2014")),o.status==="ok")console.log(_.ok(` \u2713 ${o.scanned.toLocaleString()} JSONL${o.scanned===1?"":"s"} scanned, none newer than the index`));else if(o.status==="warn"){console.log(` ${_.warn(`${o.staleCount} stale file${o.staleCount===1?"":"s"}`)} newer than the index \u2014 live watcher may be missing events`);for(let u of o.sampleFiles.slice(0,5))console.log(` ${_.dim("\u2022")} ${u}`);console.log(_.dim(" Run `recall index` to force a reindex, or restart the daemon if persistent."))}else{console.log(` ${_.err(`${o.staleCount} stale file${o.staleCount===1?"":"s"}`)} newer than the index \u2014 significant ingest gap`);for(let u of o.sampleFiles.slice(0,5))console.log(` ${_.dim("\u2022")} ${u}`);console.log(_.err(" Restart the daemon (recall stop && recall start) \u2014 the live watcher has fallen behind."))}console.log(""),console.log(_.dim("\u2014 chunk_queue growth \u2014"));{let u=d,f=u.status,T=u.currentSize.toLocaleString(),R=u.lastHourGrowth!==null?`\u0394 ${u.lastHourGrowth>=0?"+":""}${u.lastHourGrowth.toLocaleString()}/h`:"no prior sample in last hour",k=u.semanticEnabled?_.dim("semantic=on"):_.dim("semantic=off");f==="ok"?console.log(_.ok(` \u2713 ${T} rows, ${R}`)+` ${k}`+_.dim(` (${u.priorSampleCount} prior sample${u.priorSampleCount===1?"":"s"} in last hour)`)):f==="critical"?(console.log(` ${_.err("CRITICAL")} ${T} rows, ${R} ${k}`),u.remediation&&console.log(_.dim(` ${u.remediation}`))):f==="high"?(console.log(` ${_.warn("HIGH")} ${T} rows, ${R} ${k}`),u.remediation&&console.log(_.dim(` ${u.remediation}`))):(console.log(` ${_.warn("MEDIUM")} ${T} rows, ${R} ${k}`),u.remediation&&console.log(_.dim(` ${u.remediation}`)))}if(console.log(""),console.log(_.dim("\u2014 ~/.claude.json MCP paths \u2014")),!c.configExists)console.log(_.dim(" (no ~/.claude.json on disk \u2014 skipped)"));else if(c.findings.length===0)console.log(_.ok(" \u2713 all MCP server script paths exist on disk"));else{let u=c.findings.filter(T=>T.severity==="HIGH"),f=c.findings.filter(T=>T.severity==="MEDIUM");if(u.length>0){console.log(` ${_.err(`${u.length} stale entry`)}${u.length===1?"":" (HIGH)"} that we own:`);for(let T of u)console.log(` ${_.err("\u2717")} ${T.name} ${_.dim("\u2192")} ${T.stalePath}`),console.log(` ${_.dim("Remediation:")} ${T.remediation}`)}if(f.length>0){console.log(` ${_.warn(`${f.length} third-party stale entr${f.length===1?"y":"ies"}`)} (MEDIUM):`);for(let T of f)console.log(` ${_.warn("!")} ${T.name} ${_.dim("\u2192")} ${T.stalePath}`),console.log(` ${_.dim("Remediation:")} ${T.remediation}`)}}if(console.log(""),console.log(_.dim("\u2014 Semantic gate drift \u2014")),l.status==="ok"?l.configEnabled!==null&&l.dbValue!==null?console.log(_.ok(` \u2713 config.json + DB gate agree (semantic.enabled=${String(l.configEnabled)}, gate=${l.dbValue})`)):console.log(_.dim(" (no gate row to compare \u2014 skipped)")):(console.log(` ${_.err("CRITICAL")} config.json semantic.enabled=${String(l.configEnabled)} but DB gate=${l.dbValue}`),l.message&&console.log(_.dim(` ${l.message}`))),console.log(""),console.log(_.dim("\u2014 Pipeline health (tab-name \u2192 session alias) \u2014")),i.daemon.running?console.log(` Daemon ${_.ok("running")} (port ${i.daemon.port}, version ${i.daemon.version??"?"}, up ${i.daemon.uptimeSeconds!==null?Math.round(i.daemon.uptimeSeconds/60)+" min":"?"})`):console.log(` Daemon ${_.warn("not reachable")}`),i.runtime.silentTerminalRejections!==null){let u=i.runtime.silentTerminalRejections;console.log(` Auth rejections ${u===0?_.ok("0"):_.err(u.toLocaleString())} (extension /api/terminal/* requests denied without a valid X-Recall-Token)`)}if(i.runtime.autoExtract){let u=i.runtime.autoExtract;u.circuitBroken?console.log(` Auto-extract ${_.err("circuit broken")} (${u.reason??"unknown"} \u2014 toggle off/on to reset)`):u.consecutiveZeroTokenRuns>0&&console.log(` Auto-extract ${_.warn(`${u.consecutiveZeroTokenRuns} zero-token run(s)`)} (breaker trips at 3)`)}if(i.runtime.lastTerminalSyncAt!==null){let u=Date.now()-Date.parse(i.runtime.lastTerminalSyncAt),f=Number.isFinite(u)?Math.round(u/6e4):null,T=f!==null&&u>Ki;console.log(` Last ext sync ${T?_.warn(`${f} min ago`):_.ok(f===0?"just now":`${f} min ago`)} (most recent successful POST /api/terminal/sync)`)}else i.daemon.running&&console.log(` Last ext sync ${_.warn("never")} (no extension has called /api/terminal/sync since the daemon started)`);if(i.terminalsJson.exists&&i.terminalsJson.ageSeconds!==null){let u=Math.round(i.terminalsJson.ageSeconds/3600),f=i.terminalsJson.ageSeconds>24*3600;console.log(` terminals.json ${f?_.warn(`${u}h old`):_.ok(`${u}h old`)} (persisted registry mtime \u2014 fresh means extensions are connecting)`)}let h=i.recentSessions;if(h.total>0){let u=Math.round(h.fractionHeuristic*100),f=h.fractionHeuristic>=Ji&&h.total>=3;console.log(` Recent titles ${f?_.err(`${u}% heuristic`):_.ok(`${u}% heuristic`)} (${h.heuristicOnly}/${h.total} sessions in last ${vn}h fell back to first-message title)`)}if(i.flags.length>0){console.log("");for(let u of i.flags)console.log(` ${_.warn("!")} ${u}`)}if(console.log(""),console.log(_.dim("\u2014 Label collisions (last 7d) \u2014")),a.length===0)console.log(_.ok(" \u2713 no session-list label collisions in the last week"));else{let u=a.filter(f=>!f.anyAliased);console.log(` ${_.warn(`${a.length} group(s)`)} of 2+ sessions in the same project share an identical display label \u2014 N parallel runs ended up with the same heuristic title.`);for(let f of a.slice(0,5)){let T=f.anyAliased?_.dim("partial alias"):_.warn("NO alias"),R=f.label.length>60?`${f.label.slice(0,57)}\u2026`:f.label;console.log(` ${_.dim(`${f.count}\xD7`)} ${R} ${T} ${_.dim(`(${f.project})`)}`)}a.length>5&&console.log(_.dim(` \u2026 and ${a.length-5} more (--json for full list)`)),console.log(""),console.log(_.dim(" The display layer auto-disambiguates these (HH:MM / msgs / UUID suffix), so customers\n never see two identical rows. To fix at the source \u2014 when spawning `claude -p` from\n a script or Captain-Code subordinate, prepend each prompt with:")),console.log(_.dim(" <!-- claude-recall-alias: T2.1 Run-Prospects -->")),console.log(_.dim(` The watcher will read the header, alias the session, and strip the marker from the
862
- displayed first user message. See docs/HANDOFF.md \u2192 "Alias header convention".`)),u.length>0&&(console.log(""),console.log(_.warn(` ${u.length} of these groups have NO alias on any row \u2014 strongest candidates
863
- for the header-alias fix above.`)))}if(console.log(""),console.log(_.dim("\u2014 Tab-name invariant \u2014")),s.length===0)return console.log(_.ok(` \u2713 holds across ${n.length.toLocaleString()} aliased session${n.length===1?"":"s"}`)),console.log(_.dim(" No fabricated origin labels (`VS Code \xB7 cwd \xB7 branch`) found. No deprecated cwd-branch synthesis found.")),r.db.integrity==="ok"&&o.status!=="fail"&&c.status!=="fail"&&d.status!=="critical"&&!m.flagged&&l.status!=="critical"&&!p.flagged?0:1;console.log(_.err(`\u2717 ${s.length} invariant violation${s.length===1?"":"s"} found across ${n.length.toLocaleString()} aliased sessions`)),console.log("");let S=new Map;for(let u of s){let f=S.get(u.violation)??[];f.push(u),S.set(u.violation,f)}for(let[u,f]of S){console.log(_.warn(` ${u} (${f.length})`));for(let T of f.slice(0,10))console.log(` ${T.session_id.slice(0,8)} ${_.dim("\u2192")} ${JSON.stringify(T.alias)}`);f.length>10&&console.log(_.dim(` \u2026 and ${f.length-10} more (rerun with --json for the full list)`)),console.log("")}return console.log(_.dim('Remediation: `recall name <id-prefix> ""` clears a bad alias so the heuristic title takes over,\nor `recall name <id-prefix> "<actual tab name>"` sets it to the real value.')),1}function Pu(){try{let e=Xi(y);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}var bu,yu,zi,Ki,vn,Ji,co=b(()=>{"use strict";mn();x();I();gi();lt();An();Ri();Ni();ye();xn();Di();Hi();Tn();bu=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],yu=new RegExp(`^(${bu.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);zi=50;Ki=5*6e4,vn=24,Ji=.5});var lo={};be(lo,{runOptimize:()=>Hu});import{existsSync as Fu,readFileSync as $u}from"node:fs";import{join as Uu}from"node:path";function ju(){let e=Uu(y,"daemon.pid");if(!Fu(e))return!1;try{let t=parseInt($u(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function De(e,t){let n=Date.now();try{return t(),{step:e,ok:!0,durationMs:Date.now()-n}}catch(s){return{step:e,ok:!1,durationMs:Date.now()-n,error:s.message}}}async function Hu(e={}){let t=E(),n=[];if(e.vacuum&&ju())return e.json?(process.stdout.write(JSON.stringify({ok:!1,error:"daemon-running",message:"VACUUM requires the daemon to be stopped. Run `recall stop`, then re-run with --vacuum."},null,2)+`
882
+ WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),s=Bu(n);t.stage("Probing daemon");let r=await _o(),i=So(t.stage);t.stage("Detecting label collisions");let o=Eo();t.stage("Checking ingest freshness");let a=wo();t.stage("Checking ~/.claude.json MCP paths");let c=Ro();t.stage("Checking semantic gate drift");let l=Ao();t.stage("Sampling chunk_queue growth");let d=In();t.stage("Scanning for sibling daemons");let m=To();t.stage("Checking daemon state files");let p=bo({liveDaemons:m.processes});if(t.done(),e.json){process.stdout.write(JSON.stringify({scanned:n.length,violations:s.length,items:s,health:i,pipeline:r,labelCollisions:o,ingestFreshness:a,staleMcpEntries:c,semanticGateDrift:l,chunkQueueGrowth:d,siblingDaemons:m,daemonStateFiles:p},null,2)),process.stdout.write(`
883
+ `);let u=r.state==="degraded",f=a.status==="fail",T=c.status==="fail",R=d.status==="critical",k=m.flagged,w=p.flagged,L=l.status==="critical";return s.length===0&&i.db.integrity==="ok"&&!u&&!f&&!T&&!R&&!k&&!L&&!w?0:1}console.log(_.dim("\u2014 System health \u2014")),console.log(` Database ${j(i.db.sizeBytes)} (${i.rows.messages.toLocaleString()} messages across ${i.rows.sessions.toLocaleString()} sessions, ${i.rows.projects.toLocaleString()} projects)`);{let u=i.wal,f=u.level==="error"?_.err(j(u.sizeBytes)):u.level==="warn"?_.warn(j(u.sizeBytes)):_.ok(j(u.sizeBytes)),T=u.level==="error"?" (readers are pinning the checkpoint frontier \u2014 see warnings below)":u.level==="warn"?" (above 100 MB \u2014 daemon will WARN until it drops)":" (daemon checkpoints PASSIVE every 60s, RESTART above 5 GB)";console.log(` WAL ${f}${T}`)}console.log(` Free pages ${i.db.freelistCount.toLocaleString()} (${j(i.db.freelistBytes)} reclaimable via VACUUM)`);{let u=i.fts.messages.fragments,f=i.fts.sessions.fragments,T=u===0&&f===0;console.log(` FTS segments messages=${u}, sessions=${f}`+(T?" (merged \u2014 search uses the index)":" (lower is faster \u2014 `recall optimize` merges them)"))}if(i.chunkQueue.size>0){let f=i.chunkQueue.size>1e5?_.warn(i.chunkQueue.size.toLocaleString()):i.chunkQueue.size.toLocaleString();console.log(` Embed queue ${f}`+(i.chunkQueue.semanticEnabled?" (worker is enabled; should drain over time)":" (semantic disabled \u2014 should be 0; if not, schema migration may be stale)"))}if(console.log(` Vector rows ${i.vectors.rows.toLocaleString()}`),i.disk.totalBytes>0){let u=i.disk.freeBytes/i.disk.totalBytes*100,f=i.disk.backups.severity,T=`${u.toFixed(1)}%`,R=f==="high"?_.err(T):f==="medium"?_.warn(T):_.ok(T);console.log(` Disk free ${j(i.disk.freeBytes)} of ${j(i.disk.totalBytes)} (${R})`)}if(i.disk.backups.fileCount>0){let u=i.disk.backups,f=j(u.totalBytes),T=u.severity==="high"?_.err(f):u.severity==="medium"?_.warn(f):f;console.log(` Snapshot files ${T} across ${u.fileCount} file${u.fileCount===1?"":"s"} in ~/.recall/`+(u.oldFiles.length>0?` (${u.oldFiles.length} older than 30 days)`:" (all recent \u2014 likely rollback-critical)"))}if(console.log(` Integrity ${i.db.integrity==="ok"?_.ok("ok"):_.err(i.db.integrity)}`),i.warnings.length>0){console.log("");for(let u of i.warnings)console.log(` ${_.warn("!")} ${u}`)}console.log(""),qu(m),Vu(p),Qu(i.mcpProcesses),console.log(""),Zu();let g=yo();if(g!==null&&(console.log(""),console.log(g)),console.log(""),console.log(_.dim("\u2014 Ingest freshness \u2014")),a.status==="ok")console.log(_.ok(` \u2713 ${a.scanned.toLocaleString()} JSONL${a.scanned===1?"":"s"} scanned, none newer than the index`));else if(a.status==="warn"){console.log(` ${_.warn(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 live watcher may be missing events`);for(let u of a.sampleFiles.slice(0,5))console.log(` ${_.dim("\u2022")} ${u}`);console.log(_.dim(" Run `recall index` to force a reindex, or restart the daemon if persistent."))}else{console.log(` ${_.err(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 significant ingest gap`);for(let u of a.sampleFiles.slice(0,5))console.log(` ${_.dim("\u2022")} ${u}`);console.log(_.err(" Restart the daemon (recall stop && recall start) \u2014 the live watcher has fallen behind."))}console.log(""),console.log(_.dim("\u2014 chunk_queue growth \u2014"));{let u=d,f=u.status,T=u.currentSize.toLocaleString(),R=u.lastHourGrowth!==null?`\u0394 ${u.lastHourGrowth>=0?"+":""}${u.lastHourGrowth.toLocaleString()}/h`:"no prior sample in last hour",k=u.semanticEnabled?_.dim("semantic=on"):_.dim("semantic=off");f==="ok"?console.log(_.ok(` \u2713 ${T} rows, ${R}`)+` ${k}`+_.dim(` (${u.priorSampleCount} prior sample${u.priorSampleCount===1?"":"s"} in last hour)`)):f==="critical"?(console.log(` ${_.err("CRITICAL")} ${T} rows, ${R} ${k}`),u.remediation&&console.log(_.dim(` ${u.remediation}`))):f==="high"?(console.log(` ${_.warn("HIGH")} ${T} rows, ${R} ${k}`),u.remediation&&console.log(_.dim(` ${u.remediation}`))):(console.log(` ${_.warn("MEDIUM")} ${T} rows, ${R} ${k}`),u.remediation&&console.log(_.dim(` ${u.remediation}`)))}if(console.log(""),console.log(_.dim("\u2014 ~/.claude.json MCP paths \u2014")),!c.configExists)console.log(_.dim(" (no ~/.claude.json on disk \u2014 skipped)"));else if(c.findings.length===0)console.log(_.ok(" \u2713 all MCP server script paths exist on disk"));else{let u=c.findings.filter(T=>T.severity==="HIGH"),f=c.findings.filter(T=>T.severity==="MEDIUM");if(u.length>0){console.log(` ${_.err(`${u.length} stale entry`)}${u.length===1?"":" (HIGH)"} that we own:`);for(let T of u)console.log(` ${_.err("\u2717")} ${T.name} ${_.dim("\u2192")} ${T.stalePath}`),console.log(` ${_.dim("Remediation:")} ${T.remediation}`)}if(f.length>0){console.log(` ${_.warn(`${f.length} third-party stale entr${f.length===1?"y":"ies"}`)} (MEDIUM):`);for(let T of f)console.log(` ${_.warn("!")} ${T.name} ${_.dim("\u2192")} ${T.stalePath}`),console.log(` ${_.dim("Remediation:")} ${T.remediation}`)}}if(console.log(""),console.log(_.dim("\u2014 Semantic gate drift \u2014")),l.status==="ok"?l.configEnabled!==null&&l.dbValue!==null?console.log(_.ok(` \u2713 config.json + DB gate agree (semantic.enabled=${String(l.configEnabled)}, gate=${l.dbValue})`)):console.log(_.dim(" (no gate row to compare \u2014 skipped)")):(console.log(` ${_.err("CRITICAL")} config.json semantic.enabled=${String(l.configEnabled)} but DB gate=${l.dbValue}`),l.message&&console.log(_.dim(` ${l.message}`))),console.log(""),console.log(_.dim("\u2014 Pipeline health (tab-name \u2192 session alias) \u2014")),r.daemon.running?console.log(` Daemon ${_.ok("running")} (port ${r.daemon.port}, version ${r.daemon.version??"?"}, up ${r.daemon.uptimeSeconds!==null?Math.round(r.daemon.uptimeSeconds/60)+" min":"?"})`):console.log(` Daemon ${_.warn("not reachable")}`),r.runtime.silentTerminalRejections!==null){let u=r.runtime.silentTerminalRejections;console.log(` Auth rejections ${u===0?_.ok("0"):_.err(u.toLocaleString())} (extension /api/terminal/* requests denied without a valid X-Recall-Token)`)}if(r.runtime.autoExtract){let u=r.runtime.autoExtract;u.circuitBroken?console.log(` Auto-extract ${_.err("circuit broken")} (${u.reason??"unknown"} \u2014 toggle off/on to reset)`):u.consecutiveZeroTokenRuns>0&&console.log(` Auto-extract ${_.warn(`${u.consecutiveZeroTokenRuns} zero-token run(s)`)} (breaker trips at 3)`)}if(r.runtime.lastTerminalSyncAt!==null){let u=Date.now()-Date.parse(r.runtime.lastTerminalSyncAt),f=Number.isFinite(u)?Math.round(u/6e4):null,T=f!==null&&u>go;console.log(` Last ext sync ${T?_.warn(`${f} min ago`):_.ok(f===0?"just now":`${f} min ago`)} (most recent successful POST /api/terminal/sync)`)}else r.daemon.running&&console.log(` Last ext sync ${_.warn("never")} (no extension has called /api/terminal/sync since the daemon started)`);if(r.terminalsJson.exists&&r.terminalsJson.ageSeconds!==null){let u=Math.round(r.terminalsJson.ageSeconds/3600),f=r.terminalsJson.ageSeconds>24*3600;console.log(` terminals.json ${f?_.warn(`${u}h old`):_.ok(`${u}h old`)} (persisted registry mtime \u2014 fresh means extensions are connecting)`)}let h=r.recentSessions;if(h.total>0){let u=Math.round(h.fractionHeuristic*100),f=h.fractionHeuristic>=fo&&h.total>=3;console.log(` Recent titles ${f?_.err(`${u}% heuristic`):_.ok(`${u}% heuristic`)} (${h.heuristicOnly}/${h.total} sessions in last ${Mn}h fell back to first-message title)`)}if(r.flags.length>0){console.log("");for(let u of r.flags)console.log(` ${_.warn("!")} ${u}`)}if(console.log(""),console.log(_.dim("\u2014 Label collisions (last 7d) \u2014")),o.length===0)console.log(_.ok(" \u2713 no session-list label collisions in the last week"));else{let u=o.filter(f=>!f.anyAliased);console.log(` ${_.warn(`${o.length} group(s)`)} of 2+ sessions in the same project share an identical display label \u2014 N parallel runs ended up with the same heuristic title.`);for(let f of o.slice(0,5)){let T=f.anyAliased?_.dim("partial alias"):_.warn("NO alias"),R=f.label.length>60?`${f.label.slice(0,57)}\u2026`:f.label;console.log(` ${_.dim(`${f.count}\xD7`)} ${R} ${T} ${_.dim(`(${f.project})`)}`)}o.length>5&&console.log(_.dim(` \u2026 and ${o.length-5} more (--json for full list)`)),console.log(""),console.log(_.dim(" The display layer auto-disambiguates these (HH:MM / msgs / UUID suffix), so customers\n never see two identical rows. To fix at the source \u2014 when spawning `claude -p` from\n a script or Captain-Code subordinate, prepend each prompt with:")),console.log(_.dim(" <!-- claude-recall-alias: T1.3 \u2014 Auth Refactor -->")),console.log(_.dim(` The watcher will read the header, alias the session, and strip the marker from the
884
+ displayed first user message. See https://clauderecall.com/docs \u2192 "Alias header convention".`)),u.length>0&&(console.log(""),console.log(_.warn(` ${u.length} of these groups have NO alias on any row \u2014 strongest candidates
885
+ for the header-alias fix above.`)))}if(console.log(""),console.log(_.dim("\u2014 Tab-name invariant \u2014")),s.length===0)return console.log(_.ok(` \u2713 holds across ${n.length.toLocaleString()} aliased session${n.length===1?"":"s"}`)),console.log(_.dim(" No fabricated origin labels (`VS Code \xB7 cwd \xB7 branch`) found. No deprecated cwd-branch synthesis found.")),i.db.integrity==="ok"&&a.status!=="fail"&&c.status!=="fail"&&d.status!=="critical"&&!m.flagged&&l.status!=="critical"&&!p.flagged?0:1;console.log(_.err(`\u2717 ${s.length} invariant violation${s.length===1?"":"s"} found across ${n.length.toLocaleString()} aliased sessions`)),console.log("");let S=new Map;for(let u of s){let f=S.get(u.violation)??[];f.push(u),S.set(u.violation,f)}for(let[u,f]of S){console.log(_.warn(` ${u} (${f.length})`));for(let T of f.slice(0,10))console.log(` ${T.session_id.slice(0,8)} ${_.dim("\u2192")} ${JSON.stringify(T.alias)}`);f.length>10&&console.log(_.dim(` \u2026 and ${f.length-10} more (rerun with --json for the full list)`)),console.log("")}return console.log(_.dim('Remediation: `recall name <id-prefix> ""` clears a bad alias so the heuristic title takes over,\nor `recall name <id-prefix> "<actual tab name>"` sets it to the real value.')),1}function sp(){try{let e=lo(y);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}var Hu,Wu,po,go,Mn,fo,oo,Lo=b(()=>{"use strict";hn();x();Pe();I();Oi();dt();xn();Hi();Bi();we();Cn();qi();ro();Rn();Hu=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],Wu=new RegExp(`^(${Hu.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);po=50;go=5*6e4,Mn=24,fo=.5;oo=5e3});var ko={};ye(ko,{runOptimize:()=>cp});import{existsSync as rp,readFileSync as ip}from"node:fs";import{join as op}from"node:path";function ap(){let e=op(y,"daemon.pid");if(!rp(e))return!1;try{let t=parseInt(ip(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function De(e,t){let n=Date.now();try{return t(),{step:e,ok:!0,durationMs:Date.now()-n}}catch(s){return{step:e,ok:!1,durationMs:Date.now()-n,error:s.message}}}async function cp(e={}){let t=E(),n=[];if(e.vacuum&&ap())return e.json?(process.stdout.write(JSON.stringify({ok:!1,error:"daemon-running",message:"VACUUM requires the daemon to be stopped. Run `recall stop`, then re-run with --vacuum."},null,2)+`
864
886
  `),2):(console.error(_.err("\u2717 VACUUM requires the daemon to be stopped. Run `recall stop` first, then re-run with --vacuum.")),2);n.push(await De("wal_checkpoint(TRUNCATE)",()=>{t.pragma("wal_checkpoint(TRUNCATE)")})),n.push(await De("messages_fts optimize",()=>{t.exec("INSERT INTO messages_fts(messages_fts) VALUES('optimize');")})),n.push(await De("sessions_fts optimize",()=>{t.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize');")})),n.push(await De("PRAGMA optimize",()=>{t.exec("PRAGMA optimize")})),e.vacuum&&n.push(await De("VACUUM",()=>{t.exec("VACUUM")}));let s=n.filter(r=>!r.ok);if(e.json)return process.stdout.write(JSON.stringify({ok:s.length===0,steps:n,vacuum:!!e.vacuum},null,2)+`
865
- `),s.length===0?0:1;for(let r of n){let i=r.ok?_.ok("\u2713"):_.err("\u2717"),a=`${r.durationMs} ms`;console.log(` ${i} ${r.step.padEnd(28)} ${_.dim(a)}`),r.error&&console.log(` ${_.err(r.error)}`)}return s.length===0?(console.log(""),console.log(_.ok("All maintenance passes completed.")),e.vacuum||console.log(_.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from deleted rows.")),0):(console.log(""),console.log(_.warn(`${s.length} step(s) failed \u2014 review the errors above.`)),1)}var uo=b(()=>{"use strict";mn();x();I()});x();import{McpServer as Wu}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as Bu}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as N}from"zod";import{fileURLToPath as Xu}from"node:url";ye();function jn(e){let t=e.onShutdown,n=e.pollIntervalMs??5e3,s=e.logger??(f=>{process.stderr.write(f+`
866
- `)}),r=e.stdin??process.stdin,i=e.getPpid??(()=>process.ppid),a=e.getPid??(()=>process.pid),o=e.startupGraceMs??1500,c=e.now??(()=>process.uptime()*1e3),l=!1,d=null,m=i(),p=()=>{d&&(clearInterval(d),d=null),r.removeListener("end",S),r.removeListener("close",u)},g=f=>{if(l)return;l=!0,s(`[parent-death-guard] shutdown triggered: ${f} (pid=${a()} initialPpid=${m} currentPpid=${i()})`),p();let T;try{T=t()}catch(R){s(`[parent-death-guard] onShutdown threw: ${ne(R)}`);return}T&&typeof T.then=="function"&&T.catch(R=>{s(`[parent-death-guard] onShutdown rejected: ${ne(R)}`)})},h=()=>o>0&&c()<o,S=()=>{if(h()){s(`[parent-death-guard] stdin end ignored during startup grace (${o}ms; ppid=${i()})`);return}g("stdin end")},u=()=>{if(h()){s(`[parent-death-guard] stdin close ignored during startup grace (${o}ms; ppid=${i()})`);return}g("stdin close")};return r.on("end",S),r.on("close",u),m!==1&&(d=setInterval(()=>{let f=i();f!==m&&g(`ppid changed ${m} -> ${f}`)},n),typeof d.unref=="function"&&d.unref()),{stop:p}}ye();var Hn=Date.now();function Tt(e=Date.now()){Hn=e}var yo=1800*1e3,wo=60*1e3,Ro=60*1e3,Ao=480*60*1e3;function No(){process.stderr.write(`
867
- `)}function Lo(e){return!e||typeof e!="object"?!1:e.code==="EPIPE"}function Wn(e){let t=e.onShutdown,n=e.idleThresholdMs??yo,s=e.idleCheckIntervalMs??wo,r=e.pipeProbeIntervalMs??Ro,i=e.maxLifetimeMs??Ao,a=e.pipeProbe??No,o=e.logger??(w=>{process.stderr.write(w+`
868
- `)}),c=e.now??(()=>Date.now()),l=e.setInterval??((w,L)=>setInterval(w,L)),d=e.setTimeout??((w,L)=>setTimeout(w,L)),m=e.clearInterval??(w=>clearInterval(w)),p=e.clearTimeout??(w=>clearTimeout(w));Tt(c());let g=!1,h=!1,S=null,u=null,f=null,T=w=>{if(!w)return;let L=w;if(typeof L.unref=="function")try{L.unref()}catch(A){o(`[mcp-lifecycle] unref failed (likely test stub): ${ne(A)}`)}},R=()=>{g||(g=!0,S&&(m(S),S=null),u&&(m(u),u=null),f&&(p(f),f=null))},k=w=>{if(h||g)return;h=!0,o(`[mcp-lifecycle] shutdown triggered: ${w}`),R();let L;try{L=t(w)}catch(A){o(`[mcp-lifecycle] onShutdown threw: ${ne(A)}`);return}L&&typeof L.then=="function"&&L.catch(A=>{o(`[mcp-lifecycle] onShutdown rejected: ${ne(A)}`)})};return S=l(()=>{if(h||g)return;let w=c()-Hn;w>n&&k(`idle ${Math.round(w/1e3)}s (threshold ${Math.round(n/1e3)}s)`)},s),T(S),u=l(()=>{if(!(h||g))try{a()}catch(w){Lo(w)?k("stderr EPIPE (parent closed read end)"):o(`[mcp-lifecycle] pipe probe write failed (non-EPIPE): ${ne(w)}`)}},r),T(u),f=d(()=>{k(`max lifetime ${Math.round(i/1e3/60)}m exceeded`)},i),T(f),{stop:R}}var ko=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,xo=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,Oo=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function vo(e){return e.replace(ko,"").trim()}function Io(e){let t=e.replace(xo,"[tool call]");return t=t.replace(Oo,"[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,`
887
+ `),s.length===0?0:1;for(let r of n){let i=r.ok?_.ok("\u2713"):_.err("\u2717"),o=`${r.durationMs} ms`;console.log(` ${i} ${r.step.padEnd(28)} ${_.dim(o)}`),r.error&&console.log(` ${_.err(r.error)}`)}return s.length===0?(console.log(""),console.log(_.ok("All maintenance passes completed.")),e.vacuum||console.log(_.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from deleted rows.")),0):(console.log(""),console.log(_.warn(`${s.length} step(s) failed \u2014 review the errors above.`)),1)}var xo=b(()=>{"use strict";hn();x();I()});x();import{McpServer as lp}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as dp}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as N}from"zod";import{fileURLToPath as up}from"node:url";we();function Vn(e){let t=e.onShutdown,n=e.pollIntervalMs??5e3,s=e.logger??(f=>{process.stderr.write(f+`
888
+ `)}),r=e.stdin??process.stdin,i=e.getPpid??(()=>process.ppid),o=e.getPid??(()=>process.pid),a=e.startupGraceMs??1500,c=e.now??(()=>process.uptime()*1e3),l=!1,d=null,m=i(),p=()=>{d&&(clearInterval(d),d=null),r.removeListener("end",S),r.removeListener("close",u)},g=f=>{if(l)return;l=!0,s(`[parent-death-guard] shutdown triggered: ${f} (pid=${o()} initialPpid=${m} currentPpid=${i()})`),p();let T;try{T=t()}catch(R){s(`[parent-death-guard] onShutdown threw: ${ne(R)}`);return}T&&typeof T.then=="function"&&T.catch(R=>{s(`[parent-death-guard] onShutdown rejected: ${ne(R)}`)})},h=()=>a>0&&c()<a,S=()=>{if(h()){s(`[parent-death-guard] stdin end ignored during startup grace (${a}ms; ppid=${i()})`);return}g("stdin end")},u=()=>{if(h()){s(`[parent-death-guard] stdin close ignored during startup grace (${a}ms; ppid=${i()})`);return}g("stdin close")};return r.on("end",S),r.on("close",u),m!==1&&(d=setInterval(()=>{let f=i();f!==m&&g(`ppid changed ${m} -> ${f}`)},n),typeof d.unref=="function"&&d.unref()),{stop:p}}we();var qn=Date.now();function bt(e=Date.now()){qn=e}var Ho=1800*1e3,Wo=60*1e3,Bo=60*1e3,Xo=480*60*1e3;function Go(){process.stderr.write(`
889
+ `)}function zo(e){return!e||typeof e!="object"?!1:e.code==="EPIPE"}function Qn(e){let t=e.onShutdown,n=e.idleThresholdMs??Ho,s=e.idleCheckIntervalMs??Wo,r=e.pipeProbeIntervalMs??Bo,i=e.maxLifetimeMs??Xo,o=e.pipeProbe??Go,a=e.logger??(w=>{process.stderr.write(w+`
890
+ `)}),c=e.now??(()=>Date.now()),l=e.setInterval??((w,L)=>setInterval(w,L)),d=e.setTimeout??((w,L)=>setTimeout(w,L)),m=e.clearInterval??(w=>clearInterval(w)),p=e.clearTimeout??(w=>clearTimeout(w));bt(c());let g=!1,h=!1,S=null,u=null,f=null,T=w=>{if(!w)return;let L=w;if(typeof L.unref=="function")try{L.unref()}catch(A){a(`[mcp-lifecycle] unref failed (likely test stub): ${ne(A)}`)}},R=()=>{g||(g=!0,S&&(m(S),S=null),u&&(m(u),u=null),f&&(p(f),f=null))},k=w=>{if(h||g)return;h=!0,a(`[mcp-lifecycle] shutdown triggered: ${w}`),R();let L;try{L=t(w)}catch(A){a(`[mcp-lifecycle] onShutdown threw: ${ne(A)}`);return}L&&typeof L.then=="function"&&L.catch(A=>{a(`[mcp-lifecycle] onShutdown rejected: ${ne(A)}`)})};return S=l(()=>{if(h||g)return;let w=c()-qn;w>n&&k(`idle ${Math.round(w/1e3)}s (threshold ${Math.round(n/1e3)}s)`)},s),T(S),u=l(()=>{if(!(h||g))try{o()}catch(w){zo(w)?k("stderr EPIPE (parent closed read end)"):a(`[mcp-lifecycle] pipe probe write failed (non-EPIPE): ${ne(w)}`)}},r),T(u),f=d(()=>{k(`max lifetime ${Math.round(i/1e3/60)}m exceeded`)},i),T(f),{stop:R}}var Yo=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,Ko=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,Jo=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function Vo(e){return e.replace(Yo,"").trim()}function qo(e){let t=e.replace(Ko,"[tool call]");return t=t.replace(Jo,"[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,`
869
891
 
870
- `),t.trim()}function Co(e){return e.role??e.type??"message"}function Bn(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,i=n.since?Date.parse(n.since):0,a=t.filter(d=>!(!r&&d.is_sidechain===1||i&&d.timestamp&&Date.parse(d.timestamp)<i)),o=[];n.prelude&&(o.push(n.prelude.trim()),o.push("")),o.push(`# Claude Recall, past session context (${s})`),o.push(""),o.push(`- **Project**: ${e.project_name}`),e.decoded_path&&o.push(`- **Path**: \`${e.decoded_path}\``),o.push(`- **Session ID**: \`${e.id}\``),e.started_at&&o.push(`- **Started**: ${e.started_at}`),e.ended_at&&o.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&o.push(`- **Branch**: \`${e.git_branch}\``),o.push(`- **Messages**: ${a.length}`),o.push(""),o.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."),o.push(""),o.push("---"),o.push("");let c=0,l=0;for(let d of a){let m=d.content_text??"",p=vo(m);s==="condensed"&&(p=Io(p));let g=p.length>0,h=!!d.tool_names&&d.tool_names.length>0;if(!g&&!h){l+=1;continue}let S=Co(d),u=d.timestamp?` \`${d.timestamp}\``:"";o.push(`## ${S}${u}`),o.push(""),h&&s==="condensed"&&(o.push(`_tools used: ${d.tool_names}_`),o.push("")),g&&(o.push(p),o.push("")),c+=1}return o.push("---"),o.push(""),o.push(`_${c} messages included_`+(l?`, ${l} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),o.join(`
871
- `)}$e();bt();import{existsSync as $o,mkdirSync as Dp,readFileSync as Uo,writeFileSync as Mp,chmodSync as Pp}from"node:fs";import{homedir as jo}from"node:os";import{join as Yn}from"node:path";import{z as oe}from"zod";function Ho(){return process.env.RECALL_HOME??Yn(jo(),".recall")}function Wo(){return Yn(Ho(),"config.json")}var Bo=oe.object({enabled:oe.boolean().default(!1),backend:oe.enum(["api","mcp"]).default("api"),apiKey:oe.string().optional(),model:oe.string().default("claude-opus-4-7"),maxTagsPerSession:oe.number().int().min(1).max(10).default(4),minTagsPerSession:oe.number().int().min(1).max(10).default(2),autopilot:oe.boolean().default(!1)}),yt={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function Xo(){let e=Wo();if(!$o(e))return{};try{return JSON.parse(Uo(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function wt(){let e=Xo().autoTag;if(!e)return{...yt};let t=Bo.safeParse({...yt,...e});return t.success?t.data:{...yt}}At();x();$e();He();import{z as P}from"zod";x();I();import{writeFileSync as aa,mkdirSync as ca,existsSync as la}from"node:fs";import{join as qn}from"node:path";var Nt=qn(y,"notes");function Vn(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function da(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&typeof n.synopsis=="string"&&typeof n.replaced_at=="string"):[]}catch{return[]}}function ua(){v(),la(Nt)||ca(Nt,{recursive:!0})}function pa(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:Vn(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:da(e.auto_synopsis_history)}}var ma="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function Lt(e){let t=E().prepare(`SELECT ${ma} FROM session_notes WHERE session_id = ?`).get(e);return t?pa(t):null}function Qn(e,t){let n=E(),s=new Date().toISOString(),r=n.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),i=[];return r&&(i=Vn(r.previous_versions),r.content!==t&&r.content.length>0&&i.push({content:r.content,replaced_at:s})),n.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
892
+ `),t.trim()}function Qo(e){return e.role??e.type??"message"}function Zn(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,i=n.since?Date.parse(n.since):0,o=t.filter(d=>!(!r&&d.is_sidechain===1||i&&d.timestamp&&Date.parse(d.timestamp)<i)),a=[];n.prelude&&(a.push(n.prelude.trim()),a.push("")),a.push(`# Claude Recall, past session context (${s})`),a.push(""),a.push(`- **Project**: ${e.project_name}`),e.decoded_path&&a.push(`- **Path**: \`${e.decoded_path}\``),a.push(`- **Session ID**: \`${e.id}\``),e.started_at&&a.push(`- **Started**: ${e.started_at}`),e.ended_at&&a.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&a.push(`- **Branch**: \`${e.git_branch}\``),a.push(`- **Messages**: ${o.length}`),a.push(""),a.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."),a.push(""),a.push("---"),a.push("");let c=0,l=0;for(let d of o){let m=d.content_text??"",p=Vo(m);s==="condensed"&&(p=qo(p));let g=p.length>0,h=!!d.tool_names&&d.tool_names.length>0;if(!g&&!h){l+=1;continue}let S=Qo(d),u=d.timestamp?` \`${d.timestamp}\``:"";a.push(`## ${S}${u}`),a.push(""),h&&s==="condensed"&&(a.push(`_tools used: ${d.tool_names}_`),a.push("")),g&&(a.push(p),a.push("")),c+=1}return a.push("---"),a.push(""),a.push(`_${c} messages included_`+(l?`, ${l} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),a.join(`
893
+ `)}Ue();yt();import{existsSync as sa,mkdirSync as im,readFileSync as ra,writeFileSync as om,chmodSync as am}from"node:fs";import{homedir as ia}from"node:os";import{join as ss}from"node:path";import{z as ae}from"zod";function oa(){return process.env.RECALL_HOME??ss(ia(),".recall")}function aa(){return ss(oa(),"config.json")}var ca=ae.object({enabled:ae.boolean().default(!1),backend:ae.enum(["api","mcp"]).default("api"),apiKey:ae.string().optional(),model:ae.string().default("claude-opus-4-7"),maxTagsPerSession:ae.number().int().min(1).max(10).default(4),minTagsPerSession:ae.number().int().min(1).max(10).default(2),autopilot:ae.boolean().default(!1)}),wt={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function la(){let e=aa();if(!sa(e))return{};try{return JSON.parse(ra(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Rt(){let e=la().autoTag;if(!e)return{...wt};let t=ca.safeParse({...wt,...e});return t.success?t.data:{...wt}}Nt();x();Ue();We();import{z as P}from"zod";x();I();import{writeFileSync as Na,mkdirSync as La,existsSync as ka}from"node:fs";import{join as os}from"node:path";var Lt=os(y,"notes");function as(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function xa(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&typeof n.synopsis=="string"&&typeof n.replaced_at=="string"):[]}catch{return[]}}function Oa(){v(),ka(Lt)||La(Lt,{recursive:!0})}function va(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:as(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:xa(e.auto_synopsis_history)}}var Ia="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function kt(e){let t=E().prepare(`SELECT ${Ia} FROM session_notes WHERE session_id = ?`).get(e);return t?va(t):null}function cs(e,t){let n=E(),s=new Date().toISOString(),r=n.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),i=[];return r&&(i=as(r.previous_versions),r.content!==t&&r.content.length>0&&i.push({content:r.content,replaced_at:s})),n.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
872
894
  VALUES (?, ?, ?, ?)
873
895
  ON CONFLICT(session_id) DO UPDATE SET
874
896
  content = excluded.content,
875
897
  updated_at = excluded.updated_at,
876
- previous_versions = excluded.previous_versions`).run(e,t,s,JSON.stringify(i)),ga(e,t,s),Lt(e)??{session_id:e,content:t,updated_at:s,previous_versions:i,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}function ga(e,t,n){try{ua();let s=qn(Nt,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${n} -->
877
- `;aa(s,r+t)}catch(s){console.error("[notes] mirror write failed:",s)}}It();x();var Dt=60,wa=6e4,_e=class extends Error{retryAfterMs;constructor(t){super(`MCP write rate limit exceeded \u2014 try again in ${Math.ceil(t/1e3)}s`),this.name="RateLimitError",this.retryAfterMs=t}};function Ct(e){let t=new Date().toISOString(),n;try{n=JSON.stringify(e.args??null)}catch{n='"<unserializable>"'}E().prepare(`INSERT INTO mcp_audit_events (tool, args_json, result, error_message, caller, at)
878
- VALUES (?, ?, ?, ?, ?, ?)`).run(e.tool,n,e.result,e.errorMessage??null,e.caller??null,t)}var ae=class{capacity;windowMs;hits=[];constructor(t=Dt,n=wa){this.capacity=t,this.windowMs=n}consume(t=Date.now()){if(this.evict(t),this.hits.length>=this.capacity){let n=this.hits[0],s=Math.max(1,n+this.windowMs-t);throw new _e(s)}this.hits.push(t)}remaining(t=Date.now()){return this.evict(t),Math.max(0,this.capacity-this.hits.length)}reset(){this.hits.length=0}evict(t){let n=t-this.windowMs;for(;this.hits.length>0&&this.hits[0]<n;)this.hits.shift()}};async function D(e){try{e.limiter.consume()}catch(t){throw t instanceof _e&&Ct({tool:e.tool,args:e.args,result:"rate_limited",errorMessage:t.message,caller:e.caller}),t}try{let t=await e.run();return Ct({tool:e.tool,args:e.args,result:"ok",caller:e.caller}),t}catch(t){let n=t instanceof Error?t.message:String(t);throw Ct({tool:e.tool,args:e.args,result:"error",errorMessage:n,caller:e.caller}),t}}x();import{z as Ra}from"zod";function z(e){let t=e.trim();if(t.length<4)return null;let n=E();if(t.length>=32)return n.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let s=n.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(`${t}%`);return s.length===1?s[0].id:null}function We(e){return{content:[{type:"text",text:e}],isError:!0}}function C(e){return e instanceof _e?We(e.message):e instanceof Ra.ZodError?We(`invalid input: ${e.message}`):e instanceof Error&&e.message.startsWith("SQLITE_")?We("database constraint error"):We(e instanceof Error?e.message:String(e))}function pe(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function ce(e){return{content:[{type:"text",text:e}],isError:!0}}var Aa=`
898
+ previous_versions = excluded.previous_versions`).run(e,t,s,JSON.stringify(i)),Ca(e,t,s),kt(e)??{session_id:e,content:t,updated_at:s,previous_versions:i,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}function Ca(e,t,n){try{Oa();let s=os(Lt,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${n} -->
899
+ `;Na(s,r+t)}catch(s){console.error("[notes] mirror write failed:",s)}}Ct();x();var Mt=60,Wa=6e4,he=class extends Error{retryAfterMs;constructor(t){super(`MCP write rate limit exceeded \u2014 try again in ${Math.ceil(t/1e3)}s`),this.name="RateLimitError",this.retryAfterMs=t}};function Dt(e){let t=new Date().toISOString(),n;try{n=JSON.stringify(e.args??null)}catch{n='"<unserializable>"'}E().prepare(`INSERT INTO mcp_audit_events (tool, args_json, result, error_message, caller, at)
900
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e.tool,n,e.result,e.errorMessage??null,e.caller??null,t)}var ce=class{capacity;windowMs;hits=[];constructor(t=Mt,n=Wa){this.capacity=t,this.windowMs=n}consume(t=Date.now()){if(this.evict(t),this.hits.length>=this.capacity){let n=this.hits[0],s=Math.max(1,n+this.windowMs-t);throw new he(s)}this.hits.push(t)}remaining(t=Date.now()){return this.evict(t),Math.max(0,this.capacity-this.hits.length)}reset(){this.hits.length=0}evict(t){let n=t-this.windowMs;for(;this.hits.length>0&&this.hits[0]<n;)this.hits.shift()}};async function D(e){try{e.limiter.consume()}catch(t){throw t instanceof he&&Dt({tool:e.tool,args:e.args,result:"rate_limited",errorMessage:t.message,caller:e.caller}),t}try{let t=await e.run();return Dt({tool:e.tool,args:e.args,result:"ok",caller:e.caller}),t}catch(t){let n=t instanceof Error?t.message:String(t);throw Dt({tool:e.tool,args:e.args,result:"error",errorMessage:n,caller:e.caller}),t}}x();import{z as Ba}from"zod";function z(e){let t=e.trim();if(t.length<4)return null;let n=E();if(t.length>=32)return n.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let s=n.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(`${t}%`);return s.length===1?s[0].id:null}function Be(e){return{content:[{type:"text",text:e}],isError:!0}}function C(e){return e instanceof he?Be(e.message):e instanceof Ba.ZodError?Be(`invalid input: ${e.message}`):e instanceof Error&&e.message.startsWith("SQLITE_")?Be("database constraint error"):Be(e instanceof Error?e.message:String(e))}function me(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function le(e){return{content:[{type:"text",text:e}],isError:!0}}var Xa=`
879
901
 
880
902
  ---
881
903
 
882
- `,Na=5e5;function ns(e,t={}){let n=t.limiter??new ae;e.registerTool("add_tag",{title:"Add tag to a session",description:"Apply a single tag to a session. Tags are normalized server-side (lowercase, hyphens, strip #). Idempotent.",inputSchema:{sessionId:P.string().describe("Full session UUID or 8+-character prefix."),tag:P.string().min(1).describe("Tag to add. Normalized: lowercase, dashes, max 40 chars.")}},async s=>{try{let r=z(s.sessionId);if(!r)return ce(`session not found or prefix ambiguous: ${s.sessionId}`);let i=await D({tool:"add_tag",args:{sessionId:r,tag:s.tag},limiter:n,run:()=>Pe(r,s.tag)});return pe({sessionId:r,...i})}catch(r){return C(r)}}),e.registerTool("remove_tag",{title:"Remove tag from a session",description:"Remove a single tag from a session. The removal is recorded in tag_events (append-only) so history is preserved.",inputSchema:{sessionId:P.string().describe("Full session UUID or 8+-character prefix."),tag:P.string().min(1).describe("Tag to remove (normalized server-side).")}},async s=>{try{let r=z(s.sessionId);if(!r)return ce(`session not found or prefix ambiguous: ${s.sessionId}`);let i=ue(s.tag);if(!i)return ce("tag must contain at least one alphanumeric character");let a=await D({tool:"remove_tag",args:{sessionId:r,tag:i},limiter:n,run:()=>Xn(r,i)});return pe({sessionId:r,...a})}catch(r){return C(r)}}),e.registerTool("set_alias",{title:"Set session alias",description:"Set a human-friendly alias for a session. Previous alias is archived to previous_aliases (never destroyed). Session UUID remains the immutable primary key.",inputSchema:{sessionId:P.string().describe("Full session UUID or 8+-character prefix."),alias:P.string().min(1).max(120).describe("New alias (non-empty, max 120 chars).")}},async s=>{try{let r=z(s.sessionId);if(!r)return ce(`session not found or prefix ambiguous: ${s.sessionId}`);let i=await D({tool:"set_alias",args:{sessionId:r,alias:s.alias},limiter:n,run:()=>je(r,s.alias)});return pe(i)}catch(r){return C(r)}}),e.registerTool("append_note",{title:"Append to session note",description:"Append markdown to a session note. If a note already exists, the new content is added below a `---` separator. Previous version is archived in previous_versions.",inputSchema:{sessionId:P.string().describe("Full session UUID or 8+-character prefix."),markdown:P.string().min(1).max(5e4).describe("Markdown to append.")}},async s=>{try{let r=z(s.sessionId);if(!r)return ce(`session not found or prefix ambiguous: ${s.sessionId}`);let i=await D({tool:"append_note",args:{sessionId:r,length:s.markdown.length},limiter:n,run:()=>{let a=Lt(r),o=a&&a.content.length>0?`${a.content}${Aa}${s.markdown}`:s.markdown;if(o.length>Na)throw new Error(`note would exceed the 500 KB cumulative limit (current: ${a?.content.length??0} bytes, adding: ${s.markdown.length} bytes)`);return Qn(r,o)}});return pe(i)}catch(r){return C(r)}}),e.registerTool("create_collection",{title:"Create a collection",description:"Create a new collection for grouping sessions. Optionally nest under a parent. Soft-deletion only.",inputSchema:{name:P.string().min(1).max(120),parent_id:P.string().optional().describe("Parent collection id (omit for root)."),icon:P.string().max(32).optional(),color:P.string().max(32).optional(),description:P.string().max(2e3).optional()}},async s=>{try{let r=await D({tool:"create_collection",args:s,limiter:n,run:()=>xt({name:s.name,parent_id:s.parent_id??null,icon:s.icon??null,color:s.color??null,description:s.description??null})});return pe(r)}catch(r){return C(r)}}),e.registerTool("add_session_to_collection",{title:"Add session to collection",description:"Add a session to a collection. Idempotent: re-adding returns added=false.",inputSchema:{collectionId:P.string().min(1),sessionId:P.string().describe("Full session UUID or 8+-character prefix."),note:P.string().max(2e3).optional()}},async s=>{try{let r=z(s.sessionId);if(!r)return ce(`session not found or prefix ambiguous: ${s.sessionId}`);if(!E().prepare("SELECT 1 FROM collections WHERE id = ?").get(s.collectionId))return ce("collection not found");let o=await D({tool:"add_session_to_collection",args:{collectionId:s.collectionId,sessionId:r,note:s.note??null},limiter:n,run:()=>Ot(s.collectionId,r,s.note??null)});return pe({collectionId:s.collectionId,sessionId:r,...o})}catch(r){return C(r)}}),e.registerTool("remove_session_from_collection",{title:"Remove session from collection",description:"Remove a session from a collection. The removal is recorded in collection_events (append-only); the underlying session is untouched.",inputSchema:{collectionId:P.string().min(1),sessionId:P.string().describe("Full session UUID or 8+-character prefix.")}},async s=>{try{let r=z(s.sessionId);if(!r)return ce(`session not found or prefix ambiguous: ${s.sessionId}`);let i=await D({tool:"remove_session_from_collection",args:{collectionId:s.collectionId,sessionId:r},limiter:n,run:()=>ts(s.collectionId,r)});return pe({collectionId:s.collectionId,sessionId:r,...i})}catch(r){return C(r)}})}import{z as B}from"zod";x();I();import{randomUUID as ss}from"node:crypto";import{writeFileSync as rs,readFileSync as Rm,existsSync as La,mkdirSync as ka}from"node:fs";import{join as Mt}from"node:path";var Be=Mt(y,"threads"),xa=Mt(Be,"index.json");function is(){v(),La(Be)||ka(Be,{recursive:!0})}function Pt(e,t,n){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:n?.project??null,project_count:n?.project_count??0,folder_id:e.folder_id??null}}function os(e){let t=new Map;if(e.length===0)return t;let n=E(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
904
+ `,Ga=5e5;function ps(e,t={}){let n=t.limiter??new ce;e.registerTool("add_tag",{title:"Add tag to a session",description:"Apply a single tag to a session. Tags are normalized server-side (lowercase, hyphens, strip #). Idempotent.",inputSchema:{sessionId:P.string().describe("Full session UUID or 8+-character prefix."),tag:P.string().min(1).describe("Tag to add. Normalized: lowercase, dashes, max 40 chars.")}},async s=>{try{let r=z(s.sessionId);if(!r)return le(`session not found or prefix ambiguous: ${s.sessionId}`);let i=await D({tool:"add_tag",args:{sessionId:r,tag:s.tag},limiter:n,run:()=>Fe(r,s.tag)});return me({sessionId:r,...i})}catch(r){return C(r)}}),e.registerTool("remove_tag",{title:"Remove tag from a session",description:"Remove a single tag from a session. The removal is recorded in tag_events (append-only) so history is preserved.",inputSchema:{sessionId:P.string().describe("Full session UUID or 8+-character prefix."),tag:P.string().min(1).describe("Tag to remove (normalized server-side).")}},async s=>{try{let r=z(s.sessionId);if(!r)return le(`session not found or prefix ambiguous: ${s.sessionId}`);let i=pe(s.tag);if(!i)return le("tag must contain at least one alphanumeric character");let o=await D({tool:"remove_tag",args:{sessionId:r,tag:i},limiter:n,run:()=>es(r,i)});return me({sessionId:r,...o})}catch(r){return C(r)}}),e.registerTool("set_alias",{title:"Set session alias",description:"Set a human-friendly alias for a session. Previous alias is archived to previous_aliases (never destroyed). Session UUID remains the immutable primary key.",inputSchema:{sessionId:P.string().describe("Full session UUID or 8+-character prefix."),alias:P.string().min(1).max(120).describe("New alias (non-empty, max 120 chars).")}},async s=>{try{let r=z(s.sessionId);if(!r)return le(`session not found or prefix ambiguous: ${s.sessionId}`);let i=await D({tool:"set_alias",args:{sessionId:r,alias:s.alias},limiter:n,run:()=>He(r,s.alias)});return me(i)}catch(r){return C(r)}}),e.registerTool("append_note",{title:"Append to session note",description:"Append markdown to a session note. If a note already exists, the new content is added below a `---` separator. Previous version is archived in previous_versions.",inputSchema:{sessionId:P.string().describe("Full session UUID or 8+-character prefix."),markdown:P.string().min(1).max(5e4).describe("Markdown to append.")}},async s=>{try{let r=z(s.sessionId);if(!r)return le(`session not found or prefix ambiguous: ${s.sessionId}`);let i=await D({tool:"append_note",args:{sessionId:r,length:s.markdown.length},limiter:n,run:()=>{let o=kt(r),a=o&&o.content.length>0?`${o.content}${Xa}${s.markdown}`:s.markdown;if(a.length>Ga)throw new Error(`note would exceed the 500 KB cumulative limit (current: ${o?.content.length??0} bytes, adding: ${s.markdown.length} bytes)`);return cs(r,a)}});return me(i)}catch(r){return C(r)}}),e.registerTool("create_collection",{title:"Create a collection",description:"Create a new collection for grouping sessions. Optionally nest under a parent. Soft-deletion only.",inputSchema:{name:P.string().min(1).max(120),parent_id:P.string().optional().describe("Parent collection id (omit for root)."),icon:P.string().max(32).optional(),color:P.string().max(32).optional(),description:P.string().max(2e3).optional()}},async s=>{try{let r=await D({tool:"create_collection",args:s,limiter:n,run:()=>Ot({name:s.name,parent_id:s.parent_id??null,icon:s.icon??null,color:s.color??null,description:s.description??null})});return me(r)}catch(r){return C(r)}}),e.registerTool("add_session_to_collection",{title:"Add session to collection",description:"Add a session to a collection. Idempotent: re-adding returns added=false.",inputSchema:{collectionId:P.string().min(1),sessionId:P.string().describe("Full session UUID or 8+-character prefix."),note:P.string().max(2e3).optional()}},async s=>{try{let r=z(s.sessionId);if(!r)return le(`session not found or prefix ambiguous: ${s.sessionId}`);if(!E().prepare("SELECT 1 FROM collections WHERE id = ?").get(s.collectionId))return le("collection not found");let a=await D({tool:"add_session_to_collection",args:{collectionId:s.collectionId,sessionId:r,note:s.note??null},limiter:n,run:()=>vt(s.collectionId,r,s.note??null)});return me({collectionId:s.collectionId,sessionId:r,...a})}catch(r){return C(r)}}),e.registerTool("remove_session_from_collection",{title:"Remove session from collection",description:"Remove a session from a collection. The removal is recorded in collection_events (append-only); the underlying session is untouched.",inputSchema:{collectionId:P.string().min(1),sessionId:P.string().describe("Full session UUID or 8+-character prefix.")}},async s=>{try{let r=z(s.sessionId);if(!r)return le(`session not found or prefix ambiguous: ${s.sessionId}`);let i=await D({tool:"remove_session_from_collection",args:{collectionId:s.collectionId,sessionId:r},limiter:n,run:()=>us(s.collectionId,r)});return me({collectionId:s.collectionId,sessionId:r,...i})}catch(r){return C(r)}})}import{z as B}from"zod";x();I();import{randomUUID as ms}from"node:crypto";import{writeFileSync as gs,readFileSync as Jm,existsSync as za,mkdirSync as Ya}from"node:fs";import{join as Pt}from"node:path";var Xe=Pt(y,"threads"),Ka=Pt(Xe,"index.json");function fs(){v(),za(Xe)||Ya(Xe,{recursive:!0})}function Ft(e,t,n){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:n?.project??null,project_count:n?.project_count??0,folder_id:e.folder_id??null}}function _s(e){let t=new Map;if(e.length===0)return t;let n=E(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
883
905
  p.name AS project,
884
906
  COUNT(*) AS n,
885
907
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
@@ -887,7 +909,7 @@ ${m+1}. ${p}`:`${m+1}. ${p}`}).join(`
887
909
  LEFT JOIN sessions s ON s.id = te.session_id
888
910
  LEFT JOIN projects p ON p.id = s.project_id
889
911
  WHERE te.thread_id IN (${s})
890
- GROUP BY te.thread_id, p.name`).all(...e),i=new Map;for(let a of r){let o=i.get(a.thread_id);o||(o=[],i.set(a.thread_id,o)),o.push(a)}for(let[a,o]of i){let c=o.filter(m=>m.project!==null),l=c.length,d=null;c.length>0&&(d=[...c].sort((p,g)=>g.n-p.n||g.origin_n-p.origin_n||(p.project??"").localeCompare(g.project??""))[0].project),t.set(a,{project:d,project_count:l})}return t}function as(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 cs(e){let n=E().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
912
+ GROUP BY te.thread_id, p.name`).all(...e),i=new Map;for(let o of r){let a=i.get(o.thread_id);a||(a=[],i.set(o.thread_id,a)),a.push(o)}for(let[o,a]of i){let c=a.filter(m=>m.project!==null),l=c.length,d=null;c.length>0&&(d=[...c].sort((p,g)=>g.n-p.n||g.origin_n-p.origin_n||(p.project??"").localeCompare(g.project??""))[0].project),t.set(o,{project:d,project_count:l})}return t}function hs(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 Es(e){let n=E().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
891
913
  s.auto_title AS auto_title,
892
914
  s.auto_title_source AS auto_title_source,
893
915
  s.first_user_message AS first_user_message,
@@ -895,11 +917,11 @@ ${m+1}. ${p}`:`${m+1}. ${p}`}).join(`
895
917
  FROM (SELECT ? AS sid) q
896
918
  LEFT JOIN sessions s ON s.id = q.sid
897
919
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
898
- LEFT JOIN projects p ON p.id = s.project_id`).get(e),s=n?.auto_title_source??null;return{alias:n?.alias??null,auto_title:n?.auto_title??null,auto_title_source:s==="agent"||s==="heuristic"?s:null,first_user_message:n?.first_user_message??null,project:n?.project??null}}function Ft(e){let n=E().prepare(`SELECT
920
+ LEFT JOIN projects p ON p.id = s.project_id`).get(e),s=n?.auto_title_source??null;return{alias:n?.alias??null,auto_title:n?.auto_title??null,auto_title_source:s==="agent"||s==="heuristic"?s:null,first_user_message:n?.first_user_message??null,project:n?.project??null}}function $t(e){let n=E().prepare(`SELECT
899
921
  COUNT(*) AS session_count,
900
922
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
901
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function q(e){let t=$(e);t&&(is(),rs(Mt(Be,`${e}.json`),JSON.stringify(t,null,2)),ls())}function ls(){is();let e=$t({includeArchived:!0});rs(xa,JSON.stringify({threads:e},null,2))}function Xe(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=E(),s=ss(),r=new Date().toISOString();n.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(s,t,e.summary?.trim()||null,r),e.originSessionId&&n.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
902
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),q(s);let i=$(s);if(!i)throw new Error("thread creation succeeded but read-back failed");return i}function $t(e={}){let t=E(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=os(s.map(i=>i.id));return s.map(i=>Pt(i,Ft(i.id),r.get(i.id)))}function $(e){let t=E(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
923
+ FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function V(e){let t=$(e);t&&(fs(),gs(Pt(Xe,`${e}.json`),JSON.stringify(t,null,2)),Ss())}function Ss(){fs();let e=Ut({includeArchived:!0});gs(Ka,JSON.stringify({threads:e},null,2))}function Ge(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=E(),s=ms(),r=new Date().toISOString();n.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(s,t,e.summary?.trim()||null,r),e.originSessionId&&n.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
924
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),V(s);let i=$(s);if(!i)throw new Error("thread creation succeeded but read-back failed");return i}function Ut(e={}){let t=E(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=_s(s.map(i=>i.id));return s.map(i=>Ft(i,$t(i.id),r.get(i.id)))}function $(e){let t=E(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
903
925
  NULLIF(sa.alias, '') AS alias,
904
926
  s.auto_title AS auto_title,
905
927
  s.auto_title_source AS auto_title_source,
@@ -910,10 +932,10 @@ ${m+1}. ${p}`:`${m+1}. ${p}`}).join(`
910
932
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
911
933
  LEFT JOIN projects p ON p.id = s.project_id
912
934
  WHERE e.thread_id = ?
913
- ORDER BY e.added_at ASC`).all(e).map(as),r=os([e]).get(e);return{...Pt(n,Ft(n.id),r),edges:s}}function ds(e){return E().prepare(`SELECT t.* FROM threads t
935
+ ORDER BY e.added_at ASC`).all(e).map(hs),r=_s([e]).get(e);return{...Ft(n,$t(n.id),r),edges:s}}function Ts(e){return E().prepare(`SELECT t.* FROM threads t
914
936
  JOIN thread_edges e ON e.thread_id = t.id
915
937
  WHERE e.session_id = ? AND t.archived = 0
916
- ORDER BY t.created_at DESC`).all(e).map(s=>Pt(s,Ft(s.id)))}function Ge(e){let t=E();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,i=e.role??(r?"child":"origin"),a=e.confidence??1,o=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
938
+ ORDER BY t.created_at DESC`).all(e).map(s=>Ft(s,$t(s.id)))}function ze(e){let t=E();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,i=e.role??(r?"child":"origin"),o=e.confidence??1,a=e.source??"manual";if(o<0||o>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
917
939
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
918
940
  VALUES (?, ?, ?, ?, ?, ?, ?)
919
941
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -921,35 +943,35 @@ ${m+1}. ${p}`:`${m+1}. ${p}`}).join(`
921
943
  role = excluded.role,
922
944
  confidence = excluded.confidence,
923
945
  source = excluded.source,
924
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,i,a,o,s),q(e.threadId);let c=cs(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:i,confidence:a,source:o,added_at:s,alias:c.alias,auto_title:c.auto_title,auto_title_source:c.auto_title_source,alias_source:c.alias?"manual":null,first_user_message:c.first_user_message,project:c.project}}function us(e,t){let s=E().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&q(e),{removed:s.changes}}function we(e,t,n){let s=E(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let o=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),c=n,l=new Set;for(;c!==null;){if(c===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(l.has(c))break;l.add(c),c=o.get(e,c)?.parent_session_id??null}}let i=n?"child":"origin";s.prepare(`UPDATE thread_edges
946
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,i,o,a,s),V(e.threadId);let c=Es(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:i,confidence:o,source:a,added_at:s,alias:c.alias,auto_title:c.auto_title,auto_title_source:c.auto_title_source,alias_source:c.alias?"manual":null,first_user_message:c.first_user_message,project:c.project}}function bs(e,t){let s=E().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&V(e),{removed:s.changes}}function Re(e,t,n){let s=E(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let a=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),c=n,l=new Set;for(;c!==null;){if(c===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(l.has(c))break;l.add(c),c=a.get(e,c)?.parent_session_id??null}}let i=n?"child":"origin";s.prepare(`UPDATE thread_edges
925
947
  SET parent_session_id = ?, role = ?, added_at = ?
926
- WHERE thread_id = ? AND session_id = ?`).run(n,i,new Date().toISOString(),e,t),q(e);let a=cs(t);return as({...r,parent_session_id:n,role:i,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 ps(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");E().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),q(e);let r=$(e);if(!r)throw new Error(`thread ${e} not found`);return r}function ms(e){E().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),q(e);let n=$(e);if(!n)throw new Error(`thread ${e} not found`);return n}function gs(e){E().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),q(e);let n=$(e);if(!n)throw new Error(`thread ${e} not found`);return n}function fs(e){E().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),q(e);let n=$(e);if(!n)throw new Error(`thread ${e} not found`);return n}function _s(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=E(),s=new Date().toISOString();n.transaction(()=>{let i=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of i)n.prepare(`INSERT INTO thread_edges
948
+ WHERE thread_id = ? AND session_id = ?`).run(n,i,new Date().toISOString(),e,t),V(e);let o=Es(t);return hs({...r,parent_session_id:n,role:i,added_at:new Date().toISOString(),alias:o.alias,auto_title:o.auto_title,auto_title_source:o.auto_title_source,first_user_message:o.first_user_message,project:o.project})}function ys(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");E().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),V(e);let r=$(e);if(!r)throw new Error(`thread ${e} not found`);return r}function ws(e){E().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),V(e);let n=$(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Rs(e){E().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),V(e);let n=$(e);if(!n)throw new Error(`thread ${e} not found`);return n}function As(e){E().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),V(e);let n=$(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Ns(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=E(),s=new Date().toISOString();n.transaction(()=>{let i=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let o of i)n.prepare(`INSERT INTO thread_edges
927
949
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
928
950
  VALUES (?, ?, ?, ?, ?, ?, ?)
929
951
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
930
952
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
931
953
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
932
954
  confidence = MAX(thread_edges.confidence, excluded.confidence),
933
- source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),q(t),ls();let r=$(t);if(!r)throw new Error("merge destination disappeared");return r}function hs(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=E(),n=new Date().toISOString(),s=ss();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let i of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,i);a&&(t.prepare(`INSERT INTO thread_edges
955
+ source = thread_edges.source`).run(t,o.session_id,o.parent_session_id,o.role,o.confidence,o.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),V(t),Ss();let r=$(t);if(!r)throw new Error("merge destination disappeared");return r}function Ls(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=E(),n=new Date().toISOString(),s=ms();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let i of e.sessionIds){let o=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,i);o&&(t.prepare(`INSERT INTO thread_edges
934
956
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
935
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,i,a.parent_session_id,a.role,a.confidence,a.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,i))}})(),q(e.threadId),q(s);let r=$(s);if(!r)throw new Error("split destination disappeared");return r}Bt();var Za=50;function ec(e){if(e.length===0)return[];let t=[...e].sort((c,l)=>c.added_at<l.added_at?-1:c.added_at>l.added_at?1:0),n=new Map,s=new Set,r=[];for(let c of t)if(c.parent_session_id){let l=n.get(c.parent_session_id)??[];l.push(c),n.set(c.parent_session_id,l)}let i=t.filter(c=>c.role==="origin"),o=[...i.length>0?i:t.filter(c=>!c.parent_session_id)];for(;o.length>0;){let c=o.shift();if(s.has(c.session_id))continue;s.add(c.session_id),r.push(c.session_id);let l=n.get(c.session_id)??[];for(let d of l)s.has(d.session_id)||o.push(d)}for(let c of t)s.has(c.session_id)||(s.add(c.session_id),r.push(c.session_id));return r}function tc(e){let t=As(e.sessionId),n=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
957
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,i,o.parent_session_id,o.role,o.confidence,o.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,i))}})(),V(e.threadId),V(s);let r=$(s);if(!r)throw new Error("split destination disappeared");return r}Xt();var Ec=50;function Sc(e){if(e.length===0)return[];let t=[...e].sort((c,l)=>c.added_at<l.added_at?-1:c.added_at>l.added_at?1:0),n=new Map,s=new Set,r=[];for(let c of t)if(c.parent_session_id){let l=n.get(c.parent_session_id)??[];l.push(c),n.set(c.parent_session_id,l)}let i=t.filter(c=>c.role==="origin"),a=[...i.length>0?i:t.filter(c=>!c.parent_session_id)];for(;a.length>0;){let c=a.shift();if(s.has(c.session_id))continue;s.add(c.session_id),r.push(c.session_id);let l=n.get(c.session_id)??[];for(let d of l)s.has(d.session_id)||a.push(d)}for(let c of t)s.has(c.session_id)||(s.add(c.session_id),r.push(c.session_id));return r}function Tc(e){let t=Ms(e.sessionId),n=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
936
958
  `);return`${t}
937
- ${n}`}function nc(e){return Ns(e)?.auto_title_source??null}async function sc(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(Xt(),Fs));if(!n())throw new Error("claude CLI not found on PATH");let s=await t(e.prompt,[],{model:e.model});if(!s.success)throw new Error(`claude CLI exited ${s.exitCode}: ${s.stderr.slice(-500)}`);let r=rc(s.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,Za)}function rc(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return $s(s)}}catch{}return $s(t)}function $s(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function js(e,t={}){let n=$(e);if(!n)throw new Error(`thread not found: ${e}`);let s=ec(n.edges),r=s.length,i=t.force??!1,a=t.signal,o=[],c=[],l=[];for(let d=0;d<s.length;d++){let m=s[d],p=d+1;if(a?.aborted){let h={sessionId:m,reason:"cancelled"};c.push(h),t.onSkipped?.(h);continue}if(!i&&nc(m)==="agent"){let h={sessionId:m,reason:"already-titled"};c.push(h),t.onSkipped?.(h);continue}let g;try{if(Us)g=await Us({sessionId:m,current:p,total:r});else{let h=tc({sessionId:m,current:p,total:r});g=await sc({prompt:h,model:t.model})}}catch(h){let S=h instanceof Error?h.message:String(h??"unknown error"),u={sessionId:m,error:S};l.push(u),t.onFailed?.(u);continue}try{Wt(m,g,"agent")}catch(h){let S=h instanceof Error?h.message:String(h??"unknown error"),u={sessionId:m,error:`setAutoTitle failed: ${S}`};l.push(u),t.onFailed?.(u);continue}o.push(m),t.onProgress?.({current:p,total:r,sessionId:m,title:g})}return{generated:o,skipped:c,failed:l}}var Us=null;x();import{execFile as Sc}from"node:child_process";import{promisify as Tc}from"node:util";import{readlink as bc,readFile as zs}from"node:fs/promises";import{platform as Ke}from"node:os";import{readFileSync as ic,statSync as oc}from"node:fs";var ac=200*1024*1024;var zt=.5,Bs=zt,cc=[{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"}],lc=["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 Hs(e){if(!e)return null;if(e.startsWith("/")){let n=e.split(" \xB7 ");if(n.length>1)return n[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function dc(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of cc)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function uc(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let i=r.toLowerCase();for(let a of lc)if(i.includes(a))return{weight:t[s],matched:a,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function Gt(e,t){if(!e||!t||e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function pc(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let i=Gt(s,r);i>n&&(n=i)}return n}function mc(e,t){let n=Gt(e.mean_embedding,t.mean_embedding),s=Gt(e.tail_pool,t.head_pool),r=pc(e.sample_chunks,t.sample_chunks),i=0,a=null;if(n>i&&(i=n,a="mean"),s>i&&(i=s,a="asymmetric"),r>i&&(i=r,a="max_pool"),i<.65)return{weight:0,cosine:i,mode:null};if(i>=.85)return{weight:.3,cosine:i,mode:a};let o=(i-.65)/.2*.3;return{weight:Math.round(o*100)/100,cosine:i,mode:a}}function gc(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 fc(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let n=0;for(let r of t)e.has(r)&&n++;return n===0?{weight:0,count:0}:{weight:Math.min(.4,n*.1),count:n}}function _c(e,t){let n=Hs(e),s=Hs(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function Ws(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function hc(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let i=t.recent_user_messages.slice(0,3).join(`
938
- `).toLowerCase();for(let a of e.authored_paths){let o=a.toLowerCase();if(t.touched_files.has(a)||i.includes(o)){n+=.5,s=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let i=Ws(t.recent_user_messages[0]);if(i.length>=200)for(let a of e.authored_content){let o=Ws(a);if(o.length<200)continue;let c=Math.min(o.length,240),l=o.slice(0,c);if(i.includes(l)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function Ec(e,t,n=Bs){if(t.started_at_ms<=e.started_at_ms)return null;let s=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<s)return null;let r=dc(s,t.started_at_ms),i=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=uc(i),o=fc(e.touched_files,t.touched_files),c=_c(e.auto_title,t.auto_title),l=mc(e,t),d=gc(e,t),m=hc(e,t),p=r.weight+a.weight+o.weight+c.weight+l.weight+d.weight+m.weight;if(p<n)return null;let g=[];if(r.label&&g.push(`temporal ${r.label} (+${r.weight})`),a.matched){let h=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;g.push(`continuation phrase "${a.matched}" in ${h} (+${a.weight})`)}if(o.count>0&&g.push(`${o.count} file${o.count===1?"":"s"} overlap (+${o.weight.toFixed(1)})`),c.brand&&g.push(`shared brand "${c.brand}" (+${c.weight})`),l.weight>0&&l.mode&&g.push(`semantic ${l.mode==="asymmetric"?"tail\u2192head":l.mode==="max_pool"?"best-chunk":"mean"} ${l.cosine.toFixed(2)} (+${l.weight.toFixed(2)})`),d.same&&g.push(`same cluster (+${d.weight})`),m.weight>0){let h=[];m.pathMatch&&h.push(`opened authored path "${m.pathMatch.split("/").pop()}"`),m.contentMatch&&h.push("verbatim-paste of authored content"),g.push(`doc-authorship: ${h.join(" + ")} (+${m.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,p),signals:{temporal:r.weight,continuation:a.weight,file_overlap:o.weight,same_brand:c.weight,semantic:l.weight,cluster:d.weight,doc_authorship:m.weight},reasons:g}}function Xs(e,t=Bs){let n=[];for(let s=0;s<e.length;s++){let r=e[s],i=null;for(let a=0;a<s;a++){let o=e[a],c=Ec(o,r,t);c&&(!i||c.confidence>i.confidence)&&(i=c)}i&&n.push(i)}return n}function Gs(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,i=[],a=new Set,o=[],c;try{if(oc(e).size>ac)return{touched_files:r,recent_user_messages:i,authored_paths:a,authored_content:o};c=ic(e,"utf8")}catch{return{touched_files:r,recent_user_messages:i,authored_paths:a,authored_content:o}}let l=0;for(;l<c.length;){let d=c.indexOf(`
939
- `,l),m=d===-1?c.length:d,p=c.slice(l,m);if(l=d===-1?c.length:d+1,!p.trim())continue;let g;try{g=JSON.parse(p)}catch{continue}let h=g;if(h.type==="user"&&h.message?.role==="user"&&typeof h.message.content=="string"&&i.length<n){let u=h.message.content.trim();u&&i.push(u.length>s?u.slice(0,s):u)}let S=h.message?.content;if(Array.isArray(S))for(let u of S){if(!u||typeof u!="object")continue;let f=u;if(f.type!=="tool_use")continue;let T=f.input??{},R=typeof T.file_path=="string"?T.file_path:null;if(R){let k=Ye(R);k&&r.add(k)}if((f.name==="Write"||f.name==="Edit"||f.name==="MultiEdit")&&R){let k=Ye(R);k&&a.add(k);let w=typeof T.content=="string"?T.content:typeof T.new_string=="string"?T.new_string:null;w&&w.length>=200&&o.push(w.length>4096?w.slice(0,4096):w)}if(f.name==="Bash"&&typeof T.command=="string")for(let k of T.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let w=Ye(k[1]);w&&r.add(w)}if((f.name==="Glob"||f.name==="Grep")&&typeof T.pattern=="string"){let k=Ye(T.pattern);k&&!k.includes("*")&&r.add(k)}}}return{touched_files:r,recent_user_messages:i,authored_paths:a,authored_content:o}}function Ye(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Kt=Tc(Sc),yc=6,Ys="Active ",Ks=" sessions \u2014 ",wc=6e4;async function Rc(){if(Ke()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Kt(t,["-eo","pid=,comm="],{timeout:2e3}),s=[];for(let r of n.split(`
940
- `)){let i=r.trim().match(/^(\d+)\s+(.+)$/);if(!i)continue;let a=Number(i[1]),o=i[2].trim();(o==="claude"||o.endsWith("/claude")||o.endsWith("/bin/claude"))&&Number.isFinite(a)&&s.push(a)}return s}catch{continue}return[]}async function Ac(e){let t=Ke();if(t==="linux")try{return(await bc(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:s}=await Kt(n,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of s.split(`
941
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Nc(e){let t=Ke();if(t==="linux")try{let n=await zs(`/proc/${e}/stat`,"utf8"),s=n.lastIndexOf(")");if(s===-1)return null;let r=n.slice(s+1).trim().split(/\s+/),i=Number(r[19]);if(!Number.isFinite(i))return null;let a=await zs("/proc/uptime","utf8"),o=Number(a.split(/\s+/)[0]);return Number.isFinite(o)?Date.now()-o*1e3+i/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Kt(n,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(s.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function Lc(e,t){let n=await Rc();if(n.length===0)return null;let s=e.replace(/\/+$/,""),r=[];for(let a of n){let o=await Ac(a);if(o&&(o===s||o.startsWith(s+"/"))){let c=await Nc(a);r.push({pid:a,startMs:c})}}if(r.length===0)return new Set;let i=new Set;for(let{startMs:a}of r){if(a==null)continue;let o=null,c=wc;for(let l of t){if(i.has(l.session_id)||!l.started_at)continue;let d=Date.parse(l.started_at);if(!Number.isFinite(d))continue;let m=Math.abs(d-a);m<c&&(c=m,o=l)}o&&i.add(o.session_id)}return i}function kc(e){let n=E().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!n)throw new Error(`project ${e} not found`);return n}function xc(e,t){let n=E(),s=`${Ys}${t}${Ks}%`,r=n.prepare(`SELECT t.id
959
+ ${n}`}function bc(e){return Ps(e)?.auto_title_source??null}async function yc(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(Gt(),Ys));if(!n())throw new Error("claude CLI not found on PATH");let s=await t(e.prompt,[],{model:e.model});if(!s.success)throw new Error(`claude CLI exited ${s.exitCode}: ${s.stderr.slice(-500)}`);let r=wc(s.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,Ec)}function wc(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return Ks(s)}}catch{}return Ks(t)}function Ks(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function Vs(e,t={}){let n=$(e);if(!n)throw new Error(`thread not found: ${e}`);let s=Sc(n.edges),r=s.length,i=t.force??!1,o=t.signal,a=[],c=[],l=[];for(let d=0;d<s.length;d++){let m=s[d],p=d+1;if(o?.aborted){let h={sessionId:m,reason:"cancelled"};c.push(h),t.onSkipped?.(h);continue}if(!i&&bc(m)==="agent"){let h={sessionId:m,reason:"already-titled"};c.push(h),t.onSkipped?.(h);continue}let g;try{if(Js)g=await Js({sessionId:m,current:p,total:r});else{let h=Tc({sessionId:m,current:p,total:r});g=await yc({prompt:h,model:t.model})}}catch(h){let S=h instanceof Error?h.message:String(h??"unknown error"),u={sessionId:m,error:S};l.push(u),t.onFailed?.(u);continue}try{Bt(m,g,"agent")}catch(h){let S=h instanceof Error?h.message:String(h??"unknown error"),u={sessionId:m,error:`setAutoTitle failed: ${S}`};l.push(u),t.onFailed?.(u);continue}a.push(m),t.onProgress?.({current:p,total:r,sessionId:m,title:g})}return{generated:a,skipped:c,failed:l}}var Js=null;x();import{execFile as $c}from"node:child_process";import{promisify as Uc}from"node:util";import{readlink as jc,readFile as nr}from"node:fs/promises";import{platform as Je}from"node:os";import{readFileSync as Rc,statSync as Ac}from"node:fs";var Nc=200*1024*1024;var Yt=.5,Zs=Yt,Lc=[{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"}],kc=["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 qs(e){if(!e)return null;if(e.startsWith("/")){let n=e.split(" \xB7 ");if(n.length>1)return n[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function xc(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of Lc)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function Oc(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let i=r.toLowerCase();for(let o of kc)if(i.includes(o))return{weight:t[s],matched:o,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function zt(e,t){if(!e||!t||e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function vc(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let i=zt(s,r);i>n&&(n=i)}return n}function Ic(e,t){let n=zt(e.mean_embedding,t.mean_embedding),s=zt(e.tail_pool,t.head_pool),r=vc(e.sample_chunks,t.sample_chunks),i=0,o=null;if(n>i&&(i=n,o="mean"),s>i&&(i=s,o="asymmetric"),r>i&&(i=r,o="max_pool"),i<.65)return{weight:0,cosine:i,mode:null};if(i>=.85)return{weight:.3,cosine:i,mode:o};let a=(i-.65)/.2*.3;return{weight:Math.round(a*100)/100,cosine:i,mode:o}}function Cc(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 Dc(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let n=0;for(let r of t)e.has(r)&&n++;return n===0?{weight:0,count:0}:{weight:Math.min(.4,n*.1),count:n}}function Mc(e,t){let n=qs(e),s=qs(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function Qs(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Pc(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let i=t.recent_user_messages.slice(0,3).join(`
960
+ `).toLowerCase();for(let o of e.authored_paths){let a=o.toLowerCase();if(t.touched_files.has(o)||i.includes(a)){n+=.5,s=o;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let i=Qs(t.recent_user_messages[0]);if(i.length>=200)for(let o of e.authored_content){let a=Qs(o);if(a.length<200)continue;let c=Math.min(a.length,240),l=a.slice(0,c);if(i.includes(l)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function Fc(e,t,n=Zs){if(t.started_at_ms<=e.started_at_ms)return null;let s=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<s)return null;let r=xc(s,t.started_at_ms),i=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],o=Oc(i),a=Dc(e.touched_files,t.touched_files),c=Mc(e.auto_title,t.auto_title),l=Ic(e,t),d=Cc(e,t),m=Pc(e,t),p=r.weight+o.weight+a.weight+c.weight+l.weight+d.weight+m.weight;if(p<n)return null;let g=[];if(r.label&&g.push(`temporal ${r.label} (+${r.weight})`),o.matched){let h=o.matchedIndex===0?"opening message":`message #${o.matchedIndex+1}`;g.push(`continuation phrase "${o.matched}" in ${h} (+${o.weight})`)}if(a.count>0&&g.push(`${a.count} file${a.count===1?"":"s"} overlap (+${a.weight.toFixed(1)})`),c.brand&&g.push(`shared brand "${c.brand}" (+${c.weight})`),l.weight>0&&l.mode&&g.push(`semantic ${l.mode==="asymmetric"?"tail\u2192head":l.mode==="max_pool"?"best-chunk":"mean"} ${l.cosine.toFixed(2)} (+${l.weight.toFixed(2)})`),d.same&&g.push(`same cluster (+${d.weight})`),m.weight>0){let h=[];m.pathMatch&&h.push(`opened authored path "${m.pathMatch.split("/").pop()}"`),m.contentMatch&&h.push("verbatim-paste of authored content"),g.push(`doc-authorship: ${h.join(" + ")} (+${m.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,p),signals:{temporal:r.weight,continuation:o.weight,file_overlap:a.weight,same_brand:c.weight,semantic:l.weight,cluster:d.weight,doc_authorship:m.weight},reasons:g}}function er(e,t=Zs){let n=[];for(let s=0;s<e.length;s++){let r=e[s],i=null;for(let o=0;o<s;o++){let a=e[o],c=Fc(a,r,t);c&&(!i||c.confidence>i.confidence)&&(i=c)}i&&n.push(i)}return n}function tr(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,i=[],o=new Set,a=[],c;try{if(Ac(e).size>Nc)return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a};c=Rc(e,"utf8")}catch{return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}let l=0;for(;l<c.length;){let d=c.indexOf(`
961
+ `,l),m=d===-1?c.length:d,p=c.slice(l,m);if(l=d===-1?c.length:d+1,!p.trim())continue;let g;try{g=JSON.parse(p)}catch{continue}let h=g;if(h.type==="user"&&h.message?.role==="user"&&typeof h.message.content=="string"&&i.length<n){let u=h.message.content.trim();u&&i.push(u.length>s?u.slice(0,s):u)}let S=h.message?.content;if(Array.isArray(S))for(let u of S){if(!u||typeof u!="object")continue;let f=u;if(f.type!=="tool_use")continue;let T=f.input??{},R=typeof T.file_path=="string"?T.file_path:null;if(R){let k=Ke(R);k&&r.add(k)}if((f.name==="Write"||f.name==="Edit"||f.name==="MultiEdit")&&R){let k=Ke(R);k&&o.add(k);let w=typeof T.content=="string"?T.content:typeof T.new_string=="string"?T.new_string:null;w&&w.length>=200&&a.push(w.length>4096?w.slice(0,4096):w)}if(f.name==="Bash"&&typeof T.command=="string")for(let k of T.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let w=Ke(k[1]);w&&r.add(w)}if((f.name==="Glob"||f.name==="Grep")&&typeof T.pattern=="string"){let k=Ke(T.pattern);k&&!k.includes("*")&&r.add(k)}}}return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}function Ke(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Jt=Uc($c),Hc=6,sr="Active ",rr=" sessions \u2014 ",Wc=6e4;async function Bc(){if(Je()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Jt(t,["-eo","pid=,comm="],{timeout:2e3}),s=[];for(let r of n.split(`
962
+ `)){let i=r.trim().match(/^(\d+)\s+(.+)$/);if(!i)continue;let o=Number(i[1]),a=i[2].trim();(a==="claude"||a.endsWith("/claude")||a.endsWith("/bin/claude"))&&Number.isFinite(o)&&s.push(o)}return s}catch{continue}return[]}async function Xc(e){let t=Je();if(t==="linux")try{return(await jc(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:s}=await Jt(n,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of s.split(`
963
+ `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Gc(e){let t=Je();if(t==="linux")try{let n=await nr(`/proc/${e}/stat`,"utf8"),s=n.lastIndexOf(")");if(s===-1)return null;let r=n.slice(s+1).trim().split(/\s+/),i=Number(r[19]);if(!Number.isFinite(i))return null;let o=await nr("/proc/uptime","utf8"),a=Number(o.split(/\s+/)[0]);return Number.isFinite(a)?Date.now()-a*1e3+i/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Jt(n,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(s.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function zc(e,t){let n=await Bc();if(n.length===0)return null;let s=e.replace(/\/+$/,""),r=[];for(let o of n){let a=await Xc(o);if(a&&(a===s||a.startsWith(s+"/"))){let c=await Gc(o);r.push({pid:o,startMs:c})}}if(r.length===0)return new Set;let i=new Set;for(let{startMs:o}of r){if(o==null)continue;let a=null,c=Wc;for(let l of t){if(i.has(l.session_id)||!l.started_at)continue;let d=Date.parse(l.started_at);if(!Number.isFinite(d))continue;let m=Math.abs(d-o);m<c&&(c=m,a=l)}a&&i.add(a.session_id)}return i}function Yc(e){let n=E().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!n)throw new Error(`project ${e} not found`);return n}function Kc(e,t){let n=E(),s=`${sr}${t}${rr}%`,r=n.prepare(`SELECT t.id
942
964
  FROM threads t
943
965
  WHERE t.archived = 0
944
966
  AND t.name LIKE ?
945
- ORDER BY t.created_at DESC`).all(s);for(let a of r){let o=$(a.id);if(o&&o.project===t)return o}let i=n.prepare(`SELECT DISTINCT te.thread_id AS id
967
+ ORDER BY t.created_at DESC`).all(s);for(let o of r){let a=$(o.id);if(a&&a.project===t)return a}let i=n.prepare(`SELECT DISTINCT te.thread_id AS id
946
968
  FROM thread_edges te
947
969
  JOIN sessions s ON s.id = te.session_id
948
970
  JOIN threads t ON t.id = te.thread_id
949
971
  WHERE s.project_id = ?
950
972
  AND t.archived = 0
951
973
  AND t.name LIKE ?
952
- LIMIT 1`).get(e,s);return i?$(i.id):null}function Js(e){let t=e?new Date(e):new Date,n=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${s}-${r}`}function Yt(e,t){let n=E(),s=t>0,r=s?Date.now()-t*60*60*1e3:0;return s?n.prepare(`SELECT s.id AS session_id,
974
+ LIMIT 1`).get(e,s);return i?$(i.id):null}function ir(e){let t=e?new Date(e):new Date,n=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${s}-${r}`}function Kt(e,t){let n=E(),s=t>0,r=s?Date.now()-t*60*60*1e3:0;return s?n.prepare(`SELECT s.id AS session_id,
953
975
  sa.alias AS alias,
954
976
  s.auto_title AS auto_title,
955
977
  s.first_user_message AS first_user_message,
@@ -969,25 +991,25 @@ ${n}`}function nc(e){return Ns(e)?.auto_title_source??null}async function sc(e){
969
991
  FROM sessions s
970
992
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
971
993
  WHERE s.project_id = ?
972
- ORDER BY s.started_at ASC`).all(e)}function Oc(e){let t=E(),n=[];for(let s of e){if(!s.started_at)continue;let r=Date.parse(s.started_at);if(!Number.isFinite(r))continue;let i=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(s.session_id);if(!i?.file_path)continue;let a=i.ended_at?Date.parse(i.ended_at):null,o=Gs(i.file_path,{maxUserMessages:7});n.push({id:s.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:s.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:s.auto_title,touched_files:o.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:o.authored_paths,authored_content:o.authored_content})}return n}function vc(e){let n=E().prepare(`SELECT session_id, parent_session_id, source
994
+ ORDER BY s.started_at ASC`).all(e)}function Jc(e){let t=E(),n=[];for(let s of e){if(!s.started_at)continue;let r=Date.parse(s.started_at);if(!Number.isFinite(r))continue;let i=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(s.session_id);if(!i?.file_path)continue;let o=i.ended_at?Date.parse(i.ended_at):null,a=tr(i.file_path,{maxUserMessages:7});n.push({id:s.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(o)?o:null,first_user_message:s.first_user_message,recent_user_messages:a.recent_user_messages,auto_title:s.auto_title,touched_files:a.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:a.authored_paths,authored_content:a.authored_content})}return n}function Vc(e){let n=E().prepare(`SELECT session_id, parent_session_id, source
973
995
  FROM thread_edges
974
- WHERE thread_id = ?`).all(e),s=new Map;for(let r of n)s.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return s}async function Jt(e,t={}){let n=kc(e),s=t.windowHours??yc,r=t.scoreThreshold??zt,i=t.useLivePids??!0,a=[],o=[];if(i&&n.decoded_path){let S=Yt(e,0),u=await Lc(n.decoded_path,S);if(u===null){let T=Ke()==="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(T),o=Yt(e,s)}else u.size===0?(a.push(`No active terminals open in ${n.name} (cwd=${n.decoded_path}). Open a Claude terminal in this repo and re-run.`),o=[]):o=S.filter(f=>u.has(f.session_id))}else o=Yt(e,s);o.length===0&&!a.length&&a.push(`No active sessions in ${n.name} within the last ${s}h.`);let c=xc(e,n.name),l=new Set(c?c.edges.map(S=>S.session_id):[]),d=o.filter(S=>!l.has(S.session_id)),m=Oc(o);m.sort((S,u)=>S.started_at_ms-u.started_at_ms);let p=Xs(m,r),g=c?c.edges.filter(S=>S.source!=="auto-active"&&(S.parent_session_id||S.role==="origin")).map(S=>({session_id:S.session_id,parent_session_id:S.parent_session_id})):[],h=c?c.name:`${Ys}${n.name}${Ks}${Js(t.todayIso)}`;return{project:n,thread:{id:c?.id??null,name:h,exists:!!c,existing_session_count:c?.edges.length??0},candidates:o,proposed_additions:d,proposed_edges:p,preserved_manual_edges:g,warnings:a}}function qs(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},n;e.thread.exists&&e.thread.id?n=e.thread.id:n=Xe({name:e.thread.name,summary:`Auto-captured by sync-active on ${Js()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=n;let s=vc(n);for(let a of e.candidates)s.has(a.session_id)||(Ge({threadId:n,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,s.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let o=s.get(a.child_id);if(o&&o.source==="auto-active"&&o.parent_session_id!==a.parent_id&&s.has(a.parent_id))try{we(n,a.child_id,a.parent_id),t.edges_set++,s.set(a.child_id,{parent_session_id:a.parent_id,source:o.source})}catch{}}let i=E().prepare(`SELECT session_id FROM thread_edges
996
+ WHERE thread_id = ?`).all(e),s=new Map;for(let r of n)s.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return s}async function Vt(e,t={}){let n=Yc(e),s=t.windowHours??Hc,r=t.scoreThreshold??Yt,i=t.useLivePids??!0,o=[],a=[];if(i&&n.decoded_path){let S=Kt(e,0),u=await zc(n.decoded_path,S);if(u===null){let T=Je()==="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.";o.push(T),a=Kt(e,s)}else u.size===0?(o.push(`No active terminals open in ${n.name} (cwd=${n.decoded_path}). Open a Claude terminal in this repo and re-run.`),a=[]):a=S.filter(f=>u.has(f.session_id))}else a=Kt(e,s);a.length===0&&!o.length&&o.push(`No active sessions in ${n.name} within the last ${s}h.`);let c=Kc(e,n.name),l=new Set(c?c.edges.map(S=>S.session_id):[]),d=a.filter(S=>!l.has(S.session_id)),m=Jc(a);m.sort((S,u)=>S.started_at_ms-u.started_at_ms);let p=er(m,r),g=c?c.edges.filter(S=>S.source!=="auto-active"&&(S.parent_session_id||S.role==="origin")).map(S=>({session_id:S.session_id,parent_session_id:S.parent_session_id})):[],h=c?c.name:`${sr}${n.name}${rr}${ir(t.todayIso)}`;return{project:n,thread:{id:c?.id??null,name:h,exists:!!c,existing_session_count:c?.edges.length??0},candidates:a,proposed_additions:d,proposed_edges:p,preserved_manual_edges:g,warnings:o}}function or(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},n;e.thread.exists&&e.thread.id?n=e.thread.id:n=Ge({name:e.thread.name,summary:`Auto-captured by sync-active on ${ir()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=n;let s=Vc(n);for(let o of e.candidates)s.has(o.session_id)||(ze({threadId:n,sessionId:o.session_id,source:"auto-active",confidence:.5}),t.added++,s.set(o.session_id,{parent_session_id:null,source:"auto-active"}));for(let o of e.proposed_edges){let a=s.get(o.child_id);if(a&&a.source==="auto-active"&&a.parent_session_id!==o.parent_id&&s.has(o.parent_id))try{Re(n,o.child_id,o.parent_id),t.edges_set++,s.set(o.child_id,{parent_session_id:o.parent_id,source:a.source})}catch{}}let i=E().prepare(`SELECT session_id FROM thread_edges
975
997
  WHERE thread_id = ?
976
998
  AND source = 'auto-active'
977
999
  AND parent_session_id IS NULL
978
- AND role = 'child'`).all(n);for(let a of i)try{we(n,a.session_id,null)}catch{}return t}function F(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Ic(e){return{content:[{type:"text",text:e}],isError:!0}}function U(e,t){return B.object(e).strict().parse(t)}var lr=B.string().uuid(),V=lr.describe("Thread id (UUID)."),le=lr.describe("Session UUID (exact, not a prefix)."),Vs={include_archived:B.boolean().optional().describe("Include archived threads (default false).")},Qs={id:V},Zs={session_id:le},er={name:B.string().min(1).max(200).describe("Human-readable thread name."),summary:B.string().max(4e3).optional().describe("Optional short description."),origin_session_id:le.optional().describe("Seed the thread with this session as its origin.")},tr={thread_id:V,session_id:le,parent_session_id:le.optional().describe("If present, the edge is role=child with this parent; otherwise role=origin."),role:B.enum(["origin","child"]).optional()},nr={thread_id:V,session_id:le,parent_session_id:le.nullable().describe("New parent (null to clear and promote the edge back to role=origin).")},sr={thread_id:V,session_id:le},rr={thread_id:V,name:B.string().min(1).max(200)},Ee={thread_id:V},ir={source_id:V.describe("Thread to dissolve \u2014 its edges move into dest_id."),dest_id:V.describe("Thread that absorbs source_id.")},or={thread_id:V,session_ids:B.array(le).min(1).max(500),new_thread_name:B.string().min(1).max(200)},ar={thread_id:V,force:B.boolean().optional().describe("When true, regenerate titles for sessions that already have an agent-sourced title. Default false skips them.")},cr={project_id:B.number().int().positive().describe("Numeric project id from list_projects. The sync is repo-scoped and never crosses projects."),mode:B.enum(["preflight","apply"]).describe("preflight returns the proposed plan without writing; apply writes the thread + edges and is idempotent."),window_hours:B.number().min(.5).max(168).optional().describe("Rolling activity window in hours. Default 6.")};function dr(e){e.registerTool("thread_list",{title:"List threads",description:"All threads (v0.15a intent groups), newest first. Excludes archived threads unless include_archived is true.",inputSchema:Vs},async t=>{try{let{include_archived:n}=U(Vs,t);return F($t({includeArchived:n??!1}))}catch(n){return C(n)}}),e.registerTool("thread_get",{title:"Get thread with edges",description:"Full thread detail including every session edge (origin + children).",inputSchema:Qs},async t=>{try{let{id:n}=U(Qs,t),s=$(n);return s?F(s):Ic(`thread not found: ${n}`)}catch(n){return C(n)}}),e.registerTool("thread_for_session",{title:"List threads containing a session",description:"Return non-archived threads that reference this session (as origin or child).",inputSchema:Zs},async t=>{try{let{session_id:n}=U(Zs,t);return F(ds(n))}catch(n){return C(n)}})}function ur(e,t={}){let n=t.limiter??new ae;e.registerTool("thread_create",{title:"Create a thread",description:"Create a new thread. Optionally seed it with an origin session. Name is required; summary is optional.",inputSchema:er},async s=>{try{let r=U(er,s),i=await D({tool:"thread_create",args:r,limiter:n,run:()=>Xe({name:r.name,summary:r.summary??null,originSessionId:r.origin_session_id})});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_add_session",{title:"Add session to thread",description:"Attach a session to a thread. If parent_session_id is provided the edge is role=child; otherwise role=origin. Upsert: re-adding updates the edge.",inputSchema:tr},async s=>{try{let r=U(tr,s),i=await D({tool:"thread_add_session",args:r,limiter:n,run:()=>Ge({threadId:r.thread_id,sessionId:r.session_id,parentSessionId:r.parent_session_id??null,role:r.role,source:"manual",confidence:1})});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_set_parent",{title:"Set edge parent within thread",description:"Change the parent session of an existing thread edge. Pass null to clear the parent and promote the edge to role=origin.",inputSchema:nr},async s=>{try{let r=U(nr,s),i=await D({tool:"thread_set_parent",args:r,limiter:n,run:()=>we(r.thread_id,r.session_id,r.parent_session_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_remove_session",{title:"Remove session from thread",description:"Detach a session from a thread. The session itself is untouched; only the edge is removed. The updated thread is re-mirrored to disk.",inputSchema:sr},async s=>{try{let r=U(sr,s),i=await D({tool:"thread_remove_session",args:r,limiter:n,run:()=>us(r.thread_id,r.session_id)});return F({thread_id:r.thread_id,session_id:r.session_id,...i})}catch(r){return C(r)}}),e.registerTool("thread_rename",{title:"Rename thread",description:"Change the display name of a thread.",inputSchema:rr},async s=>{try{let r=U(rr,s),i=await D({tool:"thread_rename",args:r,limiter:n,run:()=>ps(r.thread_id,r.name)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_close",{title:"Close thread",description:"Mark the thread as closed (sets closed_at). Thread remains listed; reopen to clear.",inputSchema:Ee},async s=>{try{let r=U(Ee,s),i=await D({tool:"thread_close",args:r,limiter:n,run:()=>ms(r.thread_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_reopen",{title:"Reopen thread",description:"Clear closed_at on a closed thread.",inputSchema:Ee},async s=>{try{let r=U(Ee,s),i=await D({tool:"thread_reopen",args:r,limiter:n,run:()=>gs(r.thread_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_archive",{title:"Archive thread",description:"Soft-delete a thread by setting archived=1. Archived threads are hidden from thread_list by default but never hard-deleted; data is preserved in SQLite and the JSON mirror.",inputSchema:Ee},async s=>{try{let r=U(Ee,s),i=await D({tool:"thread_archive",args:r,limiter:n,run:()=>fs(r.thread_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_merge",{title:"Merge two threads",description:"Move every edge from source_id into dest_id, then delete source_id. Origin roles are preserved when present on either side.",inputSchema:ir},async s=>{try{let r=U(ir,s),i=await D({tool:"thread_merge",args:r,limiter:n,run:()=>_s(r.source_id,r.dest_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_split",{title:"Split sessions into a new thread",description:"Peel the specified session_ids out of thread_id into a brand-new thread named new_thread_name. Non-member session_ids are silently skipped.",inputSchema:or},async s=>{try{let r=U(or,s),i=await D({tool:"thread_split",args:r,limiter:n,run:()=>hs({threadId:r.thread_id,sessionIds:r.session_ids,newThreadName:r.new_thread_name})});return F(i)}catch(r){return C(r)}}),e.registerTool("sync_active_sessions",{title:"Sync currently active sessions in a repo into a thread",description:'Captures the Captain Code Pattern: scans every Claude session whose JSONL was touched within the rolling window in the given project, infers parent/child edges via the 7-signal scorer (temporal, continuation, file overlap, brand, semantic, cluster, doc-authorship), and stitches them into one thread. Idempotent: re-running reuses the existing "Active <project> sessions \u2014 *" thread, only appends new sessions, and never overwrites edges with source=manual. Use mode=preflight first to show the user the proposed plan, then mode=apply to write.',inputSchema:cr},async s=>{try{let r=U(cr,s);if(r.mode==="preflight"){let a=await Jt(r.project_id,{windowHours:r.window_hours});return F({plan:a})}let i=await D({tool:"sync_active_sessions",args:r,limiter:n,run:async()=>{let a=await Jt(r.project_id,{windowHours:r.window_hours}),o=qs(a);return{plan:a,result:o}}});return F(i)}catch(r){return C(r)}}),e.registerTool("generate_thread_titles",{title:"Generate titles for every session in a thread",description:"Walk a thread DAG in topology order (origins first by added_at, then children breadth-first) and generate a coherent agent title for each session. Each successive title sees already-titled ancestors and earlier siblings as strong context so a naming pattern emerges. Set force=true to regenerate already-titled sessions; default false skips them. Returns the final {generated, skipped, failed} summary after the whole walk completes.",inputSchema:ar},async s=>{try{let r=U(ar,s),i=await D({tool:"generate_thread_titles",args:r,limiter:n,run:async()=>{let a=await js(r.thread_id,{force:r.force??!1});if(a.failed.length>0&&a.generated.length===0&&a.skipped.length===0)throw new Error(`all ${a.failed.length} session(s) failed title generation`);return a}});return F(i)}catch(r){return C(r)}})}nn();x();I();import{writeFileSync as rl}from"node:fs";import{join as il}from"node:path";var ol=il(y,"recall-events.json");function Er(e,t,n,s="cli"){E().prepare(`
1000
+ AND role = 'child'`).all(n);for(let o of i)try{Re(n,o.session_id,null)}catch{}return t}function F(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function qc(e){return{content:[{type:"text",text:e}],isError:!0}}function U(e,t){return B.object(e).strict().parse(t)}var Sr=B.string().uuid(),q=Sr.describe("Thread id (UUID)."),de=Sr.describe("Session UUID (exact, not a prefix)."),ar={include_archived:B.boolean().optional().describe("Include archived threads (default false).")},cr={id:q},lr={session_id:de},dr={name:B.string().min(1).max(200).describe("Human-readable thread name."),summary:B.string().max(4e3).optional().describe("Optional short description."),origin_session_id:de.optional().describe("Seed the thread with this session as its origin.")},ur={thread_id:q,session_id:de,parent_session_id:de.optional().describe("If present, the edge is role=child with this parent; otherwise role=origin."),role:B.enum(["origin","child"]).optional()},pr={thread_id:q,session_id:de,parent_session_id:de.nullable().describe("New parent (null to clear and promote the edge back to role=origin).")},mr={thread_id:q,session_id:de},gr={thread_id:q,name:B.string().min(1).max(200)},Se={thread_id:q},fr={source_id:q.describe("Thread to dissolve \u2014 its edges move into dest_id."),dest_id:q.describe("Thread that absorbs source_id.")},_r={thread_id:q,session_ids:B.array(de).min(1).max(500),new_thread_name:B.string().min(1).max(200)},hr={thread_id:q,force:B.boolean().optional().describe("When true, regenerate titles for sessions that already have an agent-sourced title. Default false skips them.")},Er={project_id:B.number().int().positive().describe("Numeric project id from list_projects. The sync is repo-scoped and never crosses projects."),mode:B.enum(["preflight","apply"]).describe("preflight returns the proposed plan without writing; apply writes the thread + edges and is idempotent."),window_hours:B.number().min(.5).max(168).optional().describe("Rolling activity window in hours. Default 6.")};function Tr(e){e.registerTool("thread_list",{title:"List threads",description:"All threads (v0.15a intent groups), newest first. Excludes archived threads unless include_archived is true.",inputSchema:ar},async t=>{try{let{include_archived:n}=U(ar,t);return F(Ut({includeArchived:n??!1}))}catch(n){return C(n)}}),e.registerTool("thread_get",{title:"Get thread with edges",description:"Full thread detail including every session edge (origin + children).",inputSchema:cr},async t=>{try{let{id:n}=U(cr,t),s=$(n);return s?F(s):qc(`thread not found: ${n}`)}catch(n){return C(n)}}),e.registerTool("thread_for_session",{title:"List threads containing a session",description:"Return non-archived threads that reference this session (as origin or child).",inputSchema:lr},async t=>{try{let{session_id:n}=U(lr,t);return F(Ts(n))}catch(n){return C(n)}})}function br(e,t={}){let n=t.limiter??new ce;e.registerTool("thread_create",{title:"Create a thread",description:"Create a new thread. Optionally seed it with an origin session. Name is required; summary is optional.",inputSchema:dr},async s=>{try{let r=U(dr,s),i=await D({tool:"thread_create",args:r,limiter:n,run:()=>Ge({name:r.name,summary:r.summary??null,originSessionId:r.origin_session_id})});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_add_session",{title:"Add session to thread",description:"Attach a session to a thread. If parent_session_id is provided the edge is role=child; otherwise role=origin. Upsert: re-adding updates the edge.",inputSchema:ur},async s=>{try{let r=U(ur,s),i=await D({tool:"thread_add_session",args:r,limiter:n,run:()=>ze({threadId:r.thread_id,sessionId:r.session_id,parentSessionId:r.parent_session_id??null,role:r.role,source:"manual",confidence:1})});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_set_parent",{title:"Set edge parent within thread",description:"Change the parent session of an existing thread edge. Pass null to clear the parent and promote the edge to role=origin.",inputSchema:pr},async s=>{try{let r=U(pr,s),i=await D({tool:"thread_set_parent",args:r,limiter:n,run:()=>Re(r.thread_id,r.session_id,r.parent_session_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_remove_session",{title:"Remove session from thread",description:"Detach a session from a thread. The session itself is untouched; only the edge is removed. The updated thread is re-mirrored to disk.",inputSchema:mr},async s=>{try{let r=U(mr,s),i=await D({tool:"thread_remove_session",args:r,limiter:n,run:()=>bs(r.thread_id,r.session_id)});return F({thread_id:r.thread_id,session_id:r.session_id,...i})}catch(r){return C(r)}}),e.registerTool("thread_rename",{title:"Rename thread",description:"Change the display name of a thread.",inputSchema:gr},async s=>{try{let r=U(gr,s),i=await D({tool:"thread_rename",args:r,limiter:n,run:()=>ys(r.thread_id,r.name)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_close",{title:"Close thread",description:"Mark the thread as closed (sets closed_at). Thread remains listed; reopen to clear.",inputSchema:Se},async s=>{try{let r=U(Se,s),i=await D({tool:"thread_close",args:r,limiter:n,run:()=>ws(r.thread_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_reopen",{title:"Reopen thread",description:"Clear closed_at on a closed thread.",inputSchema:Se},async s=>{try{let r=U(Se,s),i=await D({tool:"thread_reopen",args:r,limiter:n,run:()=>Rs(r.thread_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_archive",{title:"Archive thread",description:"Soft-delete a thread by setting archived=1. Archived threads are hidden from thread_list by default but never hard-deleted; data is preserved in SQLite and the JSON mirror.",inputSchema:Se},async s=>{try{let r=U(Se,s),i=await D({tool:"thread_archive",args:r,limiter:n,run:()=>As(r.thread_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_merge",{title:"Merge two threads",description:"Move every edge from source_id into dest_id, then delete source_id. Origin roles are preserved when present on either side.",inputSchema:fr},async s=>{try{let r=U(fr,s),i=await D({tool:"thread_merge",args:r,limiter:n,run:()=>Ns(r.source_id,r.dest_id)});return F(i)}catch(r){return C(r)}}),e.registerTool("thread_split",{title:"Split sessions into a new thread",description:"Peel the specified session_ids out of thread_id into a brand-new thread named new_thread_name. Non-member session_ids are silently skipped.",inputSchema:_r},async s=>{try{let r=U(_r,s),i=await D({tool:"thread_split",args:r,limiter:n,run:()=>Ls({threadId:r.thread_id,sessionIds:r.session_ids,newThreadName:r.new_thread_name})});return F(i)}catch(r){return C(r)}}),e.registerTool("sync_active_sessions",{title:"Sync currently active sessions in a repo into a thread",description:'Captures the Captain Code Pattern: scans every Claude session whose JSONL was touched within the rolling window in the given project, infers parent/child edges via the 7-signal scorer (temporal, continuation, file overlap, brand, semantic, cluster, doc-authorship), and stitches them into one thread. Idempotent: re-running reuses the existing "Active <project> sessions \u2014 *" thread, only appends new sessions, and never overwrites edges with source=manual. Use mode=preflight first to show the user the proposed plan, then mode=apply to write.',inputSchema:Er},async s=>{try{let r=U(Er,s);if(r.mode==="preflight"){let o=await Vt(r.project_id,{windowHours:r.window_hours});return F({plan:o})}let i=await D({tool:"sync_active_sessions",args:r,limiter:n,run:async()=>{let o=await Vt(r.project_id,{windowHours:r.window_hours}),a=or(o);return{plan:o,result:a}}});return F(i)}catch(r){return C(r)}}),e.registerTool("generate_thread_titles",{title:"Generate titles for every session in a thread",description:"Walk a thread DAG in topology order (origins first by added_at, then children breadth-first) and generate a coherent agent title for each session. Each successive title sees already-titled ancestors and earlier siblings as strong context so a naming pattern emerges. Set force=true to regenerate already-titled sessions; default false skips them. Returns the final {generated, skipped, failed} summary after the whole walk completes.",inputSchema:hr},async s=>{try{let r=U(hr,s),i=await D({tool:"generate_thread_titles",args:r,limiter:n,run:async()=>{let o=await Vs(r.thread_id,{force:r.force??!1});if(o.failed.length>0&&o.generated.length===0&&o.skipped.length===0)throw new Error(`all ${o.failed.length} session(s) failed title generation`);return o}});return F(i)}catch(r){return C(r)}})}on();x();I();import{writeFileSync as Al}from"node:fs";import{join as Nl}from"node:path";var Ll=Nl(y,"recall-events.json");function kr(e,t,n,s="cli"){E().prepare(`
979
1001
  INSERT INTO recall_events (session_id, recalled_at, token_count, mode, caller)
980
1002
  VALUES (?, datetime('now'), ?, ?, ?)
981
- `).run(e,t,n,s),al()}function al(){v();let t=E().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();rl(ol,JSON.stringify(t,null,2)+`
982
- `,"utf-8")}x();var me=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,n){super(`semantic search refused: vec_chunks has ${t} rows (cap ${n}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=n}};function sn(e,t={}){let n=t.limit??cl()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new me(r,n)}function cl(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}qe();import{Worker as ll}from"node:worker_threads";import{join as dl}from"node:path";var rn=class extends Error{constructor(n){super(`semantic kNN exceeded ${n}ms wall-clock budget -- query aborted`);this.timeoutMs=n}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},ul=1e4;function pl(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function on(e){let t=e.timeoutMs??pl()??ul,n=(e.workerFactory??ml)();return new Promise((s,r)=>{let i=setTimeout(()=>{n.terminate().catch(()=>{}),r(new rn(t))},t);n.once("message",a=>{clearTimeout(i);let o=a;o&&o.ok===!0&&Array.isArray(o.hits)?s(o.hits):r(new Error(o?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",a=>{clearTimeout(i),n.terminate().catch(()=>{}),r(a)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function ml(){let e=dl(Se(),"dist","daemon","query-worker.js");return new ll(e)}async function an(e,t=50){return sn(E()),on({query:e,limit:t})}async function Sr(e,t=10,n=.65){let s=E();sn(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let i=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!i)return[];let a=await on({precomputedVector:i.embedding,limit:t*5}),o=new Map;for(let l of a){if(l.sessionId===e)continue;let d=o.get(l.sessionId);(d===void 0||l.distance<d)&&o.set(l.sessionId,l.distance)}let c=[];for(let[l,d]of o){let m=1-d;m>=n&&c.push({sessionId:l,similarity:m})}return c.sort((l,d)=>d.similarity-l.similarity),c.slice(0,t)}function gl(){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 Tr(e){let t=gl(),n=new Map;for(let r of e)for(let i=0;i<r.length;i++){let a=r[i],o=1/(t+i+1),c=n.get(a.id);c?(c.score+=o,c.lanes.push(a.lane),i+1<c.bestRank&&(c.bestRank=i+1,c.bestData=a.data)):n.set(a.id,{score:o,lanes:[a.lane],bestRank:i+1,bestData:a.data})}let s=[];for(let[r,i]of n)s.push({id:r,score:i.score,lanes:i.lanes,data:i.bestData});return s.sort((r,i)=>i.score-r.score),s}cn();import{existsSync as Sl,mkdirSync as ff,rmSync as _f,createWriteStream as hf,statSync as Ef}from"node:fs";import{join as Ar}from"node:path";I();var Tl=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function bl(){return Ar(y,"models","BAAI","bge-base-en-v1.5")}function ve(){let e=bl();return Tl.every(t=>Sl(Ar(e,t.path)))}I();import{existsSync as yl,readFileSync as wl,writeFileSync as wf,unlinkSync as Rf}from"node:fs";import{join as Rl}from"node:path";var Nr=Rl(y,"license.json");function Lr(){if(!yl(Nr))return null;try{let e=wl(Nr,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as Al,importSPKI as Nl}from"jose";var kr=`-----BEGIN PUBLIC KEY-----
1003
+ `).run(e,t,n,s),kl()}function kl(){v();let t=E().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();Al(Ll,JSON.stringify(t,null,2)+`
1004
+ `,"utf-8")}x();Pe();var ge=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,n){super(`semantic search refused: vec_chunks has ${t} rows (cap ${n}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=n}};function an(e,t={}){let n=t.limit??xl()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new ge(r,n)}function xl(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}qe();import{Worker as Ol}from"node:worker_threads";import{join as vl}from"node:path";var cn=class extends Error{constructor(n){super(`semantic kNN exceeded ${n}ms wall-clock budget -- query aborted`);this.timeoutMs=n}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},Il=1e4;function Cl(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function ln(e){let t=e.timeoutMs??Cl()??Il,n=(e.workerFactory??Dl)();return new Promise((s,r)=>{let i=setTimeout(()=>{n.terminate().catch(()=>{}),r(new cn(t))},t);n.once("message",o=>{clearTimeout(i);let a=o;a&&a.ok===!0&&Array.isArray(a.hits)?s(a.hits):r(new Error(a?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",o=>{clearTimeout(i),n.terminate().catch(()=>{}),r(o)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function Dl(){let e=vl(Te(),"dist","daemon","query-worker.js"),t={...process.env,RECALL_DB_PROFILE:"worker"};return new Ol(e,{env:t})}async function dn(e,t=50){let n=E();return oe(n),an(n),ln({query:e,limit:t})}async function xr(e,t=10,n=.65){let s=E();oe(s),an(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let i=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!i)return[];let o=await ln({precomputedVector:i.embedding,limit:t*5}),a=new Map;for(let l of o){if(l.sessionId===e)continue;let d=a.get(l.sessionId);(d===void 0||l.distance<d)&&a.set(l.sessionId,l.distance)}let c=[];for(let[l,d]of a){let m=1-d;m>=n&&c.push({sessionId:l,similarity:m})}return c.sort((l,d)=>d.similarity-l.similarity),c.slice(0,t)}function Ml(){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 Or(e){let t=Ml(),n=new Map;for(let r of e)for(let i=0;i<r.length;i++){let o=r[i],a=1/(t+i+1),c=n.get(o.id);c?(c.score+=a,c.lanes.push(o.lane),i+1<c.bestRank&&(c.bestRank=i+1,c.bestData=o.data)):n.set(o.id,{score:a,lanes:[o.lane],bestRank:i+1,bestData:o.data})}let s=[];for(let[r,i]of n)s.push({id:r,score:i.score,lanes:i.lanes,data:i.bestData});return s.sort((r,i)=>i.score-r.score),s}un();import{existsSync as jl,mkdirSync as Xf,rmSync as Gf,createWriteStream as zf,statSync as Yf}from"node:fs";import{join as Mr}from"node:path";I();var Hl=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function Wl(){return Mr(y,"models","BAAI","bge-base-en-v1.5")}function it(){let e=Wl();return Hl.every(t=>jl(Mr(e,t.path)))}I();import{existsSync as Bl,readFileSync as Xl,writeFileSync as Qf,unlinkSync as Zf}from"node:fs";import{join as Gl}from"node:path";var Pr=Gl(y,"license.json");function Fr(){if(!Bl(Pr))return null;try{let e=Xl(Pr,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as zl,importSPKI as Yl}from"jose";var $r=`-----BEGIN PUBLIC KEY-----
983
1005
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
984
1006
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
985
1007
  -----END PUBLIC KEY-----
986
- `,ln="ES256",xr="clauderecall.com",Or="clauderecall-cli";var rt=null;async function Ll(){return rt||(rt=await Nl(kr,ln),rt)}async function vr(e){try{let t=await Ll(),{payload:n}=await Al(e,t,{issuer:xr,audience:Or,algorithms:[ln]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as kl}from"node:crypto";import{hostname as xl,userInfo as Ol,platform as vl,arch as Il}from"node:os";function Ir(){let e="unknown";try{e=Ol().username}catch{}let t=[xl(),e,vl(),Il()];return kl("sha256").update(t.join("\0")).digest("hex")}I();import{existsSync as Cl,readFileSync as Dl,writeFileSync as $f}from"node:fs";import{join as Ml}from"node:path";var Cr=Ml(y,"license-check.json"),Wf=1440*60*1e3,Pl=720*60*60*1e3;function Fl(){if(!Cl(Cr))return null;try{let e=JSON.parse(Dl(Cr,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Dr(e){let t=Fl();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()>Pl?{revoked:!0,reason:"license has not been validated with the server in 30+ days. Reconnect to the internet and run `recall license check`"}:null}var $l=Date.UTC(2026,5,1,7,0,0);var Ul=1440*60*1e3,zf=60*Ul;async function it(){let e=Lr();if(!e)return{tier:"free"};let t=await vr(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Ir())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=Dr(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:jl(e,t.claims)}function jl(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}x();x();I();import{join as dn}from"node:path";var Hl=new Set(["pending","approved","rejected"]),Wl=new Set(["L1","L2","L3","L4","user"]),a_=dn(y,"links"),Bl=dn(y,"suggestions"),c_=dn(Bl,"index.json");function Mr(e){try{return JSON.parse(e)}catch{return e}}function Xl(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:Mr(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Gl(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:Mr(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function zl(e){if(!Wl.has(e))throw new Error(`invalid inferred_by: ${e}`)}function un(e){return E().prepare(`SELECT * FROM session_links
1008
+ `,pn="ES256",Ur="clauderecall.com",jr="clauderecall-cli";var ot=null;async function Kl(){return ot||(ot=await Yl($r,pn),ot)}async function Hr(e){try{let t=await Kl(),{payload:n}=await zl(e,t,{issuer:Ur,audience:jr,algorithms:[pn]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Jl}from"node:crypto";import{hostname as Vl,userInfo as ql,platform as Ql,arch as Zl}from"node:os";function Wr(){let e="unknown";try{e=ql().username}catch{}let t=[Vl(),e,Ql(),Zl()];return Jl("sha256").update(t.join("\0")).digest("hex")}I();import{existsSync as ed,readFileSync as td,writeFileSync as m_}from"node:fs";import{join as nd}from"node:path";var Br=nd(y,"license-check.json"),h_=1440*60*1e3,sd=720*60*60*1e3;function rd(){if(!ed(Br))return null;try{let e=JSON.parse(td(Br,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Xr(e){let t=rd();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()>sd?{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 id=1440*60*1e3,y_=60*id;async function mn(){let e=Fr();if(!e)return{tier:"free"};let t=await Hr(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Wr())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=Xr(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:od(e,t.claims)}function od(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}x();x();I();import{join as gn}from"node:path";var ad=new Set(["pending","approved","rejected"]),cd=new Set(["L1","L2","L3","L4","user"]),F_=gn(y,"links"),ld=gn(y,"suggestions"),$_=gn(ld,"index.json");function Gr(e){try{return JSON.parse(e)}catch{return e}}function dd(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:Gr(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function ud(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:Gr(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function pd(e){if(!cd.has(e))throw new Error(`invalid inferred_by: ${e}`)}function fn(e){return E().prepare(`SELECT * FROM session_links
987
1009
  WHERE source_session_id = ? OR target_session_id = ?
988
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(Xl)}function pn(e={}){let t=E(),n=[],s=[];if(e.status){if(!Hl.has(e.status))throw new Error(`invalid status: ${e.status}`);n.push("status = ?"),s.push(e.status)}e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.inferredBy&&(zl(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",i=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
1010
+ ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(dd)}function _n(e={}){let t=E(),n=[],s=[];if(e.status){if(!ad.has(e.status))throw new Error(`invalid status: ${e.status}`);n.push("status = ?"),s.push(e.status)}e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.inferredBy&&(pd(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",i=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
989
1011
  ORDER BY confidence DESC, created_at DESC
990
- LIMIT ?`).all(...s,i).map(Gl)}var Yl=4e3,Kl=2,Jl=30,ql=.2,Vl={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function ot(e){return e?Math.ceil(e.length/4):0}function Pr(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Jl);return Math.max(ql,t)}function Fr(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function $r(e){return E().prepare(`SELECT s.id,
1012
+ LIMIT ?`).all(...s,i).map(ud)}var md=4e3,gd=2,fd=30,_d=.2,hd={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function at(e){return e?Math.ceil(e.length/4):0}function zr(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/fd);return Math.max(_d,t)}function Yr(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function Kr(e){return E().prepare(`SELECT s.id,
991
1013
  NULLIF(sa.alias, '') AS alias,
992
1014
  s.auto_title,
993
1015
  s.auto_title_source,
@@ -998,28 +1020,26 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
998
1020
  FROM sessions s
999
1021
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1000
1022
  LEFT JOIN projects p ON p.id = s.project_id
1001
- WHERE s.id = ?`).get(e)??null}function Ur(e){let n=E().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!n||!n.summary)return null;let s=n.summary.trim();return s.length>0?s:null}function jr(e){let t=e.alias?.trim(),n=e.auto_title?.trim(),s=e.first_user_message?.trim();return n&&e.auto_title_source==="agent"?n:t||n||(s?s.slice(0,80):e.id.slice(0,8))}function Ql(e){let n=E().prepare(`SELECT id, auto_title, started_at
1023
+ WHERE s.id = ?`).get(e)??null}function Jr(e){let n=E().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!n||!n.summary)return null;let s=n.summary.trim();return s.length>0?s:null}function Vr(e){let t=e.alias?.trim(),n=e.auto_title?.trim(),s=e.first_user_message?.trim();return n&&e.auto_title_source==="agent"?n:t||n||(s?s.slice(0,80):e.id.slice(0,8))}function Ed(e){let n=E().prepare(`SELECT id, auto_title, started_at
1002
1024
  FROM sessions
1003
1025
  WHERE project_id = ?
1004
- ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,i=[];for(let p of n){if(!p.auto_title||!p.auto_title.startsWith("/")){i.push({id:p.id,brand:null,skill:null});continue}let g=p.auto_title.split(" \xB7 "),h=g[0].trim(),S=g.length>1?g.slice(1).join(" \xB7 ").trim():null;i.push({id:p.id,brand:S||null,skill:h||null}),S&&s.add(S),h&&r.add(h)}let a=[...s].sort(),o=new Map;a.forEach((p,g)=>o.set(p,g));let c=[...r].sort(),l=new Map;c.forEach((p,g)=>l.set(p,g));let d=new Map,m=new Map;for(let p of i){if(!p.brand||!p.skill)continue;let g=o.get(p.brand),h=l.get(p.skill);if(g===void 0||h===void 0)continue;let S=`${g}.${h}`,u=(d.get(S)??0)+1;d.set(S,u),m.set(p.id,`${g}.${h}.${u}`)}return{byId:m}}function Zl(e){return{table:e!==null?Ql(e):null,originProjectId:e,cache:new Map}}function at(e,t){let n=e.cache.get(t);if(n)return n;let s=$r(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,i={session_id:s.id,title:jr(s),decimal:r,summary:Ur(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,i),i}function ed(e,t){let s=E().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1026
+ ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,i=[];for(let p of n){if(!p.auto_title||!p.auto_title.startsWith("/")){i.push({id:p.id,brand:null,skill:null});continue}let g=p.auto_title.split(" \xB7 "),h=g[0].trim(),S=g.length>1?g.slice(1).join(" \xB7 ").trim():null;i.push({id:p.id,brand:S||null,skill:h||null}),S&&s.add(S),h&&r.add(h)}let o=[...s].sort(),a=new Map;o.forEach((p,g)=>a.set(p,g));let c=[...r].sort(),l=new Map;c.forEach((p,g)=>l.set(p,g));let d=new Map,m=new Map;for(let p of i){if(!p.brand||!p.skill)continue;let g=a.get(p.brand),h=l.get(p.skill);if(g===void 0||h===void 0)continue;let S=`${g}.${h}`,u=(d.get(S)??0)+1;d.set(S,u),m.set(p.id,`${g}.${h}.${u}`)}return{byId:m}}function Sd(e){return{table:e!==null?Ed(e):null,originProjectId:e,cache:new Map}}function ct(e,t){let n=e.cache.get(t);if(n)return n;let s=Kr(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,i={session_id:s.id,title:Vr(s),decimal:r,summary:Jr(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,i),i}function Td(e,t){let s=E().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1005
1027
  FROM thread_edges te
1006
1028
  WHERE te.session_id = ?
1007
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let i of s){if(!i.pid)continue;let a=at(e,i.pid);a&&r.push(a)}return r}function td(e,t){let s=E().prepare(`SELECT DISTINCT te.session_id AS sid
1029
+ AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let i of s){if(!i.pid)continue;let o=ct(e,i.pid);o&&r.push(o)}return r}function bd(e,t){let s=E().prepare(`SELECT DISTINCT te.session_id AS sid
1008
1030
  FROM thread_edges te
1009
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let i of s){if(!i.sid)continue;let a=at(e,i.sid);a&&r.push(a)}return r}function Hr(e){let t=Vl[e.linkType]??.5,n=Te(e.confidence),s=t*n,r=Pr(e.daysApart),i=e.embeddingCosine??.5,a=Te(e.pagerank);if(e.scoring==="pagerank")return Te(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Te(s):Te(i);let o=.35*s+.2*r+.2*i+.25*a;return Te(o)}function Te(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function nd(e,t,n,s,r){let i=new Map;function a(o,c){if(o===c)return;let l=i.get(o);l||(l=new Set,i.set(o,l)),l.add(c)}for(let o of t)a(o.source_session_id,o.target_session_id),a(o.target_session_id,o.source_session_id);for(let o of n)a(e,o.session_id);for(let o of n)a(o.session_id,e);for(let o of s)a(e,o.session_id);for(let o of s)a(o.session_id,e);if(r>1){let o=new Set([e]),c=new Set([e]);for(let l=1;l<r;l++){let d=new Set;for(let m of o){let p=i.get(m);if(p)for(let g of p){if(c.has(g))continue;let h=un(g).filter(S=>S.approved);for(let S of h)a(S.source_session_id,S.target_session_id),a(S.target_session_id,S.source_session_id);c.add(g),d.add(g)}}if(d.size===0)break;for(let m of d)o.add(m)}}return{edges:i}}function sd(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let i=1/r.length,a=new Map(r.map(l=>[l,i]));for(let l=0;l<n;l++){let d=new Map(r.map(m=>[m,(1-s)/r.length]));for(let m of r){let p=e.edges.get(m);if(!p||p.size===0)continue;let g=(a.get(m)??0)/p.size;for(let h of p)d.set(h,(d.get(h)??0)+s*g)}a=d}let o=0;for(let l of a.values())l>o&&(o=l);if(o<=0)return a;let c=new Map;for(let[l,d]of a)c.set(l,d/o);return c}var Wr=240;function Br(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function rd(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let i=Br(e.summary,Wr);return`${r}
1010
- ${i}`}return r}function id(e,t,n){let s=[],r=[],i=0,a=e.decimal?`${e.decimal}: `:"",o=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(s.push(o),i+=ot(o),e.summary){let c=Br(e.summary,Wr*4);s.push(c),i+=ot(c)}s.push("");for(let c of t){if(c.refs.length===0)continue;let l=`## ${c.heading}`,d=ot(l),m=[],p=0;for(let g of c.refs){let h=rd(g),S=ot(h);if(i+d+p+S>n){r.push({session_id:g.session_id,title:g.title,decimal:g.decimal,summary:g.summary,project:g.project,started_at:g.started_at});continue}m.push(h),p+=S}if(m.length>0){s.push(l);for(let g of m)s.push(g);s.push(""),i+=d+p}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
1031
+ WHERE te.parent_session_id = ?`).all(t),r=[];for(let i of s){if(!i.sid)continue;let o=ct(e,i.sid);o&&r.push(o)}return r}function qr(e){let t=hd[e.linkType]??.5,n=be(e.confidence),s=t*n,r=zr(e.daysApart),i=e.embeddingCosine??.5,o=be(e.pagerank);if(e.scoring==="pagerank")return be(o);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?be(s):be(i);let a=.35*s+.2*r+.2*i+.25*o;return be(a)}function be(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function yd(e,t,n,s,r){let i=new Map;function o(a,c){if(a===c)return;let l=i.get(a);l||(l=new Set,i.set(a,l)),l.add(c)}for(let a of t)o(a.source_session_id,a.target_session_id),o(a.target_session_id,a.source_session_id);for(let a of n)o(e,a.session_id);for(let a of n)o(a.session_id,e);for(let a of s)o(e,a.session_id);for(let a of s)o(a.session_id,e);if(r>1){let a=new Set([e]),c=new Set([e]);for(let l=1;l<r;l++){let d=new Set;for(let m of a){let p=i.get(m);if(p)for(let g of p){if(c.has(g))continue;let h=fn(g).filter(S=>S.approved);for(let S of h)o(S.source_session_id,S.target_session_id),o(S.target_session_id,S.source_session_id);c.add(g),d.add(g)}}if(d.size===0)break;for(let m of d)a.add(m)}}return{edges:i}}function wd(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let i=1/r.length,o=new Map(r.map(l=>[l,i]));for(let l=0;l<n;l++){let d=new Map(r.map(m=>[m,(1-s)/r.length]));for(let m of r){let p=e.edges.get(m);if(!p||p.size===0)continue;let g=(o.get(m)??0)/p.size;for(let h of p)d.set(h,(d.get(h)??0)+s*g)}o=d}let a=0;for(let l of o.values())l>a&&(a=l);if(a<=0)return o;let c=new Map;for(let[l,d]of o)c.set(l,d/a);return c}var Qr=240;function Zr(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function Rd(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let i=Zr(e.summary,Qr);return`${r}
1032
+ ${i}`}return r}function Ad(e,t,n){let s=[],r=[],i=0,o=e.decimal?`${e.decimal}: `:"",a=`# Neighborhood for ${e.session_id} (${o}${e.title})`;if(s.push(a),i+=at(a),e.summary){let c=Zr(e.summary,Qr*4);s.push(c),i+=at(c)}s.push("");for(let c of t){if(c.refs.length===0)continue;let l=`## ${c.heading}`,d=at(l),m=[],p=0;for(let g of c.refs){let h=Rd(g),S=at(h);if(i+d+p+S>n){r.push({session_id:g.session_id,title:g.title,decimal:g.decimal,summary:g.summary,project:g.project,started_at:g.started_at});continue}m.push(h),p+=S}if(m.length>0){s.push(l);for(let g of m)s.push(g);s.push(""),i+=d+p}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
1011
1033
  `)+`
1012
- `,budgetUsed:i,truncated:r}}function od(e,t,n,s,r,i){let a=[];for(let o of n){if(s&&!s.has(o.link_type))continue;let c=null;if(o.source_session_id===t.session_id?c=o.target_session_id:o.target_session_id===t.session_id&&(c=o.source_session_id),!c)continue;let l=at(e,c);if(!l)continue;let d=Fr(t.started_at,l.started_at),m=Hr({confidence:o.confidence,linkType:o.link_type,daysApart:d,embeddingCosine:null,pagerank:i.get(c)??0,scoring:r});a.push({...l,score:m,evidence:`(suggestion, ${o.inferred_by}) confidence=${o.confidence.toFixed(2)} ${Math.round(d)}d apart`,link_type:o.link_type})}return a}function Xr(e,t={}){let n=Math.max(100,Math.floor(t.budget??Yl)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Kl)),i=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,o=t.edgeTypes?new Set(t.edgeTypes):null,c=$r(e);if(!c)throw new Error(`session not found: ${e}`);let l=Zl(c.project_id),d={session_id:c.id,title:jr(c),decimal:l.table?.byId.get(c.id)??null,summary:Ur(c.id),project:c.project,started_at:c.started_at};l.cache.set(c.id,d);let m=ed(l,e),p=td(l,e),g=un(e).filter(A=>A.approved).filter(A=>!o||o.has(A.link_type)).filter(A=>i||A.link_type!=="wiki_link"),h=nd(e,g,m,p,r),S=sd(h),u=[],f=[],T=[],R=[];for(let A of g){let H=A.source_session_id===e?A.target_session_id:A.source_session_id,J=at(l,H);if(!J)continue;let ie=Fr(d.started_at,J.started_at),de=Hr({confidence:A.confidence,linkType:A.link_type,daysApart:ie,embeddingCosine:null,pagerank:S.get(H)??0,scoring:s}),mt=Pr(ie),X=`${A.link_type} confidence=${A.confidence.toFixed(2)} recency=${mt.toFixed(2)} (${Math.round(ie)}d apart)`,Me={...J,score:de,evidence:X,link_type:A.link_type};A.link_type==="citation"?u.push(Me):A.link_type==="similar"?f.push(Me):A.link_type==="wiki_link"?R.push(Me):T.push(Me)}if(a){let A=pn({sourceSessionId:e,status:"pending",limit:100}),H=pn({targetSessionId:e,status:"pending",limit:100}),J=[...A,...H],ie=new Set,de=J.filter(X=>ie.has(X.id)?!1:(ie.add(X.id),!0)),mt=od(l,d,de,o,s,S);for(let X of mt)X.link_type==="citation"?u.push(X):X.link_type==="similar"?f.push(X):X.link_type==="wiki_link"?R.push(X):T.push(X)}let k=(A,H)=>H.score-A.score;u.sort(k),f.sort(k),T.sort(k),R.sort(k);let L=id(d,[{heading:"Parents",refs:m},{heading:"Children",refs:p},{heading:"Citations (approved)",refs:u},{heading:"Similar sessions",refs:f},{heading:"Cousins (skill track + temporal)",refs:T},{heading:"Wiki links (manual)",refs:R}],n);return{origin:d,parents:m,children:p,citations:u,similar:f,cousins:T,wikiLinks:R,bundle:L.bundle,budgetUsed:L.budgetUsed,budgetRemaining:Math.max(0,n-L.budgetUsed),truncated:L.truncated}}import{readFileSync as Gu}from"node:fs";import{dirname as zu,join as Yu}from"node:path";var Ku=(()=>{try{let e=zu(Xu(import.meta.url));return Yu(e,"..","..","package.json")}catch{return"package.json"}})(),Ju=(()=>{try{return JSON.parse(Gu(Ku,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})();function qu(){let e=process.env.RECALL_MCP_ALLOW_WRITES;return e==="1"||e==="true"}var Vu=[/no such table:\s*vec_chunks/i,/database is locked/i];function Qu(e){return e instanceof Error?Vu.some(t=>t.test(e.message)):!1}async function In(e){try{return await e()}catch(t){if(!Qu(t))throw t;return await new Promise(n=>setTimeout(n,25)),await e()}}function O(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Cn(e){return{content:[{type:"text",text:e}]}}function K(e){return{content:[{type:"text",text:e}],isError:!0}}function mo(e){if(e instanceof Y)switch(e.kind){case"bundle-missing":return"embedder bundle missing \u2014 dev install requires `npm run build:cli`";case"platform-unsupported":return"embedder not available on this platform \u2014 run `recall semantic install` for setup";case"load-failed":return"embedder load failed \u2014 see daemon logs for details";default:return"embedder not available \u2014 run `recall semantic install`"}return"embedder load failed \u2014 see daemon logs for details"}async function Zu(){try{if((await it()).tier!=="pro"||!ve())return;await st(),process.stderr.write(`[mcp] embedder loaded
1013
- `)}catch(e){process.stderr.write(`[mcp] embedder preload failed: ${mo(e)}
1014
- `)}}async function ep(){if((await it()).tier!=="pro")return O({upgrade_required:!0,reason:"Semantic vector search requires Pro.",buy_url:"https://clauderecall.com/pro"});if(!ve())return O({error:"embedder_model_missing",reason:"Run `recall semantic install` to download the embedding model."});if(!Oe().loaded)try{await st()}catch(t){return O({error:"embedder_load_failed",reason:mo(t)})}return null}async function po(){try{return(await it()).tier!=="pro"||!ve()?!1:(Oe().loaded||await st(),!0)}catch(e){return e instanceof Y||process.stderr.write(`[mcp] isVectorLaneReady failed unexpectedly: ${e instanceof Error?e.message:String(e)}
1015
- `),!1}}function tp(){let e=new Wu({name:"claude-recall",version:Ju}),t=qu();if(e.registerTool("list_projects",{title:"List projects",description:"Every Claude Code project currently indexed by Recall, with session and message counts and the most recent activity timestamp.",inputSchema:{}},async()=>{let s=E().prepare(`SELECT p.name,
1034
+ `,budgetUsed:i,truncated:r}}function Nd(e,t,n,s,r,i){let o=[];for(let a of n){if(s&&!s.has(a.link_type))continue;let c=null;if(a.source_session_id===t.session_id?c=a.target_session_id:a.target_session_id===t.session_id&&(c=a.source_session_id),!c)continue;let l=ct(e,c);if(!l)continue;let d=Yr(t.started_at,l.started_at),m=qr({confidence:a.confidence,linkType:a.link_type,daysApart:d,embeddingCosine:null,pagerank:i.get(c)??0,scoring:r});o.push({...l,score:m,evidence:`(suggestion, ${a.inferred_by}) confidence=${a.confidence.toFixed(2)} ${Math.round(d)}d apart`,link_type:a.link_type})}return o}function ei(e,t={}){let n=Math.max(100,Math.floor(t.budget??md)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??gd)),i=t.includeWikiLinks??!0,o=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,c=Kr(e);if(!c)throw new Error(`session not found: ${e}`);let l=Sd(c.project_id),d={session_id:c.id,title:Vr(c),decimal:l.table?.byId.get(c.id)??null,summary:Jr(c.id),project:c.project,started_at:c.started_at};l.cache.set(c.id,d);let m=Td(l,e),p=bd(l,e),g=fn(e).filter(A=>A.approved).filter(A=>!a||a.has(A.link_type)).filter(A=>i||A.link_type!=="wiki_link"),h=yd(e,g,m,p,r),S=wd(h),u=[],f=[],T=[],R=[];for(let A of g){let H=A.source_session_id===e?A.target_session_id:A.source_session_id,J=ct(l,H);if(!J)continue;let ie=Yr(d.started_at,J.started_at),ue=qr({confidence:A.confidence,linkType:A.link_type,daysApart:ie,embeddingCosine:null,pagerank:S.get(H)??0,scoring:s}),gt=zr(ie),X=`${A.link_type} confidence=${A.confidence.toFixed(2)} recency=${gt.toFixed(2)} (${Math.round(ie)}d apart)`,Me={...J,score:ue,evidence:X,link_type:A.link_type};A.link_type==="citation"?u.push(Me):A.link_type==="similar"?f.push(Me):A.link_type==="wiki_link"?R.push(Me):T.push(Me)}if(o){let A=_n({sourceSessionId:e,status:"pending",limit:100}),H=_n({targetSessionId:e,status:"pending",limit:100}),J=[...A,...H],ie=new Set,ue=J.filter(X=>ie.has(X.id)?!1:(ie.add(X.id),!0)),gt=Nd(l,d,ue,a,s,S);for(let X of gt)X.link_type==="citation"?u.push(X):X.link_type==="similar"?f.push(X):X.link_type==="wiki_link"?R.push(X):T.push(X)}let k=(A,H)=>H.score-A.score;u.sort(k),f.sort(k),T.sort(k),R.sort(k);let L=Ad(d,[{heading:"Parents",refs:m},{heading:"Children",refs:p},{heading:"Citations (approved)",refs:u},{heading:"Similar sessions",refs:f},{heading:"Cousins (skill track + temporal)",refs:T},{heading:"Wiki links (manual)",refs:R}],n);return{origin:d,parents:m,children:p,citations:u,similar:f,cousins:T,wikiLinks:R,bundle:L.bundle,budgetUsed:L.budgetUsed,budgetRemaining:Math.max(0,n-L.budgetUsed),truncated:L.truncated}}import{readFileSync as pp}from"node:fs";import{dirname as mp,join as gp}from"node:path";var fp=(()=>{try{let e=mp(up(import.meta.url));return gp(e,"..","..","package.json")}catch{return"package.json"}})(),_p=(()=>{try{return JSON.parse(pp(fp,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})();function hp(){let e=process.env.RECALL_MCP_ALLOW_WRITES;return e==="1"||e==="true"}var Ep=[/no such table:\s*vec_chunks/i,/database is locked/i];function Sp(e){return e instanceof Error?Ep.some(t=>t.test(e.message)):!1}async function Pn(e){try{return await e()}catch(t){if(!Sp(t))throw t;return await new Promise(n=>setTimeout(n,25)),await e()}}function O(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Fn(e){return{content:[{type:"text",text:e}]}}function K(e){return{content:[{type:"text",text:e}],isError:!0}}function Tp(e){if(e instanceof Y)switch(e.kind){case"bundle-missing":return"embedder bundle missing \u2014 dev install requires `npm run build:cli`";case"platform-unsupported":return"embedder not available on this platform \u2014 run `recall semantic install` for setup";case"load-failed":return"embedder load failed \u2014 see daemon logs for details";default:return"embedder not available \u2014 run `recall semantic install`"}return"embedder load failed \u2014 see daemon logs for details"}async function bp(){if((await mn()).tier!=="pro")return O({upgrade_required:!0,reason:"Semantic vector search requires Pro.",buy_url:"https://clauderecall.com/pro"});if(!it())return O({error:"embedder_model_missing",reason:"Run `recall semantic install` to download the embedding model."});if(!ve().loaded)try{await rn()}catch(t){return O({error:"embedder_load_failed",reason:Tp(t)})}return null}async function Oo(){try{return(await mn()).tier!=="pro"||!it()?!1:(ve().loaded||await rn(),!0)}catch(e){return e instanceof Y||process.stderr.write(`[mcp] isVectorLaneReady failed unexpectedly: ${e instanceof Error?e.message:String(e)}
1035
+ `),!1}}function yp(){let e=new lp({name:"claude-recall",version:_p}),t=hp();if(e.registerTool("list_projects",{title:"List projects",description:"Every Claude Code project currently indexed by Recall, with session and message counts and the most recent activity timestamp.",inputSchema:{}},async()=>{let s=E().prepare(`SELECT p.name,
1016
1036
  COUNT(s.id) AS session_count,
1017
1037
  COALESCE(SUM(s.message_count), 0) AS message_count,
1018
1038
  MAX(s.started_at) AS latest
1019
1039
  FROM projects p
1020
1040
  LEFT JOIN sessions s ON s.project_id = p.id
1021
1041
  GROUP BY p.id
1022
- ORDER BY MAX(COALESCE(s.started_at, '')) DESC`).all();return O(s)}),e.registerTool("list_sessions",{title:"List sessions",description:"Recent sessions with alias, tags, and message counts. Optional filters for project, tag, and date range.",inputSchema:{project:N.string().optional().describe("Substring match against project name or decoded filesystem path."),tag:N.string().optional().describe("Only sessions carrying this tag (leading # optional)."),since:N.string().optional().describe("Only sessions started at or after this ISO timestamp or YYYY-MM-DD date."),until:N.string().optional().describe("Only sessions started at or before this ISO timestamp or YYYY-MM-DD date."),limit:N.number().int().min(1).max(500).optional()}},async({project:n,tag:s,since:r,until:i,limit:a})=>{let o=E(),c={limit:a??100},l="s.message_count > 2";if(n&&(l+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",c.proj=`%${n}%`),r&&(l+=" AND s.started_at >= @since",c.since=r),i&&(l+=" AND s.started_at <= @until",c.until=/^\d{4}-\d{2}-\d{2}$/.test(i)?`${i}T23:59:59.999Z`:i),s){let p=ue(s);p&&(l+=" AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag)",c.tag=p)}let m=o.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1042
+ ORDER BY MAX(COALESCE(s.started_at, '')) DESC`).all();return O(s)}),e.registerTool("list_sessions",{title:"List sessions",description:"Recent sessions with alias, tags, and message counts. Optional filters for project, tag, and date range.",inputSchema:{project:N.string().optional().describe("Substring match against project name or decoded filesystem path."),tag:N.string().optional().describe("Only sessions carrying this tag (leading # optional)."),since:N.string().optional().describe("Only sessions started at or after this ISO timestamp or YYYY-MM-DD date."),until:N.string().optional().describe("Only sessions started at or before this ISO timestamp or YYYY-MM-DD date."),limit:N.number().int().min(1).max(500).optional()}},async({project:n,tag:s,since:r,until:i,limit:o})=>{let a=E(),c={limit:o??100},l="s.message_count > 2";if(n&&(l+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",c.proj=`%${n}%`),r&&(l+=" AND s.started_at >= @since",c.since=r),i&&(l+=" AND s.started_at <= @until",c.until=/^\d{4}-\d{2}-\d{2}$/.test(i)?`${i}T23:59:59.999Z`:i),s){let p=pe(s);p&&(l+=" AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag)",c.tag=p)}let m=a.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1023
1043
  s.message_count, s.first_user_message, s.git_branch,
1024
1044
  NULLIF(sa.alias, '') AS alias,
1025
1045
  CASE WHEN sn.content IS NOT NULL AND sn.content != '' THEN 1 ELSE 0 END AS has_notes,
@@ -1034,7 +1054,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1034
1054
  LEFT JOIN session_notes sn ON sn.session_id = s.id
1035
1055
  WHERE ${l}
1036
1056
  ORDER BY COALESCE(s.started_at, '') DESC
1037
- LIMIT @limit`).all(c).map(({tags_csv:p,...g})=>({...g,tags:p?p.split(","):[]}));return O(m)}),e.registerTool("list_tags",{title:"List tags",description:"Every tag currently applied to a session, with its count, most popular first.",inputSchema:{}},async()=>O(Gn())),e.registerTool("search",{title:"Search messages",description:"Full-text search over every message in every indexed session. Use `#tag-name` tokens inside the query string to also filter by tag; plain terms are ANDed together. Pass `mode` to control the search lane: 'fts' = keyword BM25 only (always available, fastest), 'semantic' = vector embedding only (Pro + model installed; uses bge-base on-device), 'fused' = RRF-fused BM25 + vector (default; silently falls back to BM25-only when the vector lane is unavailable).",inputSchema:{query:N.string().describe("Text to find. Supports inline `#tag-name` tokens to narrow to sessions with the tag."),project:N.string().optional().describe("Substring match against project name or path."),limit:N.number().int().min(1).max(200).optional(),mode:N.enum(["fts","semantic","fused"]).optional().describe("Search lane: 'fts' (keyword only), 'semantic' (vector only \u2014 requires Pro + model installed), 'fused' (RRF-fused BM25 + vector, default; falls back to BM25 if vector is unavailable).")}},async({query:n,project:s,limit:r,mode:i})=>{let a=E(),o=n.trim();if(!o)return O({query:"",hits:[],tags:[],mode:i??"fused"});let c=i??"fused",l=o.split(/\s+/).filter(Boolean),d=l.filter(f=>f.startsWith("#")).map(ue).filter(f=>!!f),p=l.filter(f=>!f.startsWith("#")).map(f=>`"${f.replace(/"/g,"")}"`).join(" "),g=Math.max(1,Math.min(200,r??30));if(!p&&d.length>0){let f=`
1057
+ LIMIT @limit`).all(c).map(({tags_csv:p,...g})=>({...g,tags:p?p.split(","):[]}));return O(m)}),e.registerTool("list_tags",{title:"List tags",description:"Every tag currently applied to a session, with its count, most popular first.",inputSchema:{}},async()=>O(ts())),e.registerTool("search",{title:"Search messages",description:"Full-text search over every message in every indexed session. Use `#tag-name` tokens inside the query string to also filter by tag; plain terms are ANDed together. Pass `mode` to control the search lane: 'fts' = keyword BM25 only (always available, fastest), 'semantic' = vector embedding only (Pro + model installed; uses bge-base on-device), 'fused' = RRF-fused BM25 + vector (default; silently falls back to BM25-only when the vector lane is unavailable).",inputSchema:{query:N.string().describe("Text to find. Supports inline `#tag-name` tokens to narrow to sessions with the tag."),project:N.string().optional().describe("Substring match against project name or path."),limit:N.number().int().min(1).max(200).optional(),mode:N.enum(["fts","semantic","fused"]).optional().describe("Search lane: 'fts' (keyword only), 'semantic' (vector only \u2014 requires Pro + model installed), 'fused' (RRF-fused BM25 + vector, default; falls back to BM25 if vector is unavailable).")}},async({query:n,project:s,limit:r,mode:i})=>{let o=E(),a=n.trim();if(!a)return O({query:"",hits:[],tags:[],mode:i??"fused"});let c=i??"fused",l=a.split(/\s+/).filter(Boolean),d=l.filter(f=>f.startsWith("#")).map(pe).filter(f=>!!f),p=l.filter(f=>!f.startsWith("#")).map(f=>`"${f.replace(/"/g,"")}"`).join(" "),g=Math.max(1,Math.min(200,r??30));if(!p&&d.length>0){let f=`
1038
1058
  SELECT s.id AS session_id,
1039
1059
  s.id AS message_uuid,
1040
1060
  p.name AS project,
@@ -1047,7 +1067,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1047
1067
  JOIN projects p ON p.id = s.project_id
1048
1068
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1049
1069
  WHERE 1=1
1050
- `,T={limit:g};return s&&(f+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",T.proj=`%${s}%`),d.forEach((R,k)=>{f+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${k})`,T[`tag_${k}`]=R}),f+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit",O({query:o,hits:a.prepare(f).all(T),tags:d})}if(!p)return O({query:o,hits:[],tags:d,mode:c});if(c==="semantic"){if(!await po())return O({query:o,hits:[],tags:d,mode:"semantic",error:"semantic_unavailable",reason:'Semantic-only search requires Pro tier with the embedder model installed. Run `recall semantic install`, or call this tool with mode="fts" or mode="fused" for a BM25 fallback.'});try{let T=(await In(()=>an(o,g))).map(R=>({session_id:R.sessionId,snippet:R.text,matched_via:"vector"}));return O({query:o,hits:T,tags:d,mode:"semantic"})}catch(f){return f instanceof me?O({query:o,hits:[],tags:d,mode:"semantic",error:"corpus_too_large",message:f.message,meta:{rowCount:f.rowCount,limit:f.limit,env:"RECALL_KNN_MAX_CORPUS"}}):O({query:o,hits:[],tags:d,mode:"semantic",error:"semantic_failed",reason:f instanceof Error?f.message:"vector search failed"})}}let h=`
1070
+ `,T={limit:g};return s&&(f+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",T.proj=`%${s}%`),d.forEach((R,k)=>{f+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${k})`,T[`tag_${k}`]=R}),f+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit",O({query:a,hits:o.prepare(f).all(T),tags:d})}if(!p)return O({query:a,hits:[],tags:d,mode:c});if(c==="semantic"){if(!await Oo())return O({query:a,hits:[],tags:d,mode:"semantic",error:"semantic_unavailable",reason:'Semantic-only search requires Pro tier with the embedder model installed. Run `recall semantic install`, or call this tool with mode="fts" or mode="fused" for a BM25 fallback.'});try{let T=(await Pn(()=>dn(a,g))).map(R=>({session_id:R.sessionId,snippet:R.text,matched_via:"vector"}));return O({query:a,hits:T,tags:d,mode:"semantic"})}catch(f){return f instanceof ge?O({query:a,hits:[],tags:d,mode:"semantic",error:"corpus_too_large",message:f.message,meta:{rowCount:f.rowCount,limit:f.limit,env:"RECALL_KNN_MAX_CORPUS"}}):O({query:a,hits:[],tags:d,mode:"semantic",error:"semantic_failed",reason:f instanceof Error?f.message:"vector search failed"})}}let h=`
1051
1071
  SELECT m.session_id AS session_id,
1052
1072
  m.uuid AS message_uuid,
1053
1073
  p.name AS project,
@@ -1062,7 +1082,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1062
1082
  JOIN projects p ON p.id = s.project_id
1063
1083
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1064
1084
  WHERE messages_fts MATCH @fts
1065
- `,S={fts:p,limit:g};s&&(h+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",S.proj=`%${s}%`),d.forEach((f,T)=>{h+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${T})`,S[`tag_${T}`]=f}),h+=" ORDER BY bm25(messages_fts) LIMIT @limit";let u=a.prepare(h).all(S);if(c==="fts")return O({query:o,hits:u,tags:d,mode:"fts"});if(await po())try{let f=await In(()=>an(o,g)),T=u.map(L=>({id:String(L.session_id),data:L,lane:"bm25"})),R=f.map(L=>({id:L.sessionId,data:{session_id:L.sessionId,snippet:L.text,matched_via:"vector"},lane:"vector"})),w=Tr([T,R]).slice(0,g).map(L=>({...L.data,session_id:L.id,rrf_score:L.score,lanes:L.lanes}));return O({query:o,hits:w,tags:d,fusion:"rrf",mode:"fused"})}catch(f){if(f instanceof me)return O({query:o,hits:u,tags:d,mode:"fused",error:"corpus_too_large",message:f.message,meta:{rowCount:f.rowCount,limit:f.limit,env:"RECALL_KNN_MAX_CORPUS"}})}return O({query:o,hits:u,tags:d,mode:"fused"})}),e.registerTool("find_similar_sessions",{title:"Find similar sessions",description:"Find sessions semantically similar to a given session using vector embeddings (Pro-only). Returns related sessions ranked by cosine similarity.",inputSchema:{session_id:N.string().uuid().describe("Session UUID to find similar sessions for."),limit:N.number().int().min(1).max(50).optional().describe("Max results (default 10)."),min_cosine:N.number().min(0).max(1).optional().describe("Minimum cosine similarity threshold (default 0.65).")}},async({session_id:n,limit:s,min_cosine:r})=>{let i=await ep();if(i)return i;try{let a=await In(()=>Sr(n,s??10,r??.65));return O({session_id:n,similar:a})}catch(a){return a instanceof me?O({session_id:n,similar:[],error:"corpus_too_large",message:a.message,meta:{rowCount:a.rowCount,limit:a.limit,env:"RECALL_KNN_MAX_CORPUS"}}):K(a instanceof Error?a.message:"vector search failed")}}),e.registerTool("semantic_status",{title:"Semantic search status",description:"Health snapshot of the semantic vector search tier: model status, worker status, queue depth.",inputSchema:{}},async()=>{let n=Oe(),s=Rr(),r=ve();return O({embedder:n,worker:s,modelInstalled:r})}),e.registerTool("get_session",{title:"Get session transcript",description:"Return the full metadata and ordered messages for a session. Accepts a full UUID or an 8+-character id prefix.",inputSchema:{id:N.string().describe("Session id (full UUID or 8+-character prefix).")}},async({id:n})=>{let s=z(n);if(!s)return K(`session not found or prefix ambiguous: ${n}`);let r=E(),i=r.prepare(`SELECT s.id, s.project_id, s.started_at, s.ended_at,
1085
+ `,S={fts:p,limit:g};s&&(h+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",S.proj=`%${s}%`),d.forEach((f,T)=>{h+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${T})`,S[`tag_${T}`]=f}),h+=" ORDER BY bm25(messages_fts) LIMIT @limit";let u=o.prepare(h).all(S);if(c==="fts")return O({query:a,hits:u,tags:d,mode:"fts"});if(await Oo())try{let f=await Pn(()=>dn(a,g)),T=u.map(L=>({id:String(L.session_id),data:L,lane:"bm25"})),R=f.map(L=>({id:L.sessionId,data:{session_id:L.sessionId,snippet:L.text,matched_via:"vector"},lane:"vector"})),w=Or([T,R]).slice(0,g).map(L=>({...L.data,session_id:L.id,rrf_score:L.score,lanes:L.lanes}));return O({query:a,hits:w,tags:d,fusion:"rrf",mode:"fused"})}catch(f){if(f instanceof ge)return O({query:a,hits:u,tags:d,mode:"fused",error:"corpus_too_large",message:f.message,meta:{rowCount:f.rowCount,limit:f.limit,env:"RECALL_KNN_MAX_CORPUS"}})}return O({query:a,hits:u,tags:d,mode:"fused"})}),e.registerTool("find_similar_sessions",{title:"Find similar sessions",description:"Find sessions semantically similar to a given session using vector embeddings (Pro-only). Returns related sessions ranked by cosine similarity.",inputSchema:{session_id:N.string().uuid().describe("Session UUID to find similar sessions for."),limit:N.number().int().min(1).max(50).optional().describe("Max results (default 10)."),min_cosine:N.number().min(0).max(1).optional().describe("Minimum cosine similarity threshold (default 0.65).")}},async({session_id:n,limit:s,min_cosine:r})=>{let i=await bp();if(i)return i;try{let o=await Pn(()=>xr(n,s??10,r??.65));return O({session_id:n,similar:o})}catch(o){return o instanceof ge?O({session_id:n,similar:[],error:"corpus_too_large",message:o.message,meta:{rowCount:o.rowCount,limit:o.limit,env:"RECALL_KNN_MAX_CORPUS"}}):K(o instanceof Error?o.message:"vector search failed")}}),e.registerTool("semantic_status",{title:"Semantic search status",description:"Health snapshot of the semantic vector search tier: model status, worker status, queue depth.",inputSchema:{}},async()=>{let n=ve(),s=Dr(),r=it();return O({embedder:n,worker:s,modelInstalled:r})}),e.registerTool("get_session",{title:"Get session transcript",description:"Return the full metadata and ordered messages for a session. Accepts a full UUID or an 8+-character id prefix.",inputSchema:{id:N.string().describe("Session id (full UUID or 8+-character prefix).")}},async({id:n})=>{let s=z(n);if(!s)return K(`session not found or prefix ambiguous: ${n}`);let r=E(),i=r.prepare(`SELECT s.id, s.project_id, s.started_at, s.ended_at,
1066
1086
  s.message_count, s.user_message_count, s.assistant_message_count,
1067
1087
  s.first_user_message, s.git_branch, s.version, s.indexed_at,
1068
1088
  p.name AS project_name,
@@ -1070,15 +1090,14 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
1070
1090
  FROM sessions s
1071
1091
  JOIN projects p ON p.id = s.project_id
1072
1092
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1073
- WHERE s.id = ?`).get(s);if(!i)return K(`session metadata missing for ${s}`);let a=Fe(s),o=r.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1093
+ WHERE s.id = ?`).get(s);if(!i)return K(`session metadata missing for ${s}`);let o=$e(s),a=r.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1074
1094
  FROM messages WHERE session_id = ?
1075
- ORDER BY COALESCE(timestamp, ''), rowid`).all(s);return O({session:{...i,tags:a},messages:o})}),e.registerTool("context_for_session",{title:"Export session as context",description:"Render a past session as markdown ready to paste into a fresh Claude conversation. This is the flagship Recall operation: pipe any previous session back into a new chat as memory.",inputSchema:{id:N.string().describe("Session id (full UUID or 8+-character prefix)."),mode:N.enum(["condensed","full"]).optional().describe("`condensed` (default) strips tool-call JSON; `full` keeps everything."),includeSidechain:N.boolean().optional().describe("Include subagent / sidechain messages (default false)."),prelude:N.string().max(1e4).optional().describe("Optional header prepended above the transcript (max 10 000 chars)."),since:N.string().optional().describe("Only messages at or after this ISO timestamp.")}},async({id:n,mode:s,includeSidechain:r,prelude:i,since:a})=>{let o=z(n);if(!o)return K(`session not found or prefix ambiguous: ${n}`);let c=E(),l=c.prepare(`SELECT s.id, p.name AS project_name,
1095
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(s);return O({session:{...i,tags:o},messages:a})}),e.registerTool("context_for_session",{title:"Export session as context",description:"Render a past session as markdown ready to paste into a fresh Claude conversation. This is the flagship Recall operation: pipe any previous session back into a new chat as memory.",inputSchema:{id:N.string().describe("Session id (full UUID or 8+-character prefix)."),mode:N.enum(["condensed","full"]).optional().describe("`condensed` (default) strips tool-call JSON; `full` keeps everything."),includeSidechain:N.boolean().optional().describe("Include subagent / sidechain messages (default false)."),prelude:N.string().max(1e4).optional().describe("Optional header prepended above the transcript (max 10 000 chars)."),since:N.string().optional().describe("Only messages at or after this ISO timestamp.")}},async({id:n,mode:s,includeSidechain:r,prelude:i,since:o})=>{let a=z(n);if(!a)return K(`session not found or prefix ambiguous: ${n}`);let c=E(),l=c.prepare(`SELECT s.id, p.name AS project_name,
1076
1096
  s.started_at, s.ended_at, s.message_count, s.git_branch
1077
1097
  FROM sessions s JOIN projects p ON p.id = s.project_id
1078
- WHERE s.id = ?`).get(o);if(!l)return K(`session metadata missing for ${o}`);let d=c.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1098
+ WHERE s.id = ?`).get(a);if(!l)return K(`session metadata missing for ${a}`);let d=c.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1079
1099
  FROM messages WHERE session_id = ?
1080
- ORDER BY COALESCE(timestamp, ''), rowid`).all(o),m=Bn(l,d,{mode:s??"condensed",includeSidechain:r??!1,prelude:i??null,since:a??null}),p=a?"since":s??"condensed";return Er(o,Math.ceil(m.length/4),p,"mcp"),Cn(m)}),e.registerTool("recall_neighborhood",{title:"Recall: neighborhood context bundle",description:"Build a ranked, budget-bounded markdown bundle for a session: parents + children from thread_edges, plus approved citations / similar / cousins / wiki_links from the cognitive graph. Pipe-friendly output ready to seed a fresh conversation. Reads only approved edges by default \u2014 pending suggestions are opt-in.",inputSchema:{session_id:N.string().describe("Session UUID or 8+-character prefix."),budget:N.number().int().min(100).max(5e4).optional().describe("Token budget for the assembled bundle. Default 4000."),scoring:N.enum(["pagerank","embedding-rerank","hybrid"]).optional().describe("Scoring mode (default hybrid)."),edge_types:N.array(N.enum(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"])).optional().describe("Restrict to certain link types. Default: all approved types."),max_depth:N.number().int().min(1).max(5).optional().describe("Pagerank traversal depth on the local subgraph (default 2)."),include_wiki_links:N.boolean().optional().describe("Include manual wiki_link rows. Default true."),include_suggestions:N.boolean().optional().describe("Surface pending session_link_suggestions. Debug only; default false."),format:N.enum(["markdown","json"]).optional().describe("markdown (default) returns the bundle as text; json returns the full NeighborhoodResult.")}},async({session_id:n,budget:s,scoring:r,edge_types:i,max_depth:a,include_wiki_links:o,include_suggestions:c,format:l})=>{let d=z(n);if(!d)return K(`session not found or prefix ambiguous: ${n}`);try{let m=Xr(d,{budget:s,scoring:r,edgeTypes:i,maxDepth:a,includeWikiLinks:o,includeSuggestions:c});return l==="json"?O(m):Cn(m.bundle)}catch(m){return K(m instanceof Error?m.message:String(m))}}),e.registerTool("doctor",{title:"Health check",description:"Read-only diagnostic snapshot of the local Claude Recall database: total size, WAL size, free pages, FTS5 fragmentation for messages and sessions, vector index row count, free disk space, integrity check, and row counts per surface table. Returns structured JSON. Equivalent to running `recall doctor --json` from the shell. Safe to call as often as needed; no side effects.",inputSchema:{}},async()=>{let{buildHealthReport:n}=await Promise.resolve().then(()=>(co(),ao));return O(n())}),dr(e),t){let n=Number(process.env.RECALL_MCP_RATE_LIMIT),s=Number.isFinite(n)&&n>0?n:Dt,r=E(),i=new Date(Date.now()-6e4).toISOString(),a=r.prepare("SELECT at FROM mcp_audit_events WHERE at >= ? ORDER BY at ASC").all(i),o=new ae(s);for(let c of a){let l=new Date(c.at).getTime();if(Number.isFinite(l))try{o.consume(l)}catch{break}}e.registerTool("list_sessions_to_tag",{title:"List sessions to tag",description:"Return session summaries and sampled messages formatted for an agent to propose tags. Respects the same scope filters used by the Recall UI scan. Only returns data if auto-tagging is enabled in config.",inputSchema:{untaggedOnly:N.boolean().optional().describe("Only sessions with zero tags (default false)."),project:N.string().optional().describe("Exact project name match."),collectionId:N.string().optional().describe("Only sessions in this collection."),limit:N.number().int().min(1).max(200).optional()}},async c=>{let l=wt();if(!l.enabled)return K("auto-tagging is disabled; enable it in Recall settings before scanning");let d=Ue({untaggedOnly:c.untaggedOnly,project:c.project,collectionId:c.collectionId,limit:c.limit??50});return O({count:d.length,sessions:d,guidance:`Produce ${l.minTagsPerSession}-${l.maxTagsPerSession} tags per session. Prefer existing tags from list_tags for consistency. Use apply_tags to write results.`})}),e.registerTool("apply_tags",{title:"Apply tags to a session",description:"Add one or more tags to a session (merge-mode, never removes existing). Only works when auto-tagging is enabled. Accepts full UUID or 8+-character id prefix.",inputSchema:{sessionId:N.string().describe("Full session UUID or 8+-character prefix."),tags:N.array(N.string()).min(1).max(10).describe("Tags to add. Normalized server-side (lowercase, hyphens, strip #).")}},async({sessionId:c,tags:l})=>{if(!wt().enabled)return K("auto-tagging is disabled; enable it in Recall settings before writing tags");let m=z(c);if(!m)return K(`session not found or prefix ambiguous: ${c}`);try{let p=await D({tool:"apply_tags",args:{sessionId:m,tags:l},limiter:o,run:()=>{let g=[],h=[];for(let S of l)try{let{tag:u,added:f}=Pe(m,S);f?g.push(u):h.push({tag:u,reason:"already present"})}catch(u){h.push({tag:S,reason:u instanceof Error?u.message:String(u)})}return{sessionId:m,applied:g,skipped:h}}});return O(p)}catch(p){return p instanceof Error&&p.message.startsWith("SQLITE_")?K("database constraint error"):K(p instanceof Error?p.message:String(p))}}),e.registerTool("optimize",{title:"Optimize the database",description:"Run the local maintenance pass: WAL checkpoint (TRUNCATE), FTS5 segment merge for messages and sessions, refresh planner stats. Equivalent to `recall optimize` from the shell. Safe while the daemon is running. Set vacuum=true to also reclaim free pages \u2014 but VACUUM rewrites the entire DB and requires the daemon to be stopped, so it errors out if the daemon is up. Write-mode only.",inputSchema:{vacuum:N.boolean().optional().describe("Also run VACUUM. Requires the daemon to be stopped.")}},async({vacuum:c})=>{let{runOptimize:l}=await Promise.resolve().then(()=>(uo(),lo)),d=process.stdout.write.bind(process.stdout),m="";process.stdout.write=p=>(m+=typeof p=="string"?p:Buffer.from(p).toString("utf-8"),!0);try{await l({vacuum:!!c,json:!0})}finally{process.stdout.write=d}try{return O(JSON.parse(m.trim()))}catch{return Cn(m.trim())}}),ns(e,{limiter:o}),ur(e,{limiter:o}),console.error(`[claude-recall-mcp] MCP writes ENABLED \u2014 write tools registered (rate limit ${s}/min)`),console.error("[claude-recall-mcp] MCP thread writes ENABLED")}else console.error("[claude-recall-mcp] MCP writes DISABLED (read-only) \u2014 pass --allow-writes to enable"),console.error("[claude-recall-mcp] MCP thread writes DISABLED (read-only)");for(let n of Kn)e.registerPrompt(n.name,{title:n.title,description:n.description,argsSchema:n.argsSchema},async s=>({messages:[{role:"user",content:{type:"text",text:n.build(s)}}]}));return e}async function np(){let e=tp(),t=new Bu;await e.connect(t),Zu().catch(a=>{let o=a instanceof Error?a.message:String(a);process.stderr.write(`[claude-recall-mcp] background embedder preload failed: ${o}
1081
- `)});let n=!1,s=async a=>{if(!n){n=!0,process.stderr.write(`[claude-recall-mcp] shutting down: ${a}
1082
- `);try{await e.close()}catch(o){let c=o instanceof Error?o.message:String(o);process.stderr.write(`[claude-recall-mcp] server.close() failed: ${c}
1083
- `)}Un(),process.exit(0)}};process.on("SIGINT",()=>{s("SIGINT")}),process.on("SIGTERM",()=>{s("SIGTERM")}),jn({onShutdown:()=>s("parent death")}),Wn({onShutdown:a=>s(a)});let i=e._registeredTools;if(i&&typeof i=="object")for(let a of Object.values(i)){let o=a.handler;typeof o=="function"&&(a.handler=function(...l){return Tt(),o.apply(this,l)})}else process.stderr.write(`[claude-recall-mcp] WARN: MCP SDK tool registry shape changed; idle watchdog will rely on probe+lifetime only
1084
- `)}var sp=(()=>{try{let e=process.argv[1];return e?import.meta.url===new URL(`file://${e}`).href:!1}catch{return!1}})();sp&&np().catch(e=>{console.error("[claude-recall-mcp] fatal:",e),process.exit(1)});export{tp as buildServer,In as withVecRetry};
1100
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a),m=Zn(l,d,{mode:s??"condensed",includeSidechain:r??!1,prelude:i??null,since:o??null}),p=o?"since":s??"condensed";return kr(a,Math.ceil(m.length/4),p,"mcp"),Fn(m)}),e.registerTool("recall_neighborhood",{title:"Recall: neighborhood context bundle",description:"Build a ranked, budget-bounded markdown bundle for a session: parents + children from thread_edges, plus approved citations / similar / cousins / wiki_links from the cognitive graph. Pipe-friendly output ready to seed a fresh conversation. Reads only approved edges by default \u2014 pending suggestions are opt-in.",inputSchema:{session_id:N.string().describe("Session UUID or 8+-character prefix."),budget:N.number().int().min(100).max(5e4).optional().describe("Token budget for the assembled bundle. Default 4000."),scoring:N.enum(["pagerank","embedding-rerank","hybrid"]).optional().describe("Scoring mode (default hybrid)."),edge_types:N.array(N.enum(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"])).optional().describe("Restrict to certain link types. Default: all approved types."),max_depth:N.number().int().min(1).max(5).optional().describe("Pagerank traversal depth on the local subgraph (default 2)."),include_wiki_links:N.boolean().optional().describe("Include manual wiki_link rows. Default true."),include_suggestions:N.boolean().optional().describe("Surface pending session_link_suggestions. Debug only; default false."),format:N.enum(["markdown","json"]).optional().describe("markdown (default) returns the bundle as text; json returns the full NeighborhoodResult.")}},async({session_id:n,budget:s,scoring:r,edge_types:i,max_depth:o,include_wiki_links:a,include_suggestions:c,format:l})=>{let d=z(n);if(!d)return K(`session not found or prefix ambiguous: ${n}`);try{let m=ei(d,{budget:s,scoring:r,edgeTypes:i,maxDepth:o,includeWikiLinks:a,includeSuggestions:c});return l==="json"?O(m):Fn(m.bundle)}catch(m){return K(m instanceof Error?m.message:String(m))}}),e.registerTool("doctor",{title:"Health check",description:"Read-only diagnostic snapshot of the local Claude Recall database: total size, WAL size, free pages, FTS5 fragmentation for messages and sessions, vector index row count, free disk space, integrity check, and row counts per surface table. Returns structured JSON. Equivalent to running `recall doctor --json` from the shell. Safe to call as often as needed; no side effects.",inputSchema:{}},async()=>{let{buildHealthReport:n}=await Promise.resolve().then(()=>(Lo(),No));return O(n())}),Tr(e),t){let n=Number(process.env.RECALL_MCP_RATE_LIMIT),s=Number.isFinite(n)&&n>0?n:Mt,r=E(),i=new Date(Date.now()-6e4).toISOString(),o=r.prepare("SELECT at FROM mcp_audit_events WHERE at >= ? ORDER BY at ASC").all(i),a=new ce(s);for(let c of o){let l=new Date(c.at).getTime();if(Number.isFinite(l))try{a.consume(l)}catch{break}}e.registerTool("list_sessions_to_tag",{title:"List sessions to tag",description:"Return session summaries and sampled messages formatted for an agent to propose tags. Respects the same scope filters used by the Recall UI scan. Only returns data if auto-tagging is enabled in config.",inputSchema:{untaggedOnly:N.boolean().optional().describe("Only sessions with zero tags (default false)."),project:N.string().optional().describe("Exact project name match."),collectionId:N.string().optional().describe("Only sessions in this collection."),limit:N.number().int().min(1).max(200).optional()}},async c=>{let l=Rt();if(!l.enabled)return K("auto-tagging is disabled; enable it in Recall settings before scanning");let d=je({untaggedOnly:c.untaggedOnly,project:c.project,collectionId:c.collectionId,limit:c.limit??50});return O({count:d.length,sessions:d,guidance:`Produce ${l.minTagsPerSession}-${l.maxTagsPerSession} tags per session. Prefer existing tags from list_tags for consistency. Use apply_tags to write results.`})}),e.registerTool("apply_tags",{title:"Apply tags to a session",description:"Add one or more tags to a session (merge-mode, never removes existing). Only works when auto-tagging is enabled. Accepts full UUID or 8+-character id prefix.",inputSchema:{sessionId:N.string().describe("Full session UUID or 8+-character prefix."),tags:N.array(N.string()).min(1).max(10).describe("Tags to add. Normalized server-side (lowercase, hyphens, strip #).")}},async({sessionId:c,tags:l})=>{if(!Rt().enabled)return K("auto-tagging is disabled; enable it in Recall settings before writing tags");let m=z(c);if(!m)return K(`session not found or prefix ambiguous: ${c}`);try{let p=await D({tool:"apply_tags",args:{sessionId:m,tags:l},limiter:a,run:()=>{let g=[],h=[];for(let S of l)try{let{tag:u,added:f}=Fe(m,S);f?g.push(u):h.push({tag:u,reason:"already present"})}catch(u){h.push({tag:S,reason:u instanceof Error?u.message:String(u)})}return{sessionId:m,applied:g,skipped:h}}});return O(p)}catch(p){return p instanceof Error&&p.message.startsWith("SQLITE_")?K("database constraint error"):K(p instanceof Error?p.message:String(p))}}),e.registerTool("optimize",{title:"Optimize the database",description:"Run the local maintenance pass: WAL checkpoint (TRUNCATE), FTS5 segment merge for messages and sessions, refresh planner stats. Equivalent to `recall optimize` from the shell. Safe while the daemon is running. Set vacuum=true to also reclaim free pages \u2014 but VACUUM rewrites the entire DB and requires the daemon to be stopped, so it errors out if the daemon is up. Write-mode only.",inputSchema:{vacuum:N.boolean().optional().describe("Also run VACUUM. Requires the daemon to be stopped.")}},async({vacuum:c})=>{let{runOptimize:l}=await Promise.resolve().then(()=>(xo(),ko)),d=process.stdout.write.bind(process.stdout),m="";process.stdout.write=p=>(m+=typeof p=="string"?p:Buffer.from(p).toString("utf-8"),!0);try{await l({vacuum:!!c,json:!0})}finally{process.stdout.write=d}try{return O(JSON.parse(m.trim()))}catch{return Fn(m.trim())}}),ps(e,{limiter:a}),br(e,{limiter:a}),console.error(`[claude-recall-mcp] MCP writes ENABLED \u2014 write tools registered (rate limit ${s}/min)`),console.error("[claude-recall-mcp] MCP thread writes ENABLED")}else console.error("[claude-recall-mcp] MCP writes DISABLED (read-only) \u2014 pass --allow-writes to enable"),console.error("[claude-recall-mcp] MCP thread writes DISABLED (read-only)");for(let n of rs)e.registerPrompt(n.name,{title:n.title,description:n.description,argsSchema:n.argsSchema},async s=>({messages:[{role:"user",content:{type:"text",text:n.build(s)}}]}));return e}async function wp(){process.env.RECALL_DB_PROFILE=process.env.RECALL_DB_PROFILE??"light";let e=yp(),t=new dp;await e.connect(t);let n=!1,s=async o=>{if(!n){n=!0,process.stderr.write(`[claude-recall-mcp] shutting down: ${o}
1101
+ `);try{await e.close()}catch(a){let c=a instanceof Error?a.message:String(a);process.stderr.write(`[claude-recall-mcp] server.close() failed: ${c}
1102
+ `)}Jn(),process.exit(0)}};process.on("SIGINT",()=>{s("SIGINT")}),process.on("SIGTERM",()=>{s("SIGTERM")}),Vn({onShutdown:()=>s("parent death")}),Qn({onShutdown:o=>s(o)});let i=e._registeredTools;if(i&&typeof i=="object")for(let o of Object.values(i)){let a=o.handler;typeof a=="function"&&(o.handler=function(...l){return bt(),a.apply(this,l)})}else process.stderr.write(`[claude-recall-mcp] WARN: MCP SDK tool registry shape changed; idle watchdog will rely on probe+lifetime only
1103
+ `)}var Rp=(()=>{try{let e=process.argv[1];return e?import.meta.url===new URL(`file://${e}`).href:!1}catch{return!1}})();Rp&&wp().catch(e=>{console.error("[claude-recall-mcp] fatal:",e),process.exit(1)});export{yp as buildServer,Pn as withVecRetry};