@clauderecallhq/cli 0.77.1 → 0.92.3

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 Yr=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var Ke=(e,t)=>{for(var s in t)Yr(e,s,{get:t[s],enumerable:!0})};import{createRequire as zr}from"node:module";var Jr,Kr,Vr,Ve,qe,qt,Zt=b(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...s){let n=s[0];return t==="warning"&&n instanceof Error&&n.name==="ExperimentalWarning"&&/SQLite/i.test(n.message)?!1:e(t,...s)}}Jr=zr(import.meta.url),Kr=["node","sqlite"].join(":"),Vr=Jr(Kr),Ve=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let s=t.length===0?this.inner.run():this.inner.run(...t);return{changes:s.changes,lastInsertRowid:s.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},qe=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new Vr.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new Ve(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,s={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(s.simple){let n=this.inner.prepare(`PRAGMA ${t}`).get();return n&&typeof n=="object"?Object.values(n)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...n)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...n);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,s){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),s===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,s)}},qt=qe});import{homedir as Qt}from"node:os";import{join as Ze,basename as xl}from"node:path";import{existsSync as qr,mkdirSync as Zr,chmodSync as Qr,readdirSync as kl,statSync as Cl}from"node:fs";function k(){qr(S)||Zr(S,{recursive:!0,mode:448}),process.platform!=="win32"&&Qr(S,448)}var Qe,S,ge,D=b(()=>{"use strict";Qe=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Ze(Qt(),".claude","projects"),S=process.env.RECALL_HOME?process.env.RECALL_HOME:Ze(Qt(),".recall"),ge=Ze(S,"db.sqlite")});function ts(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),s=new Set(t.map(g=>g.name)),n=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[g,_]of n)s.has(g)||e.exec(`ALTER TABLE sessions ADD COLUMN ${g} ${_}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),i=new Set(r.map(g=>g.name)),o=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[g,_]of o)i.has(g)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${g} ${_}`);let a=e.prepare("PRAGMA table_info(session_notes)").all(),c=new Set(a.map(g=>g.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[g,_]of d)c.has(g)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${g} ${_}`);let l=e.prepare("PRAGMA table_info(threads)").all();new Set(l.map(g=>g.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 u=e.prepare("PRAGMA table_info(thread_folders)").all(),p=new Set(u.map(g=>g.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 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(`
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 Yr=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var Ke=(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
- `)}var es,ss=b(()=>{"use strict";es=`
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=`
16
16
  PRAGMA journal_mode = WAL;
17
17
  PRAGMA synchronous = NORMAL;
18
18
  PRAGMA foreign_keys = ON;
@@ -21,7 +21,11 @@ CREATE TABLE IF NOT EXISTS projects (
21
21
  id INTEGER PRIMARY KEY AUTOINCREMENT,
22
22
  encoded_path TEXT UNIQUE NOT NULL,
23
23
  decoded_path TEXT NOT NULL,
24
- name TEXT NOT NULL
24
+ name TEXT NOT NULL,
25
+ repo_root TEXT,
26
+ main_repo TEXT,
27
+ is_repo INTEGER NOT NULL DEFAULT 0,
28
+ is_worktree INTEGER NOT NULL DEFAULT 0
25
29
  );
26
30
 
27
31
  CREATE TABLE IF NOT EXISTS sessions (
@@ -242,15 +246,62 @@ CREATE TABLE IF NOT EXISTS chunk_meta (
242
246
  CREATE INDEX IF NOT EXISTS idx_chunk_meta_session ON chunk_meta(session_id);
243
247
  CREATE INDEX IF NOT EXISTS idx_chunk_meta_stale ON chunk_meta(stale) WHERE stale = 1;
244
248
 
249
+ -- chunk_queue action CHECK constraint expanded in v0.78 to include
250
+ -- 'pending_post_migration'. New databases accept this value; existing
251
+ -- databases retain the older 3-action check (SQLite cannot ALTER a
252
+ -- CHECK constraint in place). The Phase 2 migrate command (src/semantic/
253
+ -- migrate.ts, TBD) pauses the embedder worker before enqueueing any
254
+ -- 'pending_post_migration' rows; on an older DB the watcher (D1) catches
255
+ -- the resulting CHECK violation and falls back to 'embed' \u2014 safe because
256
+ -- the worker is paused on the migration lock during the window.
245
257
  CREATE TABLE IF NOT EXISTS chunk_queue (
246
258
  id INTEGER PRIMARY KEY AUTOINCREMENT,
247
259
  session_id TEXT NOT NULL,
248
260
  message_uuid TEXT,
249
- action TEXT NOT NULL CHECK (action IN ('embed', 'reembed', 'delete')),
261
+ action TEXT NOT NULL CHECK (action IN ('embed', 'reembed', 'delete', 'pending_post_migration')),
250
262
  enqueued_at TEXT NOT NULL DEFAULT (datetime('now'))
251
263
  );
252
264
  CREATE INDEX IF NOT EXISTS idx_chunk_queue_session ON chunk_queue(session_id);
253
265
 
266
+ -- v0.78 \u2014 semantic migration state (Phase 2 of Tier 2 modernization).
267
+ -- Tracks the lifecycle of a \`recall semantic migrate\` run: in-progress with
268
+ -- a cursor, completed (vec_chunks now holds the new-backend vectors and
269
+ -- vec_chunks_v1_backup retains the prior set for 30 days), failed (the
270
+ -- partial vec_chunks_v2 is left in place for forensic inspection then
271
+ -- discarded by a future migrate retry), or rolled_back (the backup was
272
+ -- swapped back in via \`recall semantic rollback-migration\`).
273
+ --
274
+ -- schema_version lets a future CLI version refuse to resume a cursor it
275
+ -- cannot interpret rather than corrupt the migration state silently.
276
+ CREATE TABLE IF NOT EXISTS migration_state (
277
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
278
+ schema_version INTEGER NOT NULL,
279
+ status TEXT NOT NULL CHECK (status IN (
280
+ 'in_progress',
281
+ 'paused',
282
+ 'completed',
283
+ 'rolled_back',
284
+ 'failed'
285
+ )),
286
+ started_at TEXT NOT NULL,
287
+ completed_at TEXT,
288
+ lock_taken_at TEXT,
289
+ lock_taken_by_pid INTEGER,
290
+ cursor_session_id TEXT,
291
+ cursor_chunk_id INTEGER,
292
+ model_id_old TEXT NOT NULL,
293
+ model_id_new TEXT NOT NULL
294
+ );
295
+ CREATE INDEX IF NOT EXISTS idx_migration_state_status ON migration_state(status);
296
+
297
+ -- Belt-and-suspenders: at most one active (in-progress or paused) row.
298
+ -- Partial UNIQUE INDEX on schema_version with predicate status IN ('in_progress','paused')
299
+ -- means "at most one row per schema_version that is active." Since schema_version
300
+ -- is monotonic, this effectively caps active migrations at one regardless of
301
+ -- application-layer race conditions.
302
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_migration_state_single_active
303
+ ON migration_state(schema_version) WHERE status IN ('in_progress', 'paused');
304
+
254
305
  -- v0.67.0 -- retention/archive (P6). Power-user durability: sessions older
255
306
  -- than the user chosen window can be moved to messages_archive so the hot
256
307
  -- DB stays small without dropping data. Sessions metadata stays hot for
@@ -292,11 +343,22 @@ INSERT OR IGNORE INTO app_settings(key, value) VALUES ('semantic_enabled', '0');
292
343
  -- Drop unconditional v0.10/v0.11-era triggers; replaced below with the gated
293
344
  -- versions. Idempotent: SQLite no-ops when the trigger does not exist, so
294
345
  -- this is safe to run on every boot and on fresh databases.
346
+ --
347
+ -- 2026-05-23 \u2014 added IF NOT EXISTS guards on the CREATE side too. Without
348
+ -- them, any failure between the DROPs and the CREATEs (e.g. a partially
349
+ -- applied prior boot, or a future statement inserted between them) left
350
+ -- the live DB with the gated triggers but the next SCHEMA_SQL re-exec
351
+ -- still threw "trigger messages_vec_ai already exists". That threw all the
352
+ -- way out of getDb(), which caused syncSemanticEnabledToDb to silently
353
+ -- 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.
295
357
  DROP TRIGGER IF EXISTS messages_vec_ai;
296
358
  DROP TRIGGER IF EXISTS messages_vec_ad;
297
359
  DROP TRIGGER IF EXISTS messages_vec_au;
298
360
 
299
- CREATE TRIGGER messages_vec_ai AFTER INSERT ON messages
361
+ CREATE TRIGGER IF NOT EXISTS messages_vec_ai AFTER INSERT ON messages
300
362
  WHEN new.is_sidechain = 0
301
363
  AND new.content_text IS NOT NULL
302
364
  AND (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
@@ -305,14 +367,14 @@ BEGIN
305
367
  VALUES (new.session_id, new.uuid, 'embed', datetime('now'));
306
368
  END;
307
369
 
308
- CREATE TRIGGER messages_vec_ad AFTER DELETE ON messages
370
+ CREATE TRIGGER IF NOT EXISTS messages_vec_ad AFTER DELETE ON messages
309
371
  WHEN (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
310
372
  BEGIN
311
373
  INSERT INTO chunk_queue(session_id, message_uuid, action, enqueued_at)
312
374
  VALUES (old.session_id, old.uuid, 'delete', datetime('now'));
313
375
  END;
314
376
 
315
- CREATE TRIGGER messages_vec_au AFTER UPDATE OF content_text ON messages
377
+ CREATE TRIGGER IF NOT EXISTS messages_vec_au AFTER UPDATE OF content_text ON messages
316
378
  WHEN new.is_sidechain = 0
317
379
  AND (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
318
380
  BEGIN
@@ -668,8 +730,8 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_target
668
730
  ON bug_synthesis_results(scope, target_id, created_at DESC);
669
731
  CREATE INDEX IF NOT EXISTS idx_synth_results_created
670
732
  ON bug_synthesis_results(created_at DESC);
671
- `});import*as ns from"sqlite-vec";function m(){if(C)return C;k(),C=new qt(ge),ns.load(C),C.pragma("cache_size = -64000"),C.pragma("mmap_size = 268435456"),C.pragma("temp_store = MEMORY"),C.pragma("busy_timeout = 5000"),C.pragma("journal_size_limit = 67108864"),C.pragma("wal_autocheckpoint = 1000"),C.exec(es),ts(C);try{C.exec("PRAGMA optimize")}catch{}return C}function rs(){if(C){try{C.exec("PRAGMA optimize")}catch{}try{C.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{C.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{C.pragma("wal_checkpoint(TRUNCATE)")}catch{}C.close(),C=null}}var C,N=b(()=>{"use strict";Zt();D();ss();C=null});function we(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),s=Math.floor(e%86400/3600);return s>0?`${t}d ${s}h`:`${t}d`}function Re(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var Ne=b(()=>{"use strict"});import{writeFileSync as oi}from"node:fs";import{join as ai}from"node:path";function se(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Le(e,t){let s=se(t);if(!s)throw new Error("tag must contain at least one alphanumeric character");let n=m(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?{tag:s,added:!1}:(n.transaction(()=>{n.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,s,r),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,s,r)})(),ls(),{tag:s,added:!0})}function as(e,t){let s=se(t);if(!s)return{tag:"",removed:!1};let n=m(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?(n.transaction(()=>{n.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,s),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,s,r)})(),ls(),{tag:s,removed:!0}):{tag:s,removed:!1}}function Ae(e){return m().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function cs(){return m().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
672
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function ls(){try{k();let e=m(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),s=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),n={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:s};oi(ci,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var ci,Oe=b(()=>{"use strict";N();D();ci=ai(S,"tags.json")});function li(e,t){let s=e.filter(i=>i.content_text&&i.content_text.trim().length>0);if(s.length<=t)return s;let n=new Set;n.add(0),n.add(s.length-1);let r=(s.length-2)/Math.max(1,t-2);for(let i=1;i<t-1;i++)n.add(Math.floor(i*r));return Array.from(n).sort((i,o)=>i-o).slice(0,t).map(i=>s[i])}function xe(e){let t=m(),s={limit:e.limit??500},n=e.sessionIds&&e.sessionIds.length>0,r=n?"1=1":"s.message_count > 2";if(n){let o=e.sessionIds.map((a,c)=>`@sid_${c}`).join(", ");r+=` AND s.id IN (${o})`,e.sessionIds.forEach((a,c)=>{s[`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",s.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",s.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
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,
673
735
  NULLIF(sa.alias, '') AS alias,
674
736
  COALESCE(s.first_user_message, '') AS first_user_message
675
737
  FROM sessions s
@@ -677,38 +739,38 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
677
739
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
678
740
  WHERE ${r}
679
741
  ORDER BY COALESCE(s.started_at, '') DESC
680
- LIMIT @limit`).all(s).map(o=>{let a=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
742
+ LIMIT @limit`).all(n).map(a=>{let o=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
681
743
  FROM messages WHERE session_id = ?
682
- ORDER BY COALESCE(timestamp, ''), rowid`).all(o.id),d=li(a,5).map(l=>`${l.role}: ${l.content_text.slice(0,400)}`).join(`
744
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),l=Fo(o,5).map(d=>`${d.role}: ${d.content_text.slice(0,400)}`).join(`
683
745
  ---
684
- `);return{id:o.id,project:o.project,git_branch:o.git_branch,alias:o.alias,first_user_message:o.first_user_message,message_sample:d,current_tags:Ae(o.id)}})}var et=b(()=>{"use strict";N();Oe()});import{z as W}from"zod";function nt(e){let t=e.minTags??2,s=e.maxTags??4,n=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],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.`)):(n&&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}-${s} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
685
- `)}function Ti(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(`
686
- `)}function bi(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(`
687
- `)}function wi(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(`
688
- `)}var hi,Ei,Si,yi,Ri,Ni,Li,Ai,us,rt=b(()=>{"use strict";hi={project:W.string().optional().describe("Exact project name match (optional)."),collectionId:W.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:W.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:W.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:W.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:W.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:W.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};Ei={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:W.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};Si={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};yi={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:W.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};Ri={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:hi,build:nt,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Ni={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:Ei,build:Ti,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Li={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:Si,build:bi,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Ai={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:yi,build:wi,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},us=[Ri,Ni,Li,Ai]});import{writeFileSync as Oi}from"node:fs";import{join as xi}from"node:path";function ps(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function ki(){return m().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:ps(t.previous_aliases)}))}function Ie(e,t){let s=t.trim();if(!s)throw new Error("alias must be non-empty");let n=m(),r=new Date().toISOString(),i=n.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),o=[];return i&&(o=ps(i.previous_aliases),i.alias!==s&&o.push({alias:i.alias,replaced_at:r})),n.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
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)
689
751
  VALUES (?, ?, ?, ?)
690
752
  ON CONFLICT(session_id) DO UPDATE SET
691
753
  alias = excluded.alias,
692
754
  updated_at = excluded.updated_at,
693
- previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(o)),Ci(),{session_id:e,alias:s,updated_at:r,previous_aliases:o}}function Ci(){try{k();let e=ki(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};Oi(Ii,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var Ii,ke=b(()=>{"use strict";N();D();Ii=xi(S,"aliases.json")});import{randomUUID as Hi}from"node:crypto";import{writeFileSync as Bi,readFileSync as Td,existsSync as Sd}from"node:fs";import{join as Wi}from"node:path";function Gi(e){return{...e}}function at(e,t,s,n=null,r=new Date().toISOString()){m().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
694
- VALUES (?, ?, ?, ?, ?)`).run(e,n,t,s?JSON.stringify(s):null,r)}function Yi(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function zi(e){if(!e)return 0;let t=0,s=e,n=new Set,r=m();for(;s;){if(n.has(s))throw new Error("collection cycle detected");n.add(s);let i=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!i)break;t+=1,s=i.parent_id}return t}function hs(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Gi(t):null}function ct(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let s=m(),n=new Date().toISOString(),r=Hi();if(e.parent_id){if(!hs(e.parent_id))throw new Error("parent collection not found");if(zi(e.parent_id)>=fs-1)throw new Error(`max collection depth is ${fs}`)}return s.transaction(()=>{s.prepare(`INSERT INTO collections
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
695
757
  (id, name, description, icon, color, parent_id, sort_key, created_at, updated_at, archived_at)
696
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",n,n),at(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),dt(),hs(r)}function lt(e,t,s=null,n={}){let r=m();if(Yi(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=n.source??"manual",c=n.rule_id??null;if(a==="auto"&&!c)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
697
- VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,s,a,c),at(e,"add",{note:s,source:a,rule_id:c},t,d)})(),dt(),{added:!0}}function Es(e,t){let s=m();if(!s.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return s.transaction(()=>{s.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),at(e,"remove",null,t,r)})(),dt(),{removed:!0}}function Ji(){return m().prepare(`SELECT id, collection_id, session_id, action, payload, 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
698
760
  FROM collection_events
699
- ORDER BY at ASC, id ASC`).all()}function dt(){try{k();let e=m(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
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,
700
762
  created_at, updated_at, archived_at
701
763
  FROM collections
702
- ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),s=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
764
+ ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),n=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
703
765
  FROM collection_sessions
704
- ORDER BY collection_id, added_at`).all(),n=Ji(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};Bi(Xi,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Xi,fs,ut=b(()=>{"use strict";N();D();Xi=Wi(S,"collections.json"),fs=8});function Fs(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let s=t.split(`
705
- `)[0].trim();return s.length>Ms?s.slice(0,Ms)+"\u2026":s}function so(e){return m().prepare(`SELECT s.id AS id,
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,
706
768
  sa.alias AS alias,
707
769
  s.auto_title AS auto_title,
708
770
  s.first_user_message AS first_user_message
709
771
  FROM sessions s
710
772
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
711
- WHERE s.id = ?`).get(e)??null}function no(e){let t=so(e);return t?Fs(t):e.slice(0,8)}function Ps(e){if(!e)return null;let t=m(),s=t.prepare(`SELECT e.thread_id AS thread_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,
712
774
  t.name AS thread_name,
713
775
  e.parent_session_id AS parent_session_id,
714
776
  e.added_at AS added_at
@@ -717,7 +779,7 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
717
779
  WHERE e.session_id = ?
718
780
  AND t.archived = 0
719
781
  ORDER BY e.added_at DESC
720
- LIMIT 1`).get(e);if(!s)return null;let n=s.parent_session_id?{id:s.parent_session_id,title:no(s.parent_session_id)}:null,r=s.parent_session_id?[e,s.parent_session_id]:[e],i=r.map(()=>"?").join(", "),a=t.prepare(`SELECT e.session_id AS session_id,
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,
721
783
  s.id AS id,
722
784
  sa.alias AS alias,
723
785
  s.auto_title AS auto_title,
@@ -727,41 +789,46 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
727
789
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
728
790
  WHERE e.thread_id = ?
729
791
  AND e.session_id NOT IN (${i})
730
- ORDER BY e.added_at ASC`).all(s.thread_id,...r).map(c=>({id:c.session_id,title:c.id?Fs(c):c.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:a}}var Ms,Us=b(()=>{"use strict";N();Ms=80});var Et=b(()=>{"use strict"});var $s=b(()=>{"use strict"});import{writeFileSync as ro,mkdirSync as io,existsSync as oo}from"node:fs";import{join as js}from"node:path";function Hs(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>!!s&&typeof s=="object"&&typeof s.title=="string"&&typeof s.replaced_at=="string")}catch{}return[]}function Bs(e){let t=m(),s=t.prepare(`SELECT rowid AS rid, content_text
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
731
793
  FROM messages
732
794
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
733
795
  AND content_text IS NOT NULL AND content_text != ''
734
796
  ORDER BY COALESCE(timestamp, ''), rowid ASC
735
- LIMIT ?`).all(e,fe),n=t.prepare(`SELECT rowid AS rid, content_text
797
+ LIMIT ?`).all(e,Re),s=t.prepare(`SELECT rowid AS rid, content_text
736
798
  FROM messages
737
799
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
738
800
  AND content_text IS NOT NULL AND content_text != ''
739
801
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
740
- LIMIT ?`).all(e,Fe),r=new Map;for(let l of s)r.set(l.rid,l.content_text);for(let l of n)r.set(l.rid,l.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let i=Array.from(r.entries()).sort((l,u)=>l[0]-u[0]).map(([,l])=>({content_text:l})),o=s.length===fe&&n.length===Fe&&r.size===fe+Fe,a=i.map((l,u)=>{let p=(l.content_text??"").slice(0,ao);return o&&u===fe?`--- (middle of session omitted) ---
741
- ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
742
- `),c=null;try{c=Ps(e)}catch(l){console.error("[autoTitle] thread context resolution failed:",l),c=null}let d=[];return c&&(d.push(co(c)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${fe}`,`messages (initial intent) and the last ${Fe} 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),d.join(`
743
- `)}function co(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let s=e.siblings.length;if(s>0){let r=e.siblings.slice(0,Tt).map(o=>`"${o.title}"`).join(", "),i=s>Tt?`, and ${s-Tt} more`:"";t.push(`- Sibling sessions (${s}): ${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(`
744
- `)}function bt(e,t,s){let n=t.trim();if(!n)return;let r=m(),i=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
745
- FROM sessions WHERE id = ?`).get(e);if(!i||s==="heuristic"&&i.auto_title_source==="agent"&&i.auto_title||i.auto_title===n&&i.auto_title_source===s)return;let o=Hs(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
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) ---
803
+ ${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
746
808
  SET auto_title = ?,
747
809
  auto_title_source = ?,
748
810
  auto_title_generated_at = ?,
749
811
  auto_title_history = ?
750
- WHERE id = ?`).run(n,s,Date.now(),JSON.stringify(o),e),uo(e,n,s,a)}function Ws(e){let t=m().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
751
- 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:Hs(t.auto_title_history)}:null}function lo(){k(),oo(St)||io(St,{recursive:!0})}function uo(e,t,s,n){try{lo();let r=js(St,`${e}.txt`),i=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
752
- `;ro(r,i+t+`
753
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var St,fe,Fe,ao,Tt,yt=b(()=>{"use strict";N();D();Us();Et();$s();St=js(S,"titles"),fe=5,Fe=15,ao=500;Tt=5});function Xs(e,t){let s=po.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}var po,Gs=b(()=>{"use strict";po=new Map});import{existsSync as mo,statSync as go}from"node:fs";import{delimiter as _o,join as fo}from"node:path";function Ys(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(_o).filter(Boolean),s=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let n of t)for(let r of s){let i=fo(n,r);try{if(mo(i)&&go(i).isFile())return i}catch{}}return null}function zs(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var Js=b(()=>{"use strict"});var en={};Ke(en,{_resetClaudePathCacheForTests:()=>So,buildScanPrompt:()=>qs,isClaudeCliAvailable:()=>Vs,runClaudeCliScan:()=>No,spawnClaudePrompt:()=>Zs});import{spawn as ho}from"node:child_process";function Ks(){if(ae!==void 0&&he!==void 0)return{path:ae,available:he};let e=Ys("claude");return ae=e??"claude",he=e!==null,{path:ae,available:he}}function To(){return Ks().path}function Vs(){return Ks().available}function So(){ae=void 0,he=void 0}function qs(e){return nt({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 bo(e,t){let s=t.get(e);return s||e.slice(0,8)}function yo(e){try{return xe(e).map(s=>({id:s.id,label:s.alias&&s.alias.trim().length>0?s.alias:s.first_user_message&&s.first_user_message.trim().length>0?s.first_user_message.slice(0,60):s.id.slice(0,8)}))}catch{return[]}}function wo(e){let{scanId:t,total:s,labelTable:n}=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 d of c.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let l=d.input,u=typeof l?.sessionId=="string"?l.sessionId:null;!u||r.has(u)||(r.add(u),Xs(t,{type:"progress",current:r.size,total:s,sessionId:u,sessionLabel:bo(u,n)}))}}}function Ro(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
754
- `);for(;n!==-1;){let r=t.slice(0,n);t=t.slice(n+1),r.length>0&&e(r),n=t.indexOf(`
755
- `)}}}async function No(e,t={},s){let n=!!t.scanId,r=n?yo(e):[],i=new Map(r.map(c=>[c.id,c.label])),o=r.length,a;return n&&t.scanId&&(a=wo({scanId:t.scanId,total:o,labelTable:i})),Qs({prompt:qs(e),allowedTools:Eo.split(","),opts:t,onProgress:s,onStdoutLine:a,outputFormat:n?"stream-json":"json"})}async function Zs(e,t,s={},n){return Qs({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function Qs(e){let{prompt:t,allowedTools:s,opts:n,onProgress:r,onStdoutLine:i,outputFormat:o}=e,a=["-p",t,"--output-format",o,"--allowedTools",s.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return o==="stream-json"&&a.push("--verbose"),n.model&&a.push("--model",n.model),new Promise(c=>{let d=To(),l=ho(d,a,{stdio:["ignore","pipe","pipe"],shell:zs(d)||process.platform==="win32"&&ae==="claude"}),u=[],p=[],g=i?Ro(i):void 0;l.stdout.on("data",h=>{u.push(h),g&&g(h)}),l.stderr.on("data",h=>{if(p.push(h),r){let E=h.toString("utf8").trim();E&&r(E)}});let _=setTimeout(()=>{l.kill("SIGKILL")},1800*1e3);l.on("close",h=>{clearTimeout(_),c({success:h===0,stdout:Buffer.concat(u).toString("utf8"),stderr:Buffer.concat(p).toString("utf8"),exitCode:h})}),l.on("error",h=>{clearTimeout(_),c({success:!1,stdout:"",stderr:String(h),exitCode:null})})})}var Eo,ae,he,wt=b(()=>{"use strict";et();rt();Gs();Js();Eo=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});import J from"chalk";import{formatDistanceToNowStrict as Sm,parseISO as bm}from"date-fns";var f,Ut=b(()=>{"use strict";f={dim:J.gray,bold:J.bold,project:J.cyan,user:J.blue,assistant:J.green,tool:J.magenta,warn:J.yellow,err:J.red,ok:J.green,accent:J.hex("#f97316")}});var nr=b(()=>{"use strict"});import{existsSync as bc,readFileSync as yc,writeFileSync as wc}from"node:fs";import{join as Rc}from"node:path";import{z as j}from"zod";function Ht(e){let t=e.trim();return!!(!t||ir.test(t)||or.test(t))}function ar(e){let t=e.trim();if(!t||or.test(t))return null;let s=t.replace(ir,"").trim();return s.length>0?s:null}function jt(e,t){if(!Ht(e))return e;let s=ar(e);return s||(t&&!Ht(t)?t:e)}function xc(e){let t=e.now-e.withinMs,s=e.pending.filter(n=>{let r=Date.parse(n.started_at);return Number.isFinite(r)&&r>=t});if(s.length===0)return{kind:"none"};if(e.shellPid!=null){let n=s.find(r=>r.shell_pid===e.shellPid);if(n)return{kind:"pid-match",entry:n}}if(e.cwd){let n=e.cwd.replace(/\/+$/,""),r=s.filter(i=>i.cwd&&i.cwd.replace(/\/+$/,"")===n);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var $t,rr,Nc,Lc,Ac,Oc,ir,or,Bt,Ic,cr=b(()=>{"use strict";D();$t=Rc(S,"terminals.json"),rr=1440*60*1e3,Nc=3e4,Lc=6e4,Ac=j.object({shell_pid:j.number(),tab_name:j.string(),cwd:j.string().nullable().optional(),opened_at:j.string(),last_seen_at:j.string()}),Oc=j.object({schema:j.string().optional(),saved_at:j.string().optional(),terminals:j.array(Ac).max(500).default([]),sessions_by_pid:j.record(j.string(),j.array(j.string()).max(50)).optional().default({})}),ir=/^[⠀-⣿✳\s]+/,or=/^\d+(\.\d+){1,3}$/;Bt=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,!!bc($t)))try{let t=yc($t,"utf8"),s=JSON.parse(t),n=Oc.safeParse(s);if(!n.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",n.error.issues);return}let r=n.data;for(let 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 d of o)d.length>0&&c.add(d);c.size>0&&this.sessionsByPid.set(a,c)}this.gc()}catch{}}save(){try{k();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([s,n])=>[String(s),Array.from(n)]))};wc($t,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=jt(t.tab_name,n?.tab_name),i=n?.opened_at??t.opened_at,o={...t,tab_name:r,opened_at:i,last_seen_at:s};return this.entries.set(t.shell_pid,o),this.gc(),this.save(),o}rename(t,s){this.ensureLoaded();let n=this.entries.get(t);if(!n)return null;let r=jt(s,n.tab_name),i={...n,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,i),this.save(),i}remove(t){this.ensureLoaded();let s=this.entries.delete(t),n=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(s||n)&&this.save(),s}claimPidOwnership(t,s,n=Date.now()){if(this.ensureLoaded(),!s)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===s?(r.last_claim_at=n,"refreshed"):n-r.last_claim_at>Lc?(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,s])=>({shell_pid:t,instance_id:s.instance_id,last_claim_at:s.last_claim_at}))}sync(t){this.ensureLoaded();let s=new Date().toISOString(),n=0,r=0;for(let i of t){let o=this.entries.get(i.shell_pid),a=jt(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:s}),o?(o.tab_name!==a||o.cwd!==i.cwd)&&r++:n++}return this.gc(),this.save(),{added:n,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,s){this.ensureLoaded();let n=this.sessionsByPid.get(s);n||(n=new Set,this.sessionsByPid.set(s,n)),n.has(t)||(n.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let s of this.sessionsByPid.values())if(s.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let s=!1;for(let[n,r]of this.sessionsByPid)r.delete(t)&&(s=!0,r.size===0&&this.sessionsByPid.delete(n));return s&&this.save(),s}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let s=xc({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(s.kind==="pid-match"||s.kind==="singleton-cwd"){let n=this.pendingClaudeStarts.indexOf(s.entry);n>=0&&this.pendingClaudeStarts.splice(n,1)}return s}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,s,n,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:s,queued_at:Date.now(),cwd:n,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,s])=>({session_id:t,...s}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[s,n]of this.deferredLinks)n.queued_at<t&&this.deferredLinks.delete(s)}gcPending(){let t=Date.now()-Nc;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(s=>{let n=Date.parse(s.started_at);return Number.isFinite(n)&&n>=t}))}outputTails=new Map;setOutputTail(t,s,n){this.ensureLoaded(),this.outputTails.set(t,{text:s,captured_at:n})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,s){this.ensureLoaded(),this.origins.set(t,s),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-rr;for(let[s,n]of this.entries){let r=Date.parse(n.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(s)}}gcOrigins(){let t=Date.now()-rr;for(let[s,n]of this.origins)n.detectedAt<t&&this.origins.delete(s)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let n=Date.now()-t,r=0,i=0;for(let[o,a]of this.sessionsByPid){let c=this.entries.get(o);if(c){let d=Date.parse(c.last_seen_at);if(Number.isFinite(d)&&d>=n)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,s=0;for(let n of[...this.entries.keys()]){let r=!0;try{process.kill(n,0)}catch(o){o.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(n),t++;let i=this.sessionsByPid.get(n);i&&(s+=i.size,this.sessionsByPid.delete(n)),this.outputTails.delete(n),this.pidOwnership.delete(n)}return(t||s)&&this.save(),{pruned_pids:t,pruned_sessions:s}}},Ic=new Bt});import{execFile as kc}from"node:child_process";import{promisify as Cc}from"node:util";var km,lr=b(()=>{"use strict";km=Cc(kc)});import{execFile as Dc}from"node:child_process";import{promisify as Mc}from"node:util";var jm,Hm,dr=b(()=>{"use strict";cr();ke();N();lr();jm=Mc(Dc),Hm=3600*1e3});var ur=b(()=>{"use strict"});import{z as K}from"zod";var Ym,pr=b(()=>{"use strict";N();Ym=K.object({enabled:K.boolean().default(!1),model:K.string().optional(),ratePerMinute:K.number().int().min(1).max(600).default(30),lastProcessedSessionId:K.string().nullable().default(null),backfillPaused:K.boolean().default(!1),autoExtractEnabled:K.boolean().default(!1),autoExtractIntervalMinutes:K.number().int().min(5).max(720).default(60),autoExtractBatchSize:K.number().int().min(1).max(20).default(1),autoResumeWorker:K.boolean().default(!1)})});var mr=b(()=>{"use strict";N();wt();pr()});var gr=b(()=>{"use strict"});import{execFile as Fc}from"node:child_process";import{promisify as Pc}from"node:util";var rg,_r=b(()=>{"use strict";N();rg=Pc(Fc)});import{z as Wt}from"zod";var ag,fr=b(()=>{"use strict";ag=Wt.object({heuristicEnabled:Wt.boolean().default(!0),agentEnabled:Wt.boolean().default(!1)})});import{basename as ug,join as Xt}from"node:path";var hr,_g,fg,Er=b(()=>{"use strict";N();D();ut();hr=Xt(S,"auto-rules"),_g=Xt(hr,"rules.json"),fg=Xt(hr,"suggestions.json")});var Tr=b(()=>{"use strict"});var Sr=b(()=>{"use strict"});import{watch as Dg}from"chokidar";import{readdirSync as Uc,statSync as Fg}from"node:fs";import{basename as Hg,join as $c}from"node:path";function br(e){let t=e.split(/[/\\]/),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}function yr(e){return e.replace(/\\/g,"/").includes("/subagents/")}function*Gt(e){let t;try{t=Uc(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let s of t){if(s.isSymbolicLink())continue;let n=$c(e,s.name);s.isDirectory()?yield*Gt(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}var wr=b(()=>{"use strict";D();N();nr();dr();ur();mr();gr();_r();yt();fr();Er();Et();Tr();ke();Sr()});import{execFileSync as Nr}from"node:child_process";function Yt(e={}){let t=e.psOutput??jc(),s=e.isProcessAlive??Hc,n=e.getParentCommand??Bc,r=[];for(let i of t.split(`
756
- `)){let o=i.trim();if(!o||!o.includes("dist/mcp/server.js")||Wc(o))continue;let a=o.split(/\s+/);if(a.length<5)continue;let c=Number(a[0]),d=Number(a[1]),l=a[2],u=Number(a[3]);if(!Number.isFinite(c)||!Number.isFinite(d))continue;let p=d>1&&s(d);r.push({pid:c,ppid:d,parentAlive:p,etimeSeconds:Xc(l),pcpu:Number.isFinite(u)?u:0,orphan:!p,parentCommand:p?n(d):null})}return r}function jc(){try{return Nr("ps",["-eo","pid,ppid,etime,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 -eo failed: ${t}
757
- `),""}}function Hc(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function Bc(e){if(!Number.isFinite(e)||e<=1)return null;try{let s=Nr("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return s.length?s.slice(0,200):null}catch{return null}}function Wc(e){let t=e.split(/\s+/);if(t.length<5)return!1;let s=t[4]??"";return s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"}function Xc(e){if(!e)return 0;let t=0,s=e,n=e.indexOf("-");n>=0&&(t=Rr(e.slice(0,n)),s=e.slice(n+1));let r=s.split(":").map(Rr),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 Rr(e){let t=Number(e);return Number.isFinite(t)?t:0}function Ye(e){return e.pcpu>=Gc&&e.etimeSeconds>=Yc}var Gc,Yc,zt=b(()=>{"use strict";Gc=50,Yc=60});var i_,Lr,Ar=b(()=>{"use strict";Ne();zt();i_=5*6e4,Lr=1073741824});var jr={};Ke(jr,{buildHealthReport:()=>Ur,buildPipelineDiagnostic:()=>Fr,checkIngestStaleness:()=>$r,detectLabelCollisions:()=>Pr,getFreeDiskBytes:()=>rl,runDoctor:()=>nl});import{existsSync as Ir,readFileSync as zc,statSync as Jt,statfsSync as kr}from"node:fs";import{join as Cr}from"node:path";import*as vr from"node:http";function Vc(e){let t=[];for(let s of e)if(s.alias){if(Kc.test(s.alias)){t.push({session_id:s.session_id,alias:s.alias,violation:"fabricated-origin-label",cwd:s.cwd});continue}if(s.cwd){let n=s.cwd.replace(/\/+$/,"").split("/").pop();n&&s.alias.startsWith(`${n} \xB7 `)&&t.push({session_id:s.session_id,alias:s.alias,violation:"fabricated-cwd-branch",cwd:s.cwd})}}return t}function qc(){let e=Cr(S,"daemon.port");if(!Ir(e))return null;try{let t=zc(e,"utf8").trim();if(t.startsWith("{")){let n=JSON.parse(t);return typeof n.port=="number"?n.port:null}let s=Number.parseInt(t,10);return Number.isFinite(s)&&s>0&&s<65536?s:null}catch{return null}}function Zc(e,t,s=1500){return new Promise(n=>{let r=vr.request({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:s,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){n(null);return}try{n(JSON.parse(Buffer.concat(o).toString("utf8")))}catch{n(null)}})});r.on("error",()=>n(null)),r.on("timeout",()=>{r.destroy(),n(null)}),r.end()})}function Qc(){let e=Cr(S,"terminals.json");if(!Ir(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=Jt(e),s=Math.floor((Date.now()-t.mtimeMs)/1e3);return{exists:!0,mtimeMs:t.mtimeMs,ageSeconds:s}}catch{return{exists:!1,mtimeMs:null,ageSeconds:null}}}function el(){let e=new Date(Date.now()-Kt*36e5).toISOString();try{let t=m().prepare(`SELECT
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(`
816
+ `);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
758
825
  COUNT(*) AS total,
759
826
  SUM(CASE WHEN sa.alias IS NULL OR sa.alias = '' THEN 1 ELSE 0 END) AS without_alias,
760
827
  SUM(CASE WHEN (sa.alias IS NULL OR sa.alias = '')
761
828
  AND s.auto_title_source = 'heuristic' THEN 1 ELSE 0 END) AS heuristic_only
762
829
  FROM sessions s
763
830
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
764
- WHERE s.started_at >= ?`).get(e),s=t.total??0,n=t.without_alias??0,r=t.heuristic_only??0,i=s>0?r/s:0;return{total:s,withoutAlias:n,heuristicOnly:r,fractionHeuristic:i}}catch{return{total:0,withoutAlias:0,heuristicOnly:0,fractionHeuristic:0}}}async function Fr(){let e=qc(),t=Qc(),s=el(),n=!1,r=null,i=null,o=null,a=null,c=null;if(e){let u=await Zc(e,"/api/health");if(u){n=!0,r=typeof u.uptimeSeconds=="number"?u.uptimeSeconds:null,i=typeof u.version=="string"?u.version:null,o=typeof u.pipeline?.silentTerminalRejections=="number"?u.pipeline.silentTerminalRejections:null,a=u.pipeline?.lastTerminalSyncAt??null;let p=u.pipeline?.autoExtract;p&&(c={circuitBroken:p.circuitBroken===!0,reason:p.reason??null,brokenAt:typeof p.brokenAt=="number"?p.brokenAt:null,consecutiveZeroTokenRuns:typeof p.consecutiveZeroTokenRuns=="number"?p.consecutiveZeroTokenRuns:0})}}let d=[];if(n||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").`),n&&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 u=Date.now()-Date.parse(a);Number.isFinite(u)&&u>Dr&&d.push(`Last successful /api/terminal/sync was ${Math.round(u/6e4)} min ago \u2014 extension may have crashed, been disabled, or is failing auth. Run \`recall doctor\` again after restarting the extension host.`)}return!n&&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.`),s.total>=3&&s.fractionHeuristic>=Mr&&d.push(`${s.heuristicOnly}/${s.total} sessions in the last ${Kt}h (${Math.round(s.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.`),{state:n?d.length>0?"degraded":"ok":"down",flags:d,daemon:{running:n,port:e,uptimeSeconds:r,version:i},runtime:{silentTerminalRejections:o,lastTerminalSyncAt:a,autoExtract:c},terminalsJson:t,recentSessions:s}}function V(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 Or(e){try{return Jt(e).size}catch{return 0}}function xr(e){try{return m().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function Pr(){try{return m().prepare(`SELECT p.name AS project,
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,
765
832
  COALESCE(NULLIF(sa.alias, ''), s.auto_title, substr(s.first_user_message, 1, 60)) AS label,
766
833
  COUNT(*) AS count,
767
834
  MAX(CASE WHEN sa.alias IS NOT NULL AND sa.alias != '' THEN 1 ELSE 0 END) AS any_aliased
@@ -773,45 +840,54 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
773
840
  GROUP BY p.name, label
774
841
  HAVING count >= 2
775
842
  ORDER BY count DESC, label ASC
776
- 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 Ur(e){let t=m(),s=t.pragma("page_size",{simple:!0})||4096,n=t.pragma("page_count",{simple:!0})||0,r=t.pragma("freelist_count",{simple:!0})||0;e?.("Checking database integrity");let i="ok";try{let O=t.pragma("quick_check").map(q=>q.quick_check);i=O.length===1&&O[0]==="ok"?"ok":O.join("; ")}catch(R){i=`check failed: ${R.message}`}let o=Or(ge),a=Or(`${ge}-wal`),c=0,d=0;try{let R=kr(S);c=Number(R.bavail)*Number(R.bsize),d=Number(R.blocks)*Number(R.bsize)}catch{}e?.("Counting rows");let l=t.prepare(`SELECT
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
777
844
  (SELECT COUNT(*) FROM projects) AS projects,
778
845
  (SELECT COUNT(*) FROM sessions) AS sessions,
779
846
  (SELECT COUNT(*) FROM messages) AS messages,
780
- (SELECT COUNT(*) FROM message_usage) AS message_usage`).get(),u=0;try{u=t.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get().n}catch{}e?.("Measuring FTS fragmentation");let p=[];c>0&&c<1*1024**3&&p.push(`Disk free is ${V(c)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`);let g=100*1024**2,_=a>=Lr?"error":a>=g?"warn":"ok";_==="error"?p.push(`WAL is ${V(a)} \u2014 readers are pinning the checkpoint frontier. Run \`recall mcp-prune\` to release stuck MCP children, then \`recall optimize\` to truncate.`):_==="warn"&&p.push(`WAL is ${V(a)} \u2014 run \`recall optimize\` to truncate it.`);let h=Yt(),E=h.filter(R=>R.orphan);E.length>0&&p.push(`${E.length} orphaned MCP child${E.length===1?"":"ren"} (pid${E.length===1?"":"s"}: ${E.map(R=>R.pid).join(", ")}). Each holds a SQLite read connection. Run \`recall mcp-prune\` to clean up.`);let T=h.filter(Ye);T.length>0&&p.push(`${T.length} MCP child${T.length===1?"":"ren"} burning CPU (pid${T.length===1?"":"s"}: ${T.map(R=>R.pid).join(", ")}). Likely runaway vec0 kNN. Run \`recall stop --all\` or \`recall mcp-prune --all\`.`),r>n*.2&&n>1e3&&p.push(`${r.toLocaleString()} free pages (${(r/n*100).toFixed(0)}% of file) \u2014 \`recall optimize --vacuum\` will reclaim them.`);let w=xr("messages_fts"),v=xr("sessions_fts");w>16&&p.push(`messages_fts has ${w} segments \u2014 \`recall optimize\` will merge them.`);let L=0;try{L=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let P=!1;try{P=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!P&&L>0?p.push(`${L.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):L>1e5&&p.push(`chunk_queue has ${L.toLocaleString()} pending rows \u2014 embedder is behind. \`recall semantic backfill\` to drain manually.`),{db:{sizeBytes:o,walSizeBytes:a,pageCount:n,pageSize:s,freelistCount:r,freelistBytes:r*s,integrity:i},disk:{freeBytes:c,totalBytes:d},fts:{messages:{fragments:w},sessions:{fragments:v}},vectors:{rows:u},rows:{projects:l.projects,sessions:l.sessions,messages:l.messages,messageUsage:l.message_usage},chunkQueue:{size:L,semanticEnabled:P},wal:{sizeBytes:a,level:_},mcpProcesses:h,warnings:p}}function tl(e){if(!e)return{stage:()=>{},done:()=>{}};let t=!!process.stderr.isTTY,s="",n=0,r=()=>{if(!s)return;let i=Date.now()-n,o=i<1e3?`${i}ms`:`${(i/1e3).toFixed(1)}s`;t?process.stderr.write(`\r\x1B[2K ${f.ok("\u2713")} ${s} ${f.dim(`(${o})`)}
781
- `):process.stderr.write(` \u2713 ${s} (${o})
782
- `),s=""};return{stage(i){r(),s=i,n=Date.now(),t?process.stderr.write(` ${f.dim("\u2026")} ${s}`):process.stderr.write(` \u2026 ${s}
783
- `)},done:r}}function sl(e){if(console.log(f.dim("\u2014 MCP processes \u2014")),e.length===0){console.log(f.ok(" \u2713 no MCP children running"));return}let t=e.filter(i=>i.orphan),s=e.reduce((i,o)=>Math.max(i,o.etimeSeconds),0),n=e.length-t.length;if(console.log(` ${e.length} total, ${n} with live parent, ${t.length===0?f.ok("0 orphaned"):f.err(`${t.length} orphaned`)} (oldest ${we(s)})`),t.length>0){console.log(f.dim(" Orphans hold a SQLite read connection and can stall WAL checkpoints. Run `recall mcp-prune` to kill them."));for(let i of t.slice(0,5))console.log(` ${f.dim("\u2022")} pid ${i.pid} age ${we(i.etimeSeconds)} ppid=${i.ppid} (gone)`)}let r=e.filter(Ye);if(r.length>0){console.log(f.warn(` ! ${r.length} MCP child${r.length===1?"":"ren"} burning CPU (likely runaway vec0 kNN -- see 2026-05-15 incident).`));for(let i of r){let o=i.parentCommand?`parent ${i.parentCommand}`:`ppid ${i.ppid}`;console.log(f.dim(` pid ${i.pid} ${i.pcpu.toFixed(0)}%cpu ${we(i.etimeSeconds)} elapsed (${o})`))}console.log(f.dim(" Run `recall stop --all` for one-command recovery, or `recall mcp-prune --all` to keep the daemon alive."))}}function $r(){let e=m(),t=e.prepare("SELECT encoded_path FROM projects").all(),s=new Set(t.map(d=>d.encoded_path));if(s.size===0)return{status:"ok",staleCount:0,scanned:0,sampleFiles:[],message:"Ingest freshness: ok (no projects indexed yet \u2014 nothing to monitor)."};let n=e.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1"),r=0,i=0,o=[];for(let d of Gt(Qe)){if(yr(d))continue;let l=br(d);if(!l||!s.has(l))continue;r+=1;let u;try{u=Jt(d).mtimeMs}catch{continue}let p=n.get(d);(!p||p.file_mtime<u)&&(i+=1,o.length<5&&o.push(d))}if(i===0)return{status:"ok",staleCount:0,scanned:r,sampleFiles:[],message:`Ingest freshness: ok (scanned ${r} JSONL${r===1?"":"s"} in ${s.size} known project${s.size===1?"":"s"}, none newer than the index).`};let a=i>10?"fail":"warn",c=o.slice(0,3).map(d=>d.split(/[/\\]/).pop()??d).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.`}}async function nl(e={}){let t=tl(!e.json);t.stage("Scanning session aliases");let s=m().prepare(`SELECT sa.session_id AS session_id, sa.alias AS alias, s.cwd AS cwd
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+=`
848
+ Snapshots older than 30 days:
849
+ `+ie.join(`
850
+ `)+"\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})
853
+ `),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
784
858
  FROM session_aliases sa
785
859
  LEFT JOIN sessions s ON s.id = sa.session_id
786
- WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),n=Vc(s),r=Ur(t.stage);t.stage("Probing daemon");let i=await Fr();t.stage("Detecting label collisions");let o=Pr();t.stage("Checking ingest freshness");let a=$r();if(t.done(),e.json){process.stdout.write(JSON.stringify({scanned:s.length,violations:n.length,items:n,health:r,pipeline:i,labelCollisions:o,ingestFreshness:a},null,2)),process.stdout.write(`
787
- `);let l=i.state==="degraded",u=a.status==="fail";return n.length===0&&r.db.integrity==="ok"&&!l&&!u?0:1}console.log(f.dim("\u2014 System health \u2014")),console.log(` Database ${V(r.db.sizeBytes)} (${r.rows.messages.toLocaleString()} messages across ${r.rows.sessions.toLocaleString()} sessions, ${r.rows.projects.toLocaleString()} projects)`);{let l=r.wal,u=l.level==="error"?f.err(V(l.sizeBytes)):l.level==="warn"?f.warn(V(l.sizeBytes)):f.ok(V(l.sizeBytes)),p=l.level==="error"?" (readers are pinning the checkpoint frontier \u2014 see warnings below)":l.level==="warn"?" (above 100 MB \u2014 daemon will WARN until it drops)":" (daemon checkpoints PASSIVE every 60s, RESTART above 5 GB)";console.log(` WAL ${u}${p}`)}console.log(` Free pages ${r.db.freelistCount.toLocaleString()} (${V(r.db.freelistBytes)} reclaimable via VACUUM)`);{let l=r.fts.messages.fragments,u=r.fts.sessions.fragments,p=l===0&&u===0;console.log(` FTS segments messages=${l}, sessions=${u}`+(p?" (merged \u2014 search uses the index)":" (lower is faster \u2014 `recall optimize` merges them)"))}if(r.chunkQueue.size>0){let u=r.chunkQueue.size>1e5?f.warn(r.chunkQueue.size.toLocaleString()):r.chunkQueue.size.toLocaleString();console.log(` Embed queue ${u}`+(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 l=r.disk.freeBytes/r.disk.totalBytes*100;console.log(` Disk free ${V(r.disk.freeBytes)} of ${V(r.disk.totalBytes)} (${l.toFixed(1)}%)`)}if(console.log(` Integrity ${r.db.integrity==="ok"?f.ok("ok"):f.err(r.db.integrity)}`),r.warnings.length>0){console.log("");for(let l of r.warnings)console.log(` ${f.warn("!")} ${l}`)}if(console.log(""),sl(r.mcpProcesses),console.log(""),console.log(f.dim("\u2014 Ingest freshness \u2014")),a.status==="ok")console.log(f.ok(` \u2713 ${a.scanned.toLocaleString()} JSONL${a.scanned===1?"":"s"} scanned, none newer than the index`));else if(a.status==="warn"){console.log(` ${f.warn(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 live watcher may be missing events`);for(let l of a.sampleFiles.slice(0,5))console.log(` ${f.dim("\u2022")} ${l}`);console.log(f.dim(" Run `recall index` to force a reindex, or restart the daemon if persistent."))}else{console.log(` ${f.err(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 significant ingest gap`);for(let l of a.sampleFiles.slice(0,5))console.log(` ${f.dim("\u2022")} ${l}`);console.log(f.err(" Restart the daemon (recall stop && recall start) \u2014 the live watcher has fallen behind."))}if(console.log(""),console.log(f.dim("\u2014 Pipeline health (tab-name \u2192 session alias) \u2014")),i.daemon.running?console.log(` Daemon ${f.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 ${f.warn("not reachable")}`),i.runtime.silentTerminalRejections!==null){let l=i.runtime.silentTerminalRejections;console.log(` Auth rejections ${l===0?f.ok("0"):f.err(l.toLocaleString())} (extension /api/terminal/* requests denied without a valid X-Recall-Token)`)}if(i.runtime.autoExtract){let l=i.runtime.autoExtract;l.circuitBroken?console.log(` Auto-extract ${f.err("circuit broken")} (${l.reason??"unknown"} \u2014 toggle off/on to reset)`):l.consecutiveZeroTokenRuns>0&&console.log(` Auto-extract ${f.warn(`${l.consecutiveZeroTokenRuns} zero-token run(s)`)} (breaker trips at 3)`)}if(i.runtime.lastTerminalSyncAt!==null){let l=Date.now()-Date.parse(i.runtime.lastTerminalSyncAt),u=Number.isFinite(l)?Math.round(l/6e4):null,p=u!==null&&l>Dr;console.log(` Last ext sync ${p?f.warn(`${u} min ago`):f.ok(u===0?"just now":`${u} min ago`)} (most recent successful POST /api/terminal/sync)`)}else i.daemon.running&&console.log(` Last ext sync ${f.warn("never")} (no extension has called /api/terminal/sync since the daemon started)`);if(i.terminalsJson.exists&&i.terminalsJson.ageSeconds!==null){let l=Math.round(i.terminalsJson.ageSeconds/3600),u=i.terminalsJson.ageSeconds>24*3600;console.log(` terminals.json ${u?f.warn(`${l}h old`):f.ok(`${l}h old`)} (persisted registry mtime \u2014 fresh means extensions are connecting)`)}let c=i.recentSessions;if(c.total>0){let l=Math.round(c.fractionHeuristic*100),u=c.fractionHeuristic>=Mr&&c.total>=3;console.log(` Recent titles ${u?f.err(`${l}% heuristic`):f.ok(`${l}% heuristic`)} (${c.heuristicOnly}/${c.total} sessions in last ${Kt}h fell back to first-message title)`)}if(i.flags.length>0){console.log("");for(let l of i.flags)console.log(` ${f.warn("!")} ${l}`)}if(console.log(""),console.log(f.dim("\u2014 Label collisions (last 7d) \u2014")),o.length===0)console.log(f.ok(" \u2713 no session-list label collisions in the last week"));else{let l=o.filter(u=>!u.anyAliased);console.log(` ${f.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 u of o.slice(0,5)){let p=u.anyAliased?f.dim("partial alias"):f.warn("NO alias"),g=u.label.length>60?`${u.label.slice(0,57)}\u2026`:u.label;console.log(` ${f.dim(`${u.count}\xD7`)} ${g} ${p} ${f.dim(`(${u.project})`)}`)}o.length>5&&console.log(f.dim(` \u2026 and ${o.length-5} more (--json for full list)`)),console.log(""),console.log(f.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(f.dim(" <!-- claude-recall-alias: T2.1 Run-Prospects -->")),console.log(f.dim(` The watcher will read the header, alias the session, and strip the marker from the
788
- displayed first user message. See docs/HANDOFF.md \u2192 "Alias header convention".`)),l.length>0&&(console.log(""),console.log(f.warn(` ${l.length} of these groups have NO alias on any row \u2014 strongest candidates
789
- for the header-alias fix above.`)))}if(console.log(""),console.log(f.dim("\u2014 Tab-name invariant \u2014")),n.length===0)return console.log(f.ok(` \u2713 holds across ${s.length.toLocaleString()} aliased session${s.length===1?"":"s"}`)),console.log(f.dim(" No fabricated origin labels (`VS Code \xB7 cwd \xB7 branch`) found. No deprecated cwd-branch synthesis found.")),r.db.integrity==="ok"&&a.status!=="fail"?0:1;console.log(f.err(`\u2717 ${n.length} invariant violation${n.length===1?"":"s"} found across ${s.length.toLocaleString()} aliased sessions`)),console.log("");let d=new Map;for(let l of n){let u=d.get(l.violation)??[];u.push(l),d.set(l.violation,u)}for(let[l,u]of d){console.log(f.warn(` ${l} (${u.length})`));for(let p of u.slice(0,10))console.log(` ${p.session_id.slice(0,8)} ${f.dim("\u2192")} ${JSON.stringify(p.alias)}`);u.length>10&&console.log(f.dim(` \u2026 and ${u.length-10} more (rerun with --json for the full list)`)),console.log("")}return console.log(f.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 rl(){try{let e=kr(S);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}var Jc,Kc,Dr,Kt,Mr,Hr=b(()=>{"use strict";Ut();N();D();wr();zt();Ar();Ne();Jc=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],Kc=new RegExp(`^(${Jc.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);Dr=5*6e4,Kt=24,Mr=.5});var Br={};Ke(Br,{runOptimize:()=>ll});import{existsSync as il,readFileSync as ol}from"node:fs";import{join as al}from"node:path";function cl(){let e=al(S,"daemon.pid");if(!il(e))return!1;try{let t=parseInt(ol(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function be(e,t){let s=Date.now();try{return t(),{step:e,ok:!0,durationMs:Date.now()-s}}catch(n){return{step:e,ok:!1,durationMs:Date.now()-s,error:n.message}}}async function ll(e={}){let t=m(),s=[];if(e.vacuum&&cl())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)+`
790
- `),2):(console.error(f.err("\u2717 VACUUM requires the daemon to be stopped. Run `recall stop` first, then re-run with --vacuum.")),2);s.push(await be("wal_checkpoint(TRUNCATE)",()=>{t.pragma("wal_checkpoint(TRUNCATE)")})),s.push(await be("messages_fts optimize",()=>{t.exec("INSERT INTO messages_fts(messages_fts) VALUES('optimize');")})),s.push(await be("sessions_fts optimize",()=>{t.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize');")})),s.push(await be("PRAGMA optimize",()=>{t.exec("PRAGMA optimize")})),e.vacuum&&s.push(await be("VACUUM",()=>{t.exec("VACUUM")}));let n=s.filter(r=>!r.ok);if(e.json)return process.stdout.write(JSON.stringify({ok:n.length===0,steps:s,vacuum:!!e.vacuum},null,2)+`
791
- `),n.length===0?0:1;for(let r of s){let i=r.ok?f.ok("\u2713"):f.err("\u2717"),o=`${r.durationMs} ms`;console.log(` ${i} ${r.step.padEnd(28)} ${f.dim(o)}`),r.error&&console.log(` ${f.err(r.error)}`)}return n.length===0?(console.log(""),console.log(f.ok("All maintenance passes completed.")),e.vacuum||console.log(f.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from deleted rows.")),0):(console.log(""),console.log(f.warn(`${n.length} step(s) failed \u2014 review the errors above.`)),1)}var Wr=b(()=>{"use strict";Ut();N();D()});N();import{McpServer as dl}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as ul}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as y}from"zod";import{fileURLToPath as pl}from"node:url";Ne();function is(e){let t=e.onShutdown,s=e.pollIntervalMs??5e3,n=e.logger??(_=>{process.stderr.write(_+`
792
- `)}),r=e.stdin??process.stdin,i=e.getPpid??(()=>process.ppid),o=e.getPid??(()=>process.pid),a=!1,c=null,d=i(),l=()=>{c&&(clearInterval(c),c=null),r.removeListener("end",p),r.removeListener("close",g)},u=_=>{if(a)return;a=!0,n(`[parent-death-guard] shutdown triggered: ${_} (pid=${o()} initialPpid=${d} currentPpid=${i()})`),l();let h;try{h=t()}catch(E){n(`[parent-death-guard] onShutdown threw: ${Re(E)}`);return}h&&typeof h.then=="function"&&h.catch(E=>{n(`[parent-death-guard] onShutdown rejected: ${Re(E)}`)})},p=()=>u("stdin end"),g=()=>u("stdin close");return r.on("end",p),r.on("close",g),d!==1&&(c=setInterval(()=>{let _=i();_!==d&&u(`ppid changed ${d} -> ${_}`)},s),typeof c.unref=="function"&&c.unref()),{stop:l}}var ei=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,ti=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,si=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function ni(e){return e.replace(ei,"").trim()}function ri(e){let t=e.replace(ti,"[tool call]");return t=t.replace(si,"[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,`
793
-
794
- `),t.trim()}function ii(e){return e.role??e.type??"message"}function os(e,t,s={}){let n=s.mode??"condensed",r=s.includeSidechain===!0,i=s.since?Date.parse(s.since):0,o=t.filter(l=>!(!r&&l.is_sidechain===1||i&&l.timestamp&&Date.parse(l.timestamp)<i)),a=[];s.prelude&&(a.push(s.prelude.trim()),a.push("")),a.push(`# Claude Recall, past session context (${n})`),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,d=0;for(let l of o){let u=l.content_text??"",p=ni(u);n==="condensed"&&(p=ri(p));let g=p.length>0,_=!!l.tool_names&&l.tool_names.length>0;if(!g&&!_){d+=1;continue}let h=ii(l),E=l.timestamp?` \`${l.timestamp}\``:"";a.push(`## ${h}${E}`),a.push(""),_&&n==="condensed"&&(a.push(`_tools used: ${l.tool_names}_`),a.push("")),g&&(a.push(p),a.push("")),c+=1}return a.push("---"),a.push(""),a.push(`_${c} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),a.join(`
795
- `)}Oe();et();import{existsSync as di,mkdirSync as Zl,readFileSync as ui,writeFileSync as Ql,chmodSync as ed}from"node:fs";import{homedir as pi}from"node:os";import{join as ds}from"node:path";import{z as Z}from"zod";function mi(){return process.env.RECALL_HOME??ds(pi(),".recall")}function gi(){return ds(mi(),"config.json")}var _i=Z.object({enabled:Z.boolean().default(!1),backend:Z.enum(["api","mcp"]).default("api"),apiKey:Z.string().optional(),model:Z.string().default("claude-opus-4-7"),maxTagsPerSession:Z.number().int().min(1).max(10).default(4),minTagsPerSession:Z.number().int().min(1).max(10).default(2),autopilot:Z.boolean().default(!1)}),tt={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function fi(){let e=gi();if(!di(e))return{};try{return JSON.parse(ui(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function st(){let e=fi().autoTag;if(!e)return{...tt};let t=_i.safeParse({...tt,...e});return t.success?t.data:{...tt}}rt();N();Oe();ke();import{z as M}from"zod";N();D();import{writeFileSync as vi,mkdirSync as Di,existsSync as Mi}from"node:fs";import{join as ms}from"node:path";var it=ms(S,"notes");function gs(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Fi(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(s=>!!s&&typeof s=="object"&&typeof s.synopsis=="string"&&typeof s.replaced_at=="string"):[]}catch{return[]}}function Pi(){k(),Mi(it)||Di(it,{recursive:!0})}function Ui(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:gs(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:Fi(e.auto_synopsis_history)}}var $i="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function ot(e){let t=m().prepare(`SELECT ${$i} FROM session_notes WHERE session_id = ?`).get(e);return t?Ui(t):null}function _s(e,t){let s=m(),n=new Date().toISOString(),r=s.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),i=[];return r&&(i=gs(r.previous_versions),r.content!==t&&r.content.length>0&&i.push({content:r.content,replaced_at:n})),s.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
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)+`
864
+ `),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,`
869
+
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)
796
872
  VALUES (?, ?, ?, ?)
797
873
  ON CONFLICT(session_id) DO UPDATE SET
798
874
  content = excluded.content,
799
875
  updated_at = excluded.updated_at,
800
- previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(i)),ji(e,t,n),ot(e)??{session_id:e,content:t,updated_at:n,previous_versions:i,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}function ji(e,t,s){try{Pi();let n=ms(it,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
801
- `;vi(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}ut();N();var mt=60,Ki=6e4,oe=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 pt(e){let t=new Date().toISOString(),s;try{s=JSON.stringify(e.args??null)}catch{s='"<unserializable>"'}m().prepare(`INSERT INTO mcp_audit_events (tool, args_json, result, error_message, caller, at)
802
- VALUES (?, ?, ?, ?, ?, ?)`).run(e.tool,s,e.result,e.errorMessage??null,e.caller??null,t)}var Q=class{capacity;windowMs;hits=[];constructor(t=mt,s=Ki){this.capacity=t,this.windowMs=s}consume(t=Date.now()){if(this.evict(t),this.hits.length>=this.capacity){let s=this.hits[0],n=Math.max(1,s+this.windowMs-t);throw new oe(n)}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 s=t-this.windowMs;for(;this.hits.length>0&&this.hits[0]<s;)this.hits.shift()}};async function I(e){try{e.limiter.consume()}catch(t){throw t instanceof oe&&pt({tool:e.tool,args:e.args,result:"rate_limited",errorMessage:t.message,caller:e.caller}),t}try{let t=await e.run();return pt({tool:e.tool,args:e.args,result:"ok",caller:e.caller}),t}catch(t){let s=t instanceof Error?t.message:String(t);throw pt({tool:e.tool,args:e.args,result:"error",errorMessage:s,caller:e.caller}),t}}N();import{z as Vi}from"zod";function X(e){let t=e.trim();if(t.length<4)return null;let s=m();if(t.length>=32)return s.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let n=s.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(`${t}%`);return n.length===1?n[0].id:null}function Ce(e){return{content:[{type:"text",text:e}],isError:!0}}function x(e){return e instanceof oe?Ce(e.message):e instanceof Vi.ZodError?Ce(`invalid input: ${e.message}`):e instanceof Error&&e.message.startsWith("SQLITE_")?Ce("database constraint error"):Ce(e instanceof Error?e.message:String(e))}function ne(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function ee(e){return{content:[{type:"text",text:e}],isError:!0}}var qi=`
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=`
803
879
 
804
880
  ---
805
881
 
806
- `,Zi=5e5;function Ts(e,t={}){let s=t.limiter??new Q;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:M.string().describe("Full session UUID or 8+-character prefix."),tag:M.string().min(1).describe("Tag to add. Normalized: lowercase, dashes, max 40 chars.")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"add_tag",args:{sessionId:r,tag:n.tag},limiter:s,run:()=>Le(r,n.tag)});return ne({sessionId:r,...i})}catch(r){return x(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:M.string().describe("Full session UUID or 8+-character prefix."),tag:M.string().min(1).describe("Tag to remove (normalized server-side).")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=se(n.tag);if(!i)return ee("tag must contain at least one alphanumeric character");let o=await I({tool:"remove_tag",args:{sessionId:r,tag:i},limiter:s,run:()=>as(r,i)});return ne({sessionId:r,...o})}catch(r){return x(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:M.string().describe("Full session UUID or 8+-character prefix."),alias:M.string().min(1).max(120).describe("New alias (non-empty, max 120 chars).")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"set_alias",args:{sessionId:r,alias:n.alias},limiter:s,run:()=>Ie(r,n.alias)});return ne(i)}catch(r){return x(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:M.string().describe("Full session UUID or 8+-character prefix."),markdown:M.string().min(1).max(5e4).describe("Markdown to append.")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"append_note",args:{sessionId:r,length:n.markdown.length},limiter:s,run:()=>{let o=ot(r),a=o&&o.content.length>0?`${o.content}${qi}${n.markdown}`:n.markdown;if(a.length>Zi)throw new Error(`note would exceed the 500 KB cumulative limit (current: ${o?.content.length??0} bytes, adding: ${n.markdown.length} bytes)`);return _s(r,a)}});return ne(i)}catch(r){return x(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:M.string().min(1).max(120),parent_id:M.string().optional().describe("Parent collection id (omit for root)."),icon:M.string().max(32).optional(),color:M.string().max(32).optional(),description:M.string().max(2e3).optional()}},async n=>{try{let r=await I({tool:"create_collection",args:n,limiter:s,run:()=>ct({name:n.name,parent_id:n.parent_id??null,icon:n.icon??null,color:n.color??null,description:n.description??null})});return ne(r)}catch(r){return x(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:M.string().min(1),sessionId:M.string().describe("Full session UUID or 8+-character prefix."),note:M.string().max(2e3).optional()}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);if(!m().prepare("SELECT 1 FROM collections WHERE id = ?").get(n.collectionId))return ee("collection not found");let a=await I({tool:"add_session_to_collection",args:{collectionId:n.collectionId,sessionId:r,note:n.note??null},limiter:s,run:()=>lt(n.collectionId,r,n.note??null)});return ne({collectionId:n.collectionId,sessionId:r,...a})}catch(r){return x(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:M.string().min(1),sessionId:M.string().describe("Full session UUID or 8+-character prefix.")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"remove_session_from_collection",args:{collectionId:n.collectionId,sessionId:r},limiter:s,run:()=>Es(n.collectionId,r)});return ne({collectionId:n.collectionId,sessionId:r,...i})}catch(r){return x(r)}})}import{z as H}from"zod";N();D();import{randomUUID as Ss}from"node:crypto";import{writeFileSync as bs,readFileSync as Bd,existsSync as Qi,mkdirSync as eo}from"node:fs";import{join as gt}from"node:path";var ve=gt(S,"threads"),to=gt(ve,"index.json");function ys(){k(),Qi(ve)||eo(ve,{recursive:!0})}function _t(e,t,s){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:s?.project??null,project_count:s?.project_count??0,folder_id:e.folder_id??null}}function ws(e){let t=new Map;if(e.length===0)return t;let s=m(),n=e.map(()=>"?").join(","),r=s.prepare(`SELECT te.thread_id AS thread_id,
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,
807
883
  p.name AS project,
808
884
  COUNT(*) AS n,
809
885
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
810
886
  FROM thread_edges te
811
887
  LEFT JOIN sessions s ON s.id = te.session_id
812
888
  LEFT JOIN projects p ON p.id = s.project_id
813
- WHERE te.thread_id IN (${n})
814
- 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(u=>u.project!==null),d=c.length,l=null;c.length>0&&(l=[...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:l,project_count:d})}return t}function Rs(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 Ns(e){let s=m().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
889
+ 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,
815
891
  s.auto_title AS auto_title,
816
892
  s.auto_title_source AS auto_title_source,
817
893
  s.first_user_message AS first_user_message,
@@ -819,11 +895,11 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
819
895
  FROM (SELECT ? AS sid) q
820
896
  LEFT JOIN sessions s ON s.id = q.sid
821
897
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
822
- LEFT JOIN projects p ON p.id = s.project_id`).get(e),n=s?.auto_title_source??null;return{alias:s?.alias??null,auto_title:s?.auto_title??null,auto_title_source:n==="agent"||n==="heuristic"?n:null,first_user_message:s?.first_user_message??null,project:s?.project??null}}function ft(e){let s=m().prepare(`SELECT
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
823
899
  COUNT(*) AS session_count,
824
900
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
825
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function Y(e){let t=U(e);t&&(ys(),bs(gt(ve,`${e}.json`),JSON.stringify(t,null,2)),Ls())}function Ls(){ys();let e=ht({includeArchived:!0});bs(to,JSON.stringify({threads:e},null,2))}function De(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=m(),n=Ss(),r=new Date().toISOString();s.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(n,t,e.summary?.trim()||null,r),e.originSessionId&&s.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
826
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),Y(n);let i=U(n);if(!i)throw new Error("thread creation succeeded but read-back failed");return i}function ht(e={}){let t=m(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=ws(n.map(i=>i.id));return n.map(i=>_t(i,ft(i.id),r.get(i.id)))}function U(e){let t=m(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
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.*,
827
903
  NULLIF(sa.alias, '') AS alias,
828
904
  s.auto_title AS auto_title,
829
905
  s.auto_title_source AS auto_title_source,
@@ -834,10 +910,10 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
834
910
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
835
911
  LEFT JOIN projects p ON p.id = s.project_id
836
912
  WHERE e.thread_id = ?
837
- ORDER BY e.added_at ASC`).all(e).map(Rs),r=ws([e]).get(e);return{..._t(s,ft(s.id),r),edges:n}}function As(e){return m().prepare(`SELECT t.* FROM threads t
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
838
914
  JOIN thread_edges e ON e.thread_id = t.id
839
915
  WHERE e.session_id = ? AND t.archived = 0
840
- ORDER BY t.created_at DESC`).all(e).map(n=>_t(n,ft(n.id)))}function Me(e){let t=m();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let n=new Date().toISOString(),r=e.parentSessionId??null,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
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
841
917
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
842
918
  VALUES (?, ?, ?, ?, ?, ?, ?)
843
919
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -845,35 +921,35 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
845
921
  role = excluded.role,
846
922
  confidence = excluded.confidence,
847
923
  source = excluded.source,
848
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,i,o,a,n),Y(e.threadId);let c=Ns(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:i,confidence:o,source:a,added_at:n,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 Os(e,t){let n=m().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&Y(e),{removed:n.changes}}function _e(e,t,s){let n=m(),r=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(s!==null){if(s===t)throw new Error("cycle detected: session cannot be its own parent");let a=n.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),c=s,d=new Set;for(;c!==null;){if(c===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(d.has(c))break;d.add(c),c=a.get(e,c)?.parent_session_id??null}}let i=s?"child":"origin";n.prepare(`UPDATE thread_edges
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
849
925
  SET parent_session_id = ?, role = ?, added_at = ?
850
- WHERE thread_id = ? AND session_id = ?`).run(s,i,new Date().toISOString(),e,t),Y(e);let o=Ns(t);return Rs({...r,parent_session_id:s,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 xs(e,t){let s=t.trim();if(!s)throw new Error("name cannot be empty");m().prepare("UPDATE threads SET name = ? WHERE id = ?").run(s,e),Y(e);let r=U(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Is(e){m().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function ks(e){m().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Cs(e){m().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function vs(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let s=m(),n=new Date().toISOString();s.transaction(()=>{let i=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let o of i)s.prepare(`INSERT INTO thread_edges
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
851
927
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
852
928
  VALUES (?, ?, ?, ?, ?, ?, ?)
853
929
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
854
930
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
855
931
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
856
932
  confidence = MAX(thread_edges.confidence, excluded.confidence),
857
- source = thread_edges.source`).run(t,o.session_id,o.parent_session_id,o.role,o.confidence,o.source,n);s.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Y(t),Ls();let r=U(t);if(!r)throw new Error("merge destination disappeared");return r}function Ds(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=m(),s=new Date().toISOString(),n=Ss();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(n,e.newThreadName.trim(),s);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
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
858
934
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
859
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(n,i,o.parent_session_id,o.role,o.confidence,o.source,s),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,i))}})(),Y(e.threadId),Y(n);let r=U(n);if(!r)throw new Error("split destination disappeared");return r}yt();var Lo=50;function Ao(e){if(e.length===0)return[];let t=[...e].sort((c,d)=>c.added_at<d.added_at?-1:c.added_at>d.added_at?1:0),s=new Map,n=new Set,r=[];for(let c of t)if(c.parent_session_id){let d=s.get(c.parent_session_id)??[];d.push(c),s.set(c.parent_session_id,d)}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(n.has(c.session_id))continue;n.add(c.session_id),r.push(c.session_id);let d=s.get(c.session_id)??[];for(let l of d)n.has(l.session_id)||a.push(l)}for(let c of t)n.has(c.session_id)||(n.add(c.session_id),r.push(c.session_id));return r}function Oo(e){let t=Bs(e.sessionId),s=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
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(`
860
936
  `);return`${t}
861
- ${s}`}function xo(e){return Ws(e)?.auto_title_source??null}async function Io(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(wt(),en));if(!s())throw new Error("claude CLI not found on PATH");let n=await t(e.prompt,[],{model:e.model});if(!n.success)throw new Error(`claude CLI exited ${n.exitCode}: ${n.stderr.slice(-500)}`);let r=ko(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,Lo)}function ko(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return tn(n)}}catch{}return tn(t)}function tn(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function nn(e,t={}){let s=U(e);if(!s)throw new Error(`thread not found: ${e}`);let n=Ao(s.edges),r=n.length,i=t.force??!1,o=t.signal,a=[],c=[],d=[];for(let l=0;l<n.length;l++){let u=n[l],p=l+1;if(o?.aborted){let _={sessionId:u,reason:"cancelled"};c.push(_),t.onSkipped?.(_);continue}if(!i&&xo(u)==="agent"){let _={sessionId:u,reason:"already-titled"};c.push(_),t.onSkipped?.(_);continue}let g;try{if(sn)g=await sn({sessionId:u,current:p,total:r});else{let _=Oo({sessionId:u,current:p,total:r});g=await Io({prompt:_,model:t.model})}}catch(_){let h=_ instanceof Error?_.message:String(_??"unknown error"),E={sessionId:u,error:h};d.push(E),t.onFailed?.(E);continue}try{bt(u,g,"agent")}catch(_){let h=_ instanceof Error?_.message:String(_??"unknown error"),E={sessionId:u,error:`setAutoTitle failed: ${h}`};d.push(E),t.onFailed?.(E);continue}a.push(u),t.onProgress?.({current:p,total:r,sessionId:u,title:g})}return{generated:a,skipped:c,failed:d}}var sn=null;N();import{execFile as Yo}from"node:child_process";import{promisify as zo}from"node:util";import{readlink as Jo,readFile as dn}from"node:fs/promises";import{platform as Ue}from"node:os";import{readFileSync as Co,statSync as vo}from"node:fs";var Do=200*1024*1024;var Nt=.5,an=Nt,Mo=[{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"}],Fo=["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 rn(e){if(!e)return null;if(e.startsWith("/")){let s=e.split(" \xB7 ");if(s.length>1)return s[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function Po(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of Mo)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Uo(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],s=Math.min(e.length,t.length);for(let n=0;n<s;n++){let r=e[n];if(!r)continue;let i=r.toLowerCase();for(let o of Fo)if(i.includes(o))return{weight:t[n],matched:o,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function Rt(e,t){if(!e||!t||e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function $o(e,t){if(e.length===0||t.length===0)return 0;let s=0;for(let n of e)for(let r of t){let i=Rt(n,r);i>s&&(s=i)}return s}function jo(e,t){let s=Rt(e.mean_embedding,t.mean_embedding),n=Rt(e.tail_pool,t.head_pool),r=$o(e.sample_chunks,t.sample_chunks),i=0,o=null;if(s>i&&(i=s,o="mean"),n>i&&(i=n,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 Ho(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 Bo(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let s=0;for(let r of t)e.has(r)&&s++;return s===0?{weight:0,count:0}:{weight:Math.min(.4,s*.1),count:s}}function Wo(e,t){let s=rn(e),n=rn(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function on(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Xo(e,t){let s=0,n=null,r=!1;if(e.authored_paths.size>0){let i=t.recent_user_messages.slice(0,3).join(`
862
- `).toLowerCase();for(let o of e.authored_paths){let a=o.toLowerCase();if(t.touched_files.has(o)||i.includes(a)){s+=.5,n=o;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let i=on(t.recent_user_messages[0]);if(i.length>=200)for(let o of e.authored_content){let a=on(o);if(a.length<200)continue;let c=Math.min(a.length,240),d=a.slice(0,c);if(i.includes(d)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function Go(e,t,s=an){if(t.started_at_ms<=e.started_at_ms)return null;let n=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<n)return null;let r=Po(n,t.started_at_ms),i=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],o=Uo(i),a=Bo(e.touched_files,t.touched_files),c=Wo(e.auto_title,t.auto_title),d=jo(e,t),l=Ho(e,t),u=Xo(e,t),p=r.weight+o.weight+a.weight+c.weight+d.weight+l.weight+u.weight;if(p<s)return null;let g=[];if(r.label&&g.push(`temporal ${r.label} (+${r.weight})`),o.matched){let _=o.matchedIndex===0?"opening message":`message #${o.matchedIndex+1}`;g.push(`continuation phrase "${o.matched}" in ${_} (+${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})`),d.weight>0&&d.mode&&g.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),l.same&&g.push(`same cluster (+${l.weight})`),u.weight>0){let _=[];u.pathMatch&&_.push(`opened authored path "${u.pathMatch.split("/").pop()}"`),u.contentMatch&&_.push("verbatim-paste of authored content"),g.push(`doc-authorship: ${_.join(" + ")} (+${u.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:d.weight,cluster:l.weight,doc_authorship:u.weight},reasons:g}}function cn(e,t=an){let s=[];for(let n=0;n<e.length;n++){let r=e[n],i=null;for(let o=0;o<n;o++){let a=e[o],c=Go(a,r,t);c&&(!i||c.confidence>i.confidence)&&(i=c)}i&&s.push(i)}return s}function ln(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,i=[],o=new Set,a=[],c;try{if(vo(e).size>Do)return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a};c=Co(e,"utf8")}catch{return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}let d=0;for(;d<c.length;){let l=c.indexOf(`
863
- `,d),u=l===-1?c.length:l,p=c.slice(d,u);if(d=l===-1?c.length:l+1,!p.trim())continue;let g;try{g=JSON.parse(p)}catch{continue}let _=g;if(_.type==="user"&&_.message?.role==="user"&&typeof _.message.content=="string"&&i.length<s){let E=_.message.content.trim();E&&i.push(E.length>n?E.slice(0,n):E)}let h=_.message?.content;if(Array.isArray(h))for(let E of h){if(!E||typeof E!="object")continue;let T=E;if(T.type!=="tool_use")continue;let w=T.input??{},v=typeof w.file_path=="string"?w.file_path:null;if(v){let L=Pe(v);L&&r.add(L)}if((T.name==="Write"||T.name==="Edit"||T.name==="MultiEdit")&&v){let L=Pe(v);L&&o.add(L);let P=typeof w.content=="string"?w.content:typeof w.new_string=="string"?w.new_string:null;P&&P.length>=200&&a.push(P.length>4096?P.slice(0,4096):P)}if(T.name==="Bash"&&typeof w.command=="string")for(let L of w.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let P=Pe(L[1]);P&&r.add(P)}if((T.name==="Glob"||T.name==="Grep")&&typeof w.pattern=="string"){let L=Pe(w.pattern);L&&!L.includes("*")&&r.add(L)}}}return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}function Pe(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var At=zo(Yo),Ko=6,un="Active ",pn=" sessions \u2014 ",Vo=6e4;async function qo(){if(Ue()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await At(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
864
- `)){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)&&n.push(o)}return n}catch{continue}return[]}async function Zo(e){let t=Ue();if(t==="linux")try{return(await Jo(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:n}=await At(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
865
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Qo(e){let t=Ue();if(t==="linux")try{let s=await dn(`/proc/${e}/stat`,"utf8"),n=s.lastIndexOf(")");if(n===-1)return null;let r=s.slice(n+1).trim().split(/\s+/),i=Number(r[19]);if(!Number.isFinite(i))return null;let o=await dn("/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 s of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await At(s,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(n.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function ea(e,t){let s=await qo();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let o of s){let a=await Zo(o);if(a&&(a===n||a.startsWith(n+"/"))){let c=await Qo(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=Vo;for(let d of t){if(i.has(d.session_id)||!d.started_at)continue;let l=Date.parse(d.started_at);if(!Number.isFinite(l))continue;let u=Math.abs(l-o);u<c&&(c=u,a=d)}a&&i.add(a.session_id)}return i}function ta(e){let s=m().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!s)throw new Error(`project ${e} not found`);return s}function sa(e,t){let s=m(),n=`${un}${t}${pn}%`,r=s.prepare(`SELECT t.id
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
866
942
  FROM threads t
867
943
  WHERE t.archived = 0
868
944
  AND t.name LIKE ?
869
- ORDER BY t.created_at DESC`).all(n);for(let o of r){let a=U(o.id);if(a&&a.project===t)return a}let i=s.prepare(`SELECT DISTINCT te.thread_id AS id
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
870
946
  FROM thread_edges te
871
947
  JOIN sessions s ON s.id = te.session_id
872
948
  JOIN threads t ON t.id = te.thread_id
873
949
  WHERE s.project_id = ?
874
950
  AND t.archived = 0
875
951
  AND t.name LIKE ?
876
- LIMIT 1`).get(e,n);return i?U(i.id):null}function mn(e){let t=e?new Date(e):new Date,s=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${s}-${n}-${r}`}function Lt(e,t){let s=m(),n=t>0,r=n?Date.now()-t*60*60*1e3:0;return n?s.prepare(`SELECT s.id AS session_id,
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,
877
953
  sa.alias AS alias,
878
954
  s.auto_title AS auto_title,
879
955
  s.first_user_message AS first_user_message,
@@ -884,7 +960,7 @@ ${s}`}function xo(e){return Ws(e)?.auto_title_source??null}async function Io(e){
884
960
  WHERE s.project_id = ?
885
961
  AND s.file_mtime IS NOT NULL
886
962
  AND s.file_mtime > ?
887
- ORDER BY s.started_at ASC`).all(e,r):s.prepare(`SELECT s.id AS session_id,
963
+ ORDER BY s.started_at ASC`).all(e,r):n.prepare(`SELECT s.id AS session_id,
888
964
  sa.alias AS alias,
889
965
  s.auto_title AS auto_title,
890
966
  s.first_user_message AS first_user_message,
@@ -893,26 +969,25 @@ ${s}`}function xo(e){return Ws(e)?.auto_title_source??null}async function Io(e){
893
969
  FROM sessions s
894
970
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
895
971
  WHERE s.project_id = ?
896
- ORDER BY s.started_at ASC`).all(e)}function na(e){let t=m(),s=[];for(let n of e){if(!n.started_at)continue;let r=Date.parse(n.started_at);if(!Number.isFinite(r))continue;let i=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(n.session_id);if(!i?.file_path)continue;let o=i.ended_at?Date.parse(i.ended_at):null,a=ln(i.file_path,{maxUserMessages:7});s.push({id:n.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(o)?o:null,first_user_message:n.first_user_message,recent_user_messages:a.recent_user_messages,auto_title:n.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 s}function ra(e){let s=m().prepare(`SELECT session_id, parent_session_id, source
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
897
973
  FROM thread_edges
898
- WHERE thread_id = ?`).all(e),n=new Map;for(let r of s)n.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return n}async function Ot(e,t={}){let s=ta(e),n=t.windowHours??Ko,r=t.scoreThreshold??Nt,i=t.useLivePids??!0,o=[],a=[];if(i&&s.decoded_path){let h=Lt(e,0),E=await ea(s.decoded_path,h);if(E===null){let w=Ue()==="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(w),a=Lt(e,n)}else E.size===0?(o.push(`No active terminals open in ${s.name} (cwd=${s.decoded_path}). Open a Claude terminal in this repo and re-run.`),a=[]):a=h.filter(T=>E.has(T.session_id))}else a=Lt(e,n);a.length===0&&!o.length&&o.push(`No active sessions in ${s.name} within the last ${n}h.`);let c=sa(e,s.name),d=new Set(c?c.edges.map(h=>h.session_id):[]),l=a.filter(h=>!d.has(h.session_id)),u=na(a);u.sort((h,E)=>h.started_at_ms-E.started_at_ms);let p=cn(u,r),g=c?c.edges.filter(h=>h.source!=="auto-active"&&(h.parent_session_id||h.role==="origin")).map(h=>({session_id:h.session_id,parent_session_id:h.parent_session_id})):[],_=c?c.name:`${un}${s.name}${pn}${mn(t.todayIso)}`;return{project:s,thread:{id:c?.id??null,name:_,exists:!!c,existing_session_count:c?.edges.length??0},candidates:a,proposed_additions:l,proposed_edges:p,preserved_manual_edges:g,warnings:o}}function gn(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},s;e.thread.exists&&e.thread.id?s=e.thread.id:s=De({name:e.thread.name,summary:`Auto-captured by sync-active on ${mn()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=s;let n=ra(s);for(let o of e.candidates)n.has(o.session_id)||(Me({threadId:s,sessionId:o.session_id,source:"auto-active",confidence:.5}),t.added++,n.set(o.session_id,{parent_session_id:null,source:"auto-active"}));for(let o of e.proposed_edges){let a=n.get(o.child_id);if(a&&a.source==="auto-active"&&a.parent_session_id!==o.parent_id&&n.has(o.parent_id))try{_e(s,o.child_id,o.parent_id),t.edges_set++,n.set(o.child_id,{parent_session_id:o.parent_id,source:a.source})}catch{}}let i=m().prepare(`SELECT session_id 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
899
975
  WHERE thread_id = ?
900
976
  AND source = 'auto-active'
901
977
  AND parent_session_id IS NULL
902
- AND role = 'child'`).all(s);for(let o of i)try{_e(s,o.session_id,null)}catch{}return t}function F(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function ia(e){return{content:[{type:"text",text:e}],isError:!0}}function $(e,t){return H.object(e).strict().parse(t)}var An=H.string().uuid(),z=An.describe("Thread id (UUID)."),te=An.describe("Session UUID (exact, not a prefix)."),_n={include_archived:H.boolean().optional().describe("Include archived threads (default false).")},fn={id:z},hn={session_id:te},En={name:H.string().min(1).max(200).describe("Human-readable thread name."),summary:H.string().max(4e3).optional().describe("Optional short description."),origin_session_id:te.optional().describe("Seed the thread with this session as its origin.")},Tn={thread_id:z,session_id:te,parent_session_id:te.optional().describe("If present, the edge is role=child with this parent; otherwise role=origin."),role:H.enum(["origin","child"]).optional()},Sn={thread_id:z,session_id:te,parent_session_id:te.nullable().describe("New parent (null to clear and promote the edge back to role=origin).")},bn={thread_id:z,session_id:te},yn={thread_id:z,name:H.string().min(1).max(200)},ce={thread_id:z},wn={source_id:z.describe("Thread to dissolve \u2014 its edges move into dest_id."),dest_id:z.describe("Thread that absorbs source_id.")},Rn={thread_id:z,session_ids:H.array(te).min(1).max(500),new_thread_name:H.string().min(1).max(200)},Nn={thread_id:z,force:H.boolean().optional().describe("When true, regenerate titles for sessions that already have an agent-sourced title. Default false skips them.")},Ln={project_id:H.number().int().positive().describe("Numeric project id from list_projects. The sync is repo-scoped and never crosses projects."),mode:H.enum(["preflight","apply"]).describe("preflight returns the proposed plan without writing; apply writes the thread + edges and is idempotent."),window_hours:H.number().min(.5).max(168).optional().describe("Rolling activity window in hours. Default 6.")};function On(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:_n},async t=>{try{let{include_archived:s}=$(_n,t);return F(ht({includeArchived:s??!1}))}catch(s){return x(s)}}),e.registerTool("thread_get",{title:"Get thread with edges",description:"Full thread detail including every session edge (origin + children).",inputSchema:fn},async t=>{try{let{id:s}=$(fn,t),n=U(s);return n?F(n):ia(`thread not found: ${s}`)}catch(s){return x(s)}}),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:hn},async t=>{try{let{session_id:s}=$(hn,t);return F(As(s))}catch(s){return x(s)}})}function xn(e,t={}){let s=t.limiter??new Q;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:En},async n=>{try{let r=$(En,n),i=await I({tool:"thread_create",args:r,limiter:s,run:()=>De({name:r.name,summary:r.summary??null,originSessionId:r.origin_session_id})});return F(i)}catch(r){return x(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:Tn},async n=>{try{let r=$(Tn,n),i=await I({tool:"thread_add_session",args:r,limiter:s,run:()=>Me({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 x(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:Sn},async n=>{try{let r=$(Sn,n),i=await I({tool:"thread_set_parent",args:r,limiter:s,run:()=>_e(r.thread_id,r.session_id,r.parent_session_id)});return F(i)}catch(r){return x(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:bn},async n=>{try{let r=$(bn,n),i=await I({tool:"thread_remove_session",args:r,limiter:s,run:()=>Os(r.thread_id,r.session_id)});return F({thread_id:r.thread_id,session_id:r.session_id,...i})}catch(r){return x(r)}}),e.registerTool("thread_rename",{title:"Rename thread",description:"Change the display name of a thread.",inputSchema:yn},async n=>{try{let r=$(yn,n),i=await I({tool:"thread_rename",args:r,limiter:s,run:()=>xs(r.thread_id,r.name)});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_close",{title:"Close thread",description:"Mark the thread as closed (sets closed_at). Thread remains listed; reopen to clear.",inputSchema:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_close",args:r,limiter:s,run:()=>Is(r.thread_id)});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_reopen",{title:"Reopen thread",description:"Clear closed_at on a closed thread.",inputSchema:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_reopen",args:r,limiter:s,run:()=>ks(r.thread_id)});return F(i)}catch(r){return x(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:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_archive",args:r,limiter:s,run:()=>Cs(r.thread_id)});return F(i)}catch(r){return x(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:wn},async n=>{try{let r=$(wn,n),i=await I({tool:"thread_merge",args:r,limiter:s,run:()=>vs(r.source_id,r.dest_id)});return F(i)}catch(r){return x(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:Rn},async n=>{try{let r=$(Rn,n),i=await I({tool:"thread_split",args:r,limiter:s,run:()=>Ds({threadId:r.thread_id,sessionIds:r.session_ids,newThreadName:r.new_thread_name})});return F(i)}catch(r){return x(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:Ln},async n=>{try{let r=$(Ln,n);if(r.mode==="preflight"){let o=await Ot(r.project_id,{windowHours:r.window_hours});return F({plan:o})}let i=await I({tool:"sync_active_sessions",args:r,limiter:s,run:async()=>{let o=await Ot(r.project_id,{windowHours:r.window_hours}),a=gn(o);return{plan:o,result:a}}});return F(i)}catch(r){return x(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:Nn},async n=>{try{let r=$(Nn,n),i=await I({tool:"generate_thread_titles",args:r,limiter:s,run:async()=>{let o=await nn(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 x(r)}})}import{Worker as la}from"node:worker_threads";import{join as da}from"node:path";import{existsSync as ua}from"node:fs";import{existsSync as oa}from"node:fs";import{dirname as In}from"node:path";import{fileURLToPath as aa}from"node:url";var $e=null;function je(){if($e)return $e;let e=In(aa(import.meta.url));for(;!oa(`${e}/package.json`);){let t=In(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return $e=e,$e}var kn="BAAI/bge-base-en-v1.5";function pa(){return da(je(),"dist","daemon","embedder-worker.js")}var le=null,Ee=new Map,xt=0,de=!1,ue=class extends Error{kind;path;underlying;cause;constructor(t){super(ma(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}};function ma(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) \u2014 or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
903
- `)}function Cn(e){for(let t of Ee.values())t.reject(e);Ee.clear()}function ga(){if(le)return le;let e=pa();if(!ua(e))throw new ue({kind:"bundle-missing",detail:"embedder-worker bundle not found \u2014 run `npm run build:cli` to emit it",path:e});let t=new la(e);return t.on("message",s=>{let n=Ee.get(s.id);n&&(Ee.delete(s.id),n.resolve(s))}),t.on("error",s=>{console.error("[embedder-worker] thread error:",s);let n=s instanceof Error?s:new Error(String(s));Cn(n),le=null,de=!1}),t.on("exit",s=>{s!==0&&(console.error(`[embedder-worker] exited with code ${s}`),Cn(new Error(`embedder worker exited with code ${s}`))),le=null,de=!1}),le=t,t}function _a(e){return new Promise((t,s)=>{let n;try{n=ga()}catch(r){s(r instanceof Error?r:new Error(String(r)));return}Ee.set(e.id,{resolve:t,reject:s}),n.postMessage(e)})}function fa(){return xt=xt+1>>>0,String(xt)}function ha(e){if(!e.ok)throw e.unavailable?new ue({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 He(){if(!(de&&le))try{ha(await _a({id:fa(),type:"load"})),de=!0}catch(e){throw de=!1,e}}function Te(){return{loaded:de,modelId:kn,dim:768}}N();D();import{writeFileSync as Ea}from"node:fs";import{join as Ta}from"node:path";var Sa=Ta(S,"recall-events.json");function vn(e,t,s,n="cli"){m().prepare(`
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(`
904
979
  INSERT INTO recall_events (session_id, recalled_at, token_count, mode, caller)
905
980
  VALUES (?, datetime('now'), ?, ?, ?)
906
- `).run(e,t,s,n),ba()}function ba(){k();let t=m().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();Ea(Sa,JSON.stringify(t,null,2)+`
907
- `,"utf-8")}N();var re=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,s){super(`semantic search refused: vec_chunks has ${t} rows (cap ${s}). 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=s}};function It(e,t={}){let s=t.limit??ya()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>s)throw new re(r,s)}function ya(){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)}import{Worker as wa}from"node:worker_threads";import{join as Ra}from"node:path";var kt=class extends Error{constructor(s){super(`semantic kNN exceeded ${s}ms wall-clock budget -- query aborted`);this.timeoutMs=s}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},Na=1e4;function La(){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 Ct(e){let t=e.timeoutMs??La()??Na,s=(e.workerFactory??Aa)();return new Promise((n,r)=>{let i=setTimeout(()=>{s.terminate().catch(()=>{}),r(new kt(t))},t);s.once("message",o=>{clearTimeout(i);let a=o;a&&a.ok===!0&&Array.isArray(a.hits)?n(a.hits):r(new Error(a?.error??"worker returned malformed reply")),s.terminate().catch(()=>{})}),s.once("error",o=>{clearTimeout(i),s.terminate().catch(()=>{}),r(o)}),s.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function Aa(){let e=Ra(je(),"dist","daemon","query-worker.js");return new wa(e)}async function vt(e,t=50){return It(m()),Ct({query:e,limit:t})}async function Dn(e,t=10,s=.65){let n=m();It(n);let r=n.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let i=n.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!i)return[];let o=await Ct({precomputedVector:i.embedding,limit:t*5}),a=new Map;for(let d of o){if(d.sessionId===e)continue;let l=a.get(d.sessionId);(l===void 0||d.distance<l)&&a.set(d.sessionId,d.distance)}let c=[];for(let[d,l]of a){let u=1-l;u>=s&&c.push({sessionId:d,similarity:u})}return c.sort((d,l)=>l.similarity-d.similarity),c.slice(0,t)}function Oa(){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 Mn(e){let t=Oa(),s=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=s.get(o.id);c?(c.score+=a,c.lanes.push(o.lane),i+1<c.bestRank&&(c.bestRank=i+1,c.bestData=o.data)):s.set(o.id,{score:a,lanes:[o.lane],bestRank:i+1,bestData:o.data})}let n=[];for(let[r,i]of s)n.push({id:r,score:i.score,lanes:i.lanes,data:i.bestData});return n.sort((r,i)=>i.score-r.score),n}N();N();var xa=!1,Ia=null;var ka=new Set;function Ca(){return m().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function Fn(){return{running:xa,queueDepth:Ca(),lastProcessedAt:Ia,blacklistedCount:ka.size}}import{existsSync as va,mkdirSync as Sp,rmSync as bp,createWriteStream as yp,statSync as wp}from"node:fs";import{join as Pn}from"node:path";D();var Da=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function Ma(){return Pn(S,"models","BAAI","bge-base-en-v1.5")}function Se(){let e=Ma();return Da.every(t=>va(Pn(e,t.path)))}D();import{existsSync as Fa,readFileSync as Pa,writeFileSync as Op,unlinkSync as xp}from"node:fs";import{join as Ua}from"node:path";var Un=Ua(S,"license.json");function $n(){if(!Fa(Un))return null;try{let e=Pa(Un,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as $a,importSPKI as ja}from"jose";var jn=`-----BEGIN PUBLIC KEY-----
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-----
908
983
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
909
984
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
910
985
  -----END PUBLIC KEY-----
911
- `,Dt="ES256",Hn="clauderecall.com",Bn="clauderecall-cli";var Be=null;async function Ha(){return Be||(Be=await ja(jn,Dt),Be)}async function Wn(e){try{let t=await Ha(),{payload:s}=await $a(e,t,{issuer:Hn,audience:Bn,algorithms:[Dt]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Ba}from"node:crypto";import{hostname as Wa,userInfo as Xa,platform as Ga,arch as Ya}from"node:os";function Xn(){let e="unknown";try{e=Xa().username}catch{}let t=[Wa(),e,Ga(),Ya()];return Ba("sha256").update(t.join("\0")).digest("hex")}D();import{existsSync as za,readFileSync as Ja,writeFileSync as Wp}from"node:fs";import{join as Ka}from"node:path";var Gn=Ka(S,"license-check.json"),zp=1440*60*1e3,Va=720*60*60*1e3;function qa(){if(!za(Gn))return null;try{let e=JSON.parse(Ja(Gn,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Yn(e){let t=qa();return!t||t.license_key!==e?null:t.revoked?{revoked:!0,reason:t.reason?`license revoked: ${t.reason}`:"license revoked by issuer"}:Date.now()-new Date(t.last_checked_at).getTime()>Va?{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 Za=Date.UTC(2026,5,1,7,0,0);var Qa=1440*60*1e3,qp=60*Qa;async function We(){let e=$n();if(!e)return{tier:"free"};let t=await Wn(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Xn())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=Yn(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:ec(e,t.claims)}function ec(e,t){let s=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:s?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...s?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}N();N();D();import{join as Mt}from"node:path";var tc=new Set(["pending","approved","rejected"]),sc=new Set(["L1","L2","L3","L4","user"]),pm=Mt(S,"links"),nc=Mt(S,"suggestions"),mm=Mt(nc,"index.json");function zn(e){try{return JSON.parse(e)}catch{return e}}function rc(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:zn(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function ic(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:zn(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function oc(e){if(!sc.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Ft(e){return m().prepare(`SELECT * FROM session_links
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
912
987
  WHERE source_session_id = ? OR target_session_id = ?
913
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(rc)}function Pt(e={}){let t=m(),s=[],n=[];if(e.status){if(!tc.has(e.status))throw new Error(`invalid status: ${e.status}`);s.push("status = ?"),n.push(e.status)}e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.inferredBy&&(oc(e.inferredBy),s.push("inferred_by = ?"),n.push(e.inferredBy));let r=s.length?`WHERE ${s.join(" AND ")}`:"",i=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
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}
914
989
  ORDER BY confidence DESC, created_at DESC
915
- LIMIT ?`).all(...n,i).map(ic)}var ac=4e3,cc=2,lc=30,dc=.2,uc={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function Xe(e){return e?Math.ceil(e.length/4):0}function Jn(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/lc);return Math.max(dc,t)}function Kn(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function Vn(e){return m().prepare(`SELECT s.id,
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,
916
991
  NULLIF(sa.alias, '') AS alias,
917
992
  s.auto_title,
918
993
  s.auto_title_source,
@@ -923,27 +998,28 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
923
998
  FROM sessions s
924
999
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
925
1000
  LEFT JOIN projects p ON p.id = s.project_id
926
- WHERE s.id = ?`).get(e)??null}function qn(e){let s=m().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!s||!s.summary)return null;let n=s.summary.trim();return n.length>0?n:null}function Zn(e){let t=e.alias?.trim(),s=e.auto_title?.trim(),n=e.first_user_message?.trim();return s&&e.auto_title_source==="agent"?s:t||s||(n?n.slice(0,80):e.id.slice(0,8))}function pc(e){let s=m().prepare(`SELECT id, auto_title, started_at
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
927
1002
  FROM sessions
928
1003
  WHERE project_id = ?
929
- ORDER BY COALESCE(started_at, ''), id`).all(e),n=new Set,r=new Set,i=[];for(let p of s){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 "),_=g[0].trim(),h=g.length>1?g.slice(1).join(" \xB7 ").trim():null;i.push({id:p.id,brand:h||null,skill:_||null}),h&&n.add(h),_&&r.add(_)}let o=[...n].sort(),a=new Map;o.forEach((p,g)=>a.set(p,g));let c=[...r].sort(),d=new Map;c.forEach((p,g)=>d.set(p,g));let l=new Map,u=new Map;for(let p of i){if(!p.brand||!p.skill)continue;let g=a.get(p.brand),_=d.get(p.skill);if(g===void 0||_===void 0)continue;let h=`${g}.${_}`,E=(l.get(h)??0)+1;l.set(h,E),u.set(p.id,`${g}.${_}.${E}`)}return{byId:u}}function mc(e){return{table:e!==null?pc(e):null,originProjectId:e,cache:new Map}}function Ge(e,t){let s=e.cache.get(t);if(s)return s;let n=Vn(t);if(!n)return null;let r=e.table&&n.project_id===e.originProjectId?e.table.byId.get(t)??null:null,i={session_id:n.id,title:Zn(n),decimal:r,summary:qn(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,i),i}function gc(e,t){let n=m().prepare(`SELECT DISTINCT te.parent_session_id AS pid
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
930
1005
  FROM thread_edges te
931
1006
  WHERE te.session_id = ?
932
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let i of n){if(!i.pid)continue;let o=Ge(e,i.pid);o&&r.push(o)}return r}function _c(e,t){let n=m().prepare(`SELECT DISTINCT te.session_id AS sid
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
933
1008
  FROM thread_edges te
934
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let i of n){if(!i.sid)continue;let o=Ge(e,i.sid);o&&r.push(o)}return r}function Qn(e){let t=uc[e.linkType]??.5,s=pe(e.confidence),n=t*s,r=Jn(e.daysApart),i=e.embeddingCosine??.5,o=pe(e.pagerank);if(e.scoring==="pagerank")return pe(o);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?pe(n):pe(i);let a=.35*n+.2*r+.2*i+.25*o;return pe(a)}function pe(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function fc(e,t,s,n,r){let i=new Map;function o(a,c){if(a===c)return;let d=i.get(a);d||(d=new Set,i.set(a,d)),d.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 s)o(e,a.session_id);for(let a of s)o(a.session_id,e);for(let a of n)o(e,a.session_id);for(let a of n)o(a.session_id,e);if(r>1){let a=new Set([e]),c=new Set([e]);for(let d=1;d<r;d++){let l=new Set;for(let u of a){let p=i.get(u);if(p)for(let g of p){if(c.has(g))continue;let _=Ft(g).filter(h=>h.approved);for(let h of _)o(h.source_session_id,h.target_session_id),o(h.target_session_id,h.source_session_id);c.add(g),l.add(g)}}if(l.size===0)break;for(let u of l)a.add(u)}}return{edges:i}}function hc(e,t={}){let s=t.iterations??12,n=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let i=1/r.length,o=new Map(r.map(d=>[d,i]));for(let d=0;d<s;d++){let l=new Map(r.map(u=>[u,(1-n)/r.length]));for(let u of r){let p=e.edges.get(u);if(!p||p.size===0)continue;let g=(o.get(u)??0)/p.size;for(let _ of p)l.set(_,(l.get(_)??0)+n*g)}o=l}let a=0;for(let d of o.values())d>a&&(a=d);if(a<=0)return o;let c=new Map;for(let[d,l]of o)c.set(d,l/a);return c}var er=240;function tr(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function Ec(e){let t=e.decimal?`${e.decimal} `:"",s=e.session_id.slice(0,8),n="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${s})${n}`;if(e.summary){let i=tr(e.summary,er);return`${r}
935
- ${i}`}return r}function Tc(e,t,s){let n=[],r=[],i=0,o=e.decimal?`${e.decimal}: `:"",a=`# Neighborhood for ${e.session_id} (${o}${e.title})`;if(n.push(a),i+=Xe(a),e.summary){let c=tr(e.summary,er*4);n.push(c),i+=Xe(c)}n.push("");for(let c of t){if(c.refs.length===0)continue;let d=`## ${c.heading}`,l=Xe(d),u=[],p=0;for(let g of c.refs){let _=Ec(g),h=Xe(_);if(i+l+p+h>s){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}u.push(_),p+=h}if(u.length>0){n.push(d);for(let g of u)n.push(g);n.push(""),i+=l+p}}for(;n.length>0&&n[n.length-1]==="";)n.pop();return{bundle:n.join(`
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(`
936
1011
  `)+`
937
- `,budgetUsed:i,truncated:r}}function Sc(e,t,s,n,r,i){let o=[];for(let a of s){if(n&&!n.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 d=Ge(e,c);if(!d)continue;let l=Kn(t.started_at,d.started_at),u=Qn({confidence:a.confidence,linkType:a.link_type,daysApart:l,embeddingCosine:null,pagerank:i.get(c)??0,scoring:r});o.push({...d,score:u,evidence:`(suggestion, ${a.inferred_by}) confidence=${a.confidence.toFixed(2)} ${Math.round(l)}d apart`,link_type:a.link_type})}return o}function sr(e,t={}){let s=Math.max(100,Math.floor(t.budget??ac)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??cc)),i=t.includeWikiLinks??!0,o=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,c=Vn(e);if(!c)throw new Error(`session not found: ${e}`);let d=mc(c.project_id),l={session_id:c.id,title:Zn(c),decimal:d.table?.byId.get(c.id)??null,summary:qn(c.id),project:c.project,started_at:c.started_at};d.cache.set(c.id,l);let u=gc(d,e),p=_c(d,e),g=Ft(e).filter(O=>O.approved).filter(O=>!a||a.has(O.link_type)).filter(O=>i||O.link_type!=="wiki_link"),_=fc(e,g,u,p,r),h=hc(_),E=[],T=[],w=[],v=[];for(let O of g){let q=O.source_session_id===e?O.target_session_id:O.source_session_id,me=Ge(d,q);if(!me)continue;let ie=Kn(l.started_at,me.started_at),ze=Qn({confidence:O.confidence,linkType:O.link_type,daysApart:ie,embeddingCosine:null,pagerank:h.get(q)??0,scoring:n}),Je=Jn(ie),B=`${O.link_type} confidence=${O.confidence.toFixed(2)} recency=${Je.toFixed(2)} (${Math.round(ie)}d apart)`,ye={...me,score:ze,evidence:B,link_type:O.link_type};O.link_type==="citation"?E.push(ye):O.link_type==="similar"?T.push(ye):O.link_type==="wiki_link"?v.push(ye):w.push(ye)}if(o){let O=Pt({sourceSessionId:e,status:"pending",limit:100}),q=Pt({targetSessionId:e,status:"pending",limit:100}),me=[...O,...q],ie=new Set,ze=me.filter(B=>ie.has(B.id)?!1:(ie.add(B.id),!0)),Je=Sc(d,l,ze,a,n,h);for(let B of Je)B.link_type==="citation"?E.push(B):B.link_type==="similar"?T.push(B):B.link_type==="wiki_link"?v.push(B):w.push(B)}let L=(O,q)=>q.score-O.score;E.sort(L),T.sort(L),w.sort(L),v.sort(L);let R=Tc(l,[{heading:"Parents",refs:u},{heading:"Children",refs:p},{heading:"Citations (approved)",refs:E},{heading:"Similar sessions",refs:T},{heading:"Cousins (skill track + temporal)",refs:w},{heading:"Wiki links (manual)",refs:v}],s);return{origin:l,parents:u,children:p,citations:E,similar:T,cousins:w,wikiLinks:v,bundle:R.bundle,budgetUsed:R.budgetUsed,budgetRemaining:Math.max(0,s-R.budgetUsed),truncated:R.truncated}}import{readFileSync as ml}from"node:fs";import{dirname as gl,join as _l}from"node:path";var fl=(()=>{try{let e=gl(pl(import.meta.url));return _l(e,"..","..","package.json")}catch{return"package.json"}})(),hl=(()=>{try{return JSON.parse(ml(fl,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})();function El(){let e=process.env.RECALL_MCP_ALLOW_WRITES;return e==="1"||e==="true"}function A(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Vt(e){return{content:[{type:"text",text:e}]}}function G(e){return{content:[{type:"text",text:e}],isError:!0}}function Gr(e){if(e instanceof ue)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 Tl(){try{if((await We()).tier!=="pro"||!Se())return;await He(),process.stderr.write(`[mcp] embedder loaded
938
- `)}catch(e){process.stderr.write(`[mcp] embedder preload failed: ${Gr(e)}
939
- `)}}async function Sl(){if((await We()).tier!=="pro")return A({upgrade_required:!0,reason:"Semantic vector search requires Pro.",buy_url:"https://clauderecall.com/pro"});if(!Se())return A({error:"embedder_model_missing",reason:"Run `recall semantic install` to download the embedding model."});if(!Te().loaded)try{await He()}catch(t){return A({error:"embedder_load_failed",reason:Gr(t)})}return null}async function Xr(){try{return(await We()).tier!=="pro"||!Se()?!1:(Te().loaded||await He(),!0)}catch{return!1}}function bl(){let e=new dl({name:"claude-recall",version:hl}),t=El();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 n=m().prepare(`SELECT p.name,
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,
940
1016
  COUNT(s.id) AS session_count,
941
1017
  COALESCE(SUM(s.message_count), 0) AS message_count,
942
1018
  MAX(s.started_at) AS latest
943
1019
  FROM projects p
944
1020
  LEFT JOIN sessions s ON s.project_id = p.id
945
1021
  GROUP BY p.id
946
- ORDER BY MAX(COALESCE(s.started_at, '')) DESC`).all();return A(n)}),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:y.string().optional().describe("Substring match against project name or decoded filesystem path."),tag:y.string().optional().describe("Only sessions carrying this tag (leading # optional)."),since:y.string().optional().describe("Only sessions started at or after this ISO timestamp or YYYY-MM-DD date."),until:y.string().optional().describe("Only sessions started at or before this ISO timestamp or YYYY-MM-DD date."),limit:y.number().int().min(1).max(500).optional()}},async({project:s,tag:n,since:r,until:i,limit:o})=>{let a=m(),c={limit:o??100},d="s.message_count > 2";if(s&&(d+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",c.proj=`%${s}%`),r&&(d+=" AND s.started_at >= @since",c.since=r),i&&(d+=" AND s.started_at <= @until",c.until=/^\d{4}-\d{2}-\d{2}$/.test(i)?`${i}T23:59:59.999Z`:i),n){let p=se(n);p&&(d+=" AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag)",c.tag=p)}let u=a.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
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,
947
1023
  s.message_count, s.first_user_message, s.git_branch,
948
1024
  NULLIF(sa.alias, '') AS alias,
949
1025
  CASE WHEN sn.content IS NOT NULL AND sn.content != '' THEN 1 ELSE 0 END AS has_notes,
@@ -956,9 +1032,9 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
956
1032
  JOIN projects p ON p.id = s.project_id
957
1033
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
958
1034
  LEFT JOIN session_notes sn ON sn.session_id = s.id
959
- WHERE ${d}
1035
+ WHERE ${l}
960
1036
  ORDER BY COALESCE(s.started_at, '') DESC
961
- LIMIT @limit`).all(c).map(({tags_csv:p,...g})=>({...g,tags:p?p.split(","):[]}));return A(u)}),e.registerTool("list_tags",{title:"List tags",description:"Every tag currently applied to a session, with its count, most popular first.",inputSchema:{}},async()=>A(cs())),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:y.string().describe("Text to find. Supports inline `#tag-name` tokens to narrow to sessions with the tag."),project:y.string().optional().describe("Substring match against project name or path."),limit:y.number().int().min(1).max(200).optional(),mode:y.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:s,project:n,limit:r,mode:i})=>{let o=m(),a=s.trim();if(!a)return A({query:"",hits:[],tags:[],mode:i??"fused"});let c=i??"fused",d=a.split(/\s+/).filter(Boolean),l=d.filter(T=>T.startsWith("#")).map(se).filter(T=>!!T),p=d.filter(T=>!T.startsWith("#")).map(T=>`"${T.replace(/"/g,"")}"`).join(" "),g=Math.max(1,Math.min(200,r??30));if(!p&&l.length>0){let T=`
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=`
962
1038
  SELECT s.id AS session_id,
963
1039
  s.id AS message_uuid,
964
1040
  p.name AS project,
@@ -971,7 +1047,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
971
1047
  JOIN projects p ON p.id = s.project_id
972
1048
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
973
1049
  WHERE 1=1
974
- `,w={limit:g};return n&&(T+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",w.proj=`%${n}%`),l.forEach((v,L)=>{T+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${L})`,w[`tag_${L}`]=v}),T+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit",A({query:a,hits:o.prepare(T).all(w),tags:l})}if(!p)return A({query:a,hits:[],tags:l,mode:c});if(c==="semantic"){if(!await Xr())return A({query:a,hits:[],tags:l,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 w=(await vt(a,g)).map(v=>({session_id:v.sessionId,snippet:v.text,matched_via:"vector"}));return A({query:a,hits:w,tags:l,mode:"semantic"})}catch(T){return T instanceof re?A({query:a,hits:[],tags:l,mode:"semantic",error:"corpus_too_large",message:T.message,meta:{rowCount:T.rowCount,limit:T.limit,env:"RECALL_KNN_MAX_CORPUS"}}):A({query:a,hits:[],tags:l,mode:"semantic",error:"semantic_failed",reason:T instanceof Error?T.message:"vector search failed"})}}let _=`
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=`
975
1051
  SELECT m.session_id AS session_id,
976
1052
  m.uuid AS message_uuid,
977
1053
  p.name AS project,
@@ -986,7 +1062,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
986
1062
  JOIN projects p ON p.id = s.project_id
987
1063
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
988
1064
  WHERE messages_fts MATCH @fts
989
- `,h={fts:p,limit:g};n&&(_+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",h.proj=`%${n}%`),l.forEach((T,w)=>{_+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${w})`,h[`tag_${w}`]=T}),_+=" ORDER BY bm25(messages_fts) LIMIT @limit";let E=o.prepare(_).all(h);if(c==="fts")return A({query:a,hits:E,tags:l,mode:"fts"});if(await Xr())try{let T=await vt(a,g),w=E.map(R=>({id:String(R.session_id),data:R,lane:"bm25"})),v=T.map(R=>({id:R.sessionId,data:{session_id:R.sessionId,snippet:R.text,matched_via:"vector"},lane:"vector"})),P=Mn([w,v]).slice(0,g).map(R=>({...R.data,session_id:R.id,rrf_score:R.score,lanes:R.lanes}));return A({query:a,hits:P,tags:l,fusion:"rrf",mode:"fused"})}catch(T){if(T instanceof re)return A({query:a,hits:E,tags:l,mode:"fused",error:"corpus_too_large",message:T.message,meta:{rowCount:T.rowCount,limit:T.limit,env:"RECALL_KNN_MAX_CORPUS"}})}return A({query:a,hits:E,tags:l,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:y.string().uuid().describe("Session UUID to find similar sessions for."),limit:y.number().int().min(1).max(50).optional().describe("Max results (default 10)."),min_cosine:y.number().min(0).max(1).optional().describe("Minimum cosine similarity threshold (default 0.65).")}},async({session_id:s,limit:n,min_cosine:r})=>{let i=await Sl();if(i)return i;try{let o=await Dn(s,n??10,r??.65);return A({session_id:s,similar:o})}catch(o){return o instanceof re?A({session_id:s,similar:[],error:"corpus_too_large",message:o.message,meta:{rowCount:o.rowCount,limit:o.limit,env:"RECALL_KNN_MAX_CORPUS"}}):G(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 s=Te(),n=Fn(),r=Se();return A({embedder:s,worker:n,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:y.string().describe("Session id (full UUID or 8+-character prefix).")}},async({id:s})=>{let n=X(s);if(!n)return G(`session not found or prefix ambiguous: ${s}`);let r=m(),i=r.prepare(`SELECT s.id, s.project_id, s.started_at, s.ended_at,
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,
990
1066
  s.message_count, s.user_message_count, s.assistant_message_count,
991
1067
  s.first_user_message, s.git_branch, s.version, s.indexed_at,
992
1068
  p.name AS project_name,
@@ -994,13 +1070,15 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
994
1070
  FROM sessions s
995
1071
  JOIN projects p ON p.id = s.project_id
996
1072
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
997
- WHERE s.id = ?`).get(n);if(!i)return G(`session metadata missing for ${n}`);let o=Ae(n),a=r.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
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
998
1074
  FROM messages WHERE session_id = ?
999
- ORDER BY COALESCE(timestamp, ''), rowid`).all(n);return A({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:y.string().describe("Session id (full UUID or 8+-character prefix)."),mode:y.enum(["condensed","full"]).optional().describe("`condensed` (default) strips tool-call JSON; `full` keeps everything."),includeSidechain:y.boolean().optional().describe("Include subagent / sidechain messages (default false)."),prelude:y.string().max(1e4).optional().describe("Optional header prepended above the transcript (max 10 000 chars)."),since:y.string().optional().describe("Only messages at or after this ISO timestamp.")}},async({id:s,mode:n,includeSidechain:r,prelude:i,since:o})=>{let a=X(s);if(!a)return G(`session not found or prefix ambiguous: ${s}`);let c=m(),d=c.prepare(`SELECT s.id, p.name AS project_name,
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,
1000
1076
  s.started_at, s.ended_at, s.message_count, s.git_branch
1001
1077
  FROM sessions s JOIN projects p ON p.id = s.project_id
1002
- WHERE s.id = ?`).get(a);if(!d)return G(`session metadata missing for ${a}`);let l=c.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
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
1003
1079
  FROM messages WHERE session_id = ?
1004
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a),u=os(d,l,{mode:n??"condensed",includeSidechain:r??!1,prelude:i??null,since:o??null}),p=o?"since":n??"condensed";return vn(a,Math.ceil(u.length/4),p,"mcp"),Vt(u)}),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:y.string().describe("Session UUID or 8+-character prefix."),budget:y.number().int().min(100).max(5e4).optional().describe("Token budget for the assembled bundle. Default 4000."),scoring:y.enum(["pagerank","embedding-rerank","hybrid"]).optional().describe("Scoring mode (default hybrid)."),edge_types:y.array(y.enum(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"])).optional().describe("Restrict to certain link types. Default: all approved types."),max_depth:y.number().int().min(1).max(5).optional().describe("Pagerank traversal depth on the local subgraph (default 2)."),include_wiki_links:y.boolean().optional().describe("Include manual wiki_link rows. Default true."),include_suggestions:y.boolean().optional().describe("Surface pending session_link_suggestions. Debug only; default false."),format:y.enum(["markdown","json"]).optional().describe("markdown (default) returns the bundle as text; json returns the full NeighborhoodResult.")}},async({session_id:s,budget:n,scoring:r,edge_types:i,max_depth:o,include_wiki_links:a,include_suggestions:c,format:d})=>{let l=X(s);if(!l)return G(`session not found or prefix ambiguous: ${s}`);try{let u=sr(l,{budget:n,scoring:r,edgeTypes:i,maxDepth:o,includeWikiLinks:a,includeSuggestions:c});return d==="json"?A(u):Vt(u.bundle)}catch(u){return G(u instanceof Error?u.message:String(u))}}),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:s}=await Promise.resolve().then(()=>(Hr(),jr));return A(s())}),On(e),t){let s=Number(process.env.RECALL_MCP_RATE_LIMIT),n=Number.isFinite(s)&&s>0?s:mt,r=m(),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 Q(n);for(let c of o){let d=new Date(c.at).getTime();if(Number.isFinite(d))try{a.consume(d)}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:y.boolean().optional().describe("Only sessions with zero tags (default false)."),project:y.string().optional().describe("Exact project name match."),collectionId:y.string().optional().describe("Only sessions in this collection."),limit:y.number().int().min(1).max(200).optional()}},async c=>{let d=st();if(!d.enabled)return G("auto-tagging is disabled; enable it in Recall settings before scanning");let l=xe({untaggedOnly:c.untaggedOnly,project:c.project,collectionId:c.collectionId,limit:c.limit??50});return A({count:l.length,sessions:l,guidance:`Produce ${d.minTagsPerSession}-${d.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:y.string().describe("Full session UUID or 8+-character prefix."),tags:y.array(y.string()).min(1).max(10).describe("Tags to add. Normalized server-side (lowercase, hyphens, strip #).")}},async({sessionId:c,tags:d})=>{if(!st().enabled)return G("auto-tagging is disabled; enable it in Recall settings before writing tags");let u=X(c);if(!u)return G(`session not found or prefix ambiguous: ${c}`);try{let p=await I({tool:"apply_tags",args:{sessionId:u,tags:d},limiter:a,run:()=>{let g=[],_=[];for(let h of d)try{let{tag:E,added:T}=Le(u,h);T?g.push(E):_.push({tag:E,reason:"already present"})}catch(E){_.push({tag:h,reason:E instanceof Error?E.message:String(E)})}return{sessionId:u,applied:g,skipped:_}}});return A(p)}catch(p){return p instanceof Error&&p.message.startsWith("SQLITE_")?G("database constraint error"):G(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:y.boolean().optional().describe("Also run VACUUM. Requires the daemon to be stopped.")}},async({vacuum:c})=>{let{runOptimize:d}=await Promise.resolve().then(()=>(Wr(),Br)),l=process.stdout.write.bind(process.stdout),u="";process.stdout.write=p=>(u+=typeof p=="string"?p:Buffer.from(p).toString("utf-8"),!0);try{await d({vacuum:!!c,json:!0})}finally{process.stdout.write=l}try{return A(JSON.parse(u.trim()))}catch{return Vt(u.trim())}}),Ts(e,{limiter:a}),xn(e,{limiter:a}),console.error(`[claude-recall-mcp] MCP writes ENABLED \u2014 write tools registered (rate limit ${n}/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 s of us)e.registerPrompt(s.name,{title:s.title,description:s.description,argsSchema:s.argsSchema},async n=>({messages:[{role:"user",content:{type:"text",text:s.build(n)}}]}));return e}async function yl(){let e=bl();await Tl();let t=new ul;await e.connect(t);let s=!1,n=async r=>{if(!s){s=!0,process.stderr.write(`[claude-recall-mcp] shutting down: ${r}
1005
- `);try{await e.close()}catch(i){let o=i instanceof Error?i.message:String(i);process.stderr.write(`[claude-recall-mcp] server.close() failed: ${o}
1006
- `)}rs(),process.exit(0)}};process.on("SIGINT",()=>{n("SIGINT")}),process.on("SIGTERM",()=>{n("SIGTERM")}),is({onShutdown:()=>n("parent death")})}var wl=(()=>{try{let e=process.argv[1];return e?import.meta.url===new URL(`file://${e}`).href:!1}catch{return!1}})();wl&&yl().catch(e=>{console.error("[claude-recall-mcp] fatal:",e),process.exit(1)});export{bl as buildServer};
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};