@clauderecallhq/cli 0.77.3 → 0.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /* Claude Recall (proprietary). See LICENSE for terms. */
3
- var xm=Object.defineProperty;var ae=(e,t)=>()=>(e&&(t=e(e=0)),t);var xn=(e,t)=>{for(var n in t)xm(e,n,{get:t[n],enumerable:!0})};import{homedir as Fi}from"node:os";import{join as rr,basename as k0}from"node:path";import{existsSync as Nm,mkdirSync as Om,chmodSync as Lm,readdirSync as x0,statSync as N0}from"node:fs";function z(){Nm(W)||Om(W,{recursive:!0,mode:448}),process.platform!=="win32"&&Lm(W,448)}var Ut,W,Bt,ee=ae(()=>{"use strict";Ut=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:rr(Fi(),".claude","projects"),W=process.env.RECALL_HOME?process.env.RECALL_HOME:rr(Fi(),".recall"),Bt=rr(W,"db.sqlite")});import{createRequire as eg}from"node:module";var tg,ng,sg,ar,cr,Ki,Vi=ae(()=>{"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)}}tg=eg(import.meta.url),ng=["node","sqlite"].join(":"),sg=tg(ng),ar=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)}},cr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new sg.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new ar(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)}},Ki=cr});function Qi(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(S=>S.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[S,T]of s)n.has(S)||e.exec(`ALTER TABLE sessions ADD COLUMN ${S} ${T}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(S=>S.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[S,T]of a)o.has(S)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${S} ${T}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(S=>S.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[S,T]of d)u.has(S)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${S} ${T}`);let m=e.prepare("PRAGMA table_info(threads)").all();new Set(m.map(S=>S.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let h=e.prepare("PRAGMA table_info(thread_folders)").all(),b=new Set(h.map(S=>S.name));b.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),b.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
3
+ var Vg=Object.defineProperty;var re=(e,t)=>()=>(e&&(t=e(e=0)),t);var _t=(e,t)=>{for(var n in t)Vg(e,n,{get:t[n],enumerable:!0})};import{homedir as Ca}from"node:os";import{join as Pr,basename as kA}from"node:path";import{existsSync as Qg,mkdirSync as Zg,chmodSync as ef,readdirSync as xA,statSync as NA}from"node:fs";function V(){Qg(W)||Zg(W,{recursive:!0,mode:448}),process.platform!=="win32"&&ef(W,448)}var nn,W,st,ne=re(()=>{"use strict";nn=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Pr(Ca(),".claude","projects"),W=process.env.RECALL_HOME?process.env.RECALL_HOME:Pr(Ca(),".recall"),st=Pr(W,"db.sqlite")});import{createRequire as kf}from"node:module";var Af,xf,Nf,Ur,Hr,Kn,Br=re(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...n){let s=n[0];return t==="warning"&&s instanceof Error&&s.name==="ExperimentalWarning"&&/SQLite/i.test(s.message)?!1:e(t,...n)}}Af=kf(import.meta.url),xf=["node","sqlite"].join(":"),Nf=Af(xf),Ur=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let n=t.length===0?this.inner.run():this.inner.run(...t);return{changes:n.changes,lastInsertRowid:n.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},Hr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new Nf.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new Ur(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,n={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(n.simple){let s=this.inner.prepare(`PRAGMA ${t}`).get();return s&&typeof s=="object"?Object.values(s)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...s)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...s);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,n){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),n===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,n)}},Kn=Hr});function qa(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(k=>k.name)),s=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[k,M]of s)n.has(k)||e.exec(`ALTER TABLE sessions ADD COLUMN ${k} ${M}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(k=>k.name)),a=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[k,M]of a)o.has(k)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${k} ${M}`);let c=e.prepare("PRAGMA table_info(session_notes)").all(),u=new Set(c.map(k=>k.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[k,M]of d)u.has(k)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${k} ${M}`);let p=e.prepare("PRAGMA table_info(threads)").all();new Set(p.map(k=>k.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let f=e.prepare("PRAGMA table_info(thread_folders)").all(),S=new Set(f.map(k=>k.name));S.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),S.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
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 xm=Object.defineProperty;var ae=(e,t)=>()=>(e&&(t=e(e=0)),t);var xn=(e,t)=>{
12
12
  );
13
13
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_session ON message_embeddings(session_id);
14
14
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_generated ON message_embeddings(generated_at DESC);
15
- `)}var Zi,ea=ae(()=>{"use strict";Zi=`
15
+ `);let y=e.prepare("PRAGMA table_info(projects)").all(),b=new Set(y.map(k=>k.name)),R=[["repo_root","TEXT"],["main_repo","TEXT"],["is_repo","INTEGER NOT NULL DEFAULT 0"],["is_worktree","INTEGER NOT NULL DEFAULT 0"]];for(let[k,M]of R)b.has(k)||e.exec(`ALTER TABLE projects ADD COLUMN ${k} ${M}`)}var Wa,Xa=re(()=>{"use strict";Wa=`
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,9 +730,10 @@ 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 ta from"sqlite-vec";function f(){if(ge)return ge;z(),ge=new Ki(Bt),ta.load(ge),ge.pragma("cache_size = -64000"),ge.pragma("mmap_size = 268435456"),ge.pragma("temp_store = MEMORY"),ge.pragma("busy_timeout = 5000"),ge.pragma("journal_size_limit = 67108864"),ge.pragma("wal_autocheckpoint = 1000"),ge.exec(Zi),Qi(ge);try{ge.exec("PRAGMA optimize")}catch{}return ge}var ge,B=ae(()=>{"use strict";Vi();ee();ea();ge=null});import{existsSync as lg}from"node:fs";import{dirname as ga}from"node:path";import{fileURLToPath as ug}from"node:url";function Ln(){if(On)return On;let e=ga(ug(import.meta.url));for(;!lg(`${e}/package.json`);){let t=ga(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return On=e,On}var On,ur=ae(()=>{"use strict";On=null});var _a,fa=ae(()=>{"use strict";_a="BAAI/bge-base-en-v1.5"});var Ea={};xn(Ea,{EmbedderUnavailableError:()=>Xt,embed:()=>at,embedQuery:()=>Eg,getEmbedderStatus:()=>_e,loadEmbedder:()=>Ue,unloadEmbedder:()=>bg});import{Worker as pg}from"node:worker_threads";import{join as mg}from"node:path";import{existsSync as gg}from"node:fs";function _g(){return mg(Ln(),"dist","daemon","embedder-worker.js")}function fg(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(`
672
- `)}function ha(e){for(let t of qt.values())t.reject(e);qt.clear()}function hg(){if(De)return De;let e=_g();if(!gg(e))throw new Xt({kind:"bundle-missing",detail:"embedder-worker bundle not found \u2014 run `npm run build:cli` to emit it",path:e});let t=new pg(e);return t.on("message",n=>{let s=qt.get(n.id);s&&(qt.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));ha(s),De=null,xe=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),ha(new Error(`embedder worker exited with code ${n}`))),De=null,xe=!1}),De=t,t}function Cn(e){return new Promise((t,n)=>{let s;try{s=hg()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}qt.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function vn(){return dr=dr+1>>>0,String(dr)}function pr(e){if(!e.ok)throw e.unavailable?new Xt({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 Ue(){if(!(xe&&De))try{pr(await Cn({id:vn(),type:"load"})),xe=!0}catch(e){throw xe=!1,e}}function _e(){return{loaded:xe,modelId:_a,dim:768}}async function at(e){if(!xe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return pr(await Cn({id:vn(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function Eg(e){if(!xe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=pr(await Cn({id:vn(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function bg(){if(!De){xe=!1;return}try{await Cn({id:vn(),type:"unload"})}catch{}xe=!1;let e=De;De=null;try{await e.terminate()}catch{}}var De,qt,dr,xe,Xt,ct=ae(()=>{"use strict";ur();fa();De=null,qt=new Map,dr=0,xe=!1,Xt=class extends Error{kind;path;underlying;cause;constructor(t){super(fg(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});import{writeFileSync as S_}from"node:fs";import{join as T_}from"node:path";function Ze(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function ut(e,t){let n=Ze(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=f(),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)})(),Ba(),{tag:n,added:!0})}function Ua(e,t){let n=Ze(t);if(!n)return{tag:"",removed:!1};let s=f(),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)})(),Ba(),{tag:n,removed:!0}):{tag:n,removed:!1}}function zt(e){return f().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function dt(){return f().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
673
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function Ba(){try{z();let e=f(),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};S_(y_,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var y_,pt=ae(()=>{"use strict";B();ee();y_=T_(W,"tags.json")});function w_(e,t){let n=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)s.add(Math.floor(o*r));return Array.from(s).sort((o,a)=>o-a).slice(0,t).map(o=>n[o])}function mt(e){let t=f(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{n[`sid_${u}`]=c})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
733
+ `});import*as Ja from"sqlite-vec";function h(){if(fe)return fe;V(),fe=new Kn(st),Ja.load(fe),fe.pragma("cache_size = -64000"),fe.pragma("mmap_size = 268435456"),fe.pragma("temp_store = MEMORY"),fe.pragma("busy_timeout = 5000"),fe.pragma("journal_size_limit = 67108864"),fe.pragma("wal_autocheckpoint = 1000"),fe.exec(Wa),qa(fe);try{fe.exec("PRAGMA optimize")}catch{}return fe}var fe,q=re(()=>{"use strict";Br();ne();Xa();fe=null});import{existsSync as Mf}from"node:fs";import{dirname as oc}from"node:path";import{fileURLToPath as jf}from"node:url";function ht(){if(zn)return zn;let e=oc(jf(import.meta.url));for(;!Mf(`${e}/package.json`);){let t=oc(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return zn=e,zn}var zn,Vn=re(()=>{"use strict";zn=null});function Df(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) - or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
734
+ `)}var Qn,xe,on=re(()=>{"use strict";Qn="BAAI/bge-base-en-v1.5",xe=class extends Error{kind;path;underlying;cause;constructor(t){super(Df(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var ts={};_t(ts,{embed:()=>qf,embedQuery:()=>Xf,getEmbedderStatus:()=>Wf,loadEmbedder:()=>Bf,unloadEmbedder:()=>Jf});import{Worker as Pf}from"node:worker_threads";import{join as Ff}from"node:path";import{existsSync as $f}from"node:fs";function Uf(){return Ff(ht(),"dist","daemon","embedder-worker.js")}function ic(e){for(let t of an.values())t.reject(e);an.clear()}function Hf(){if(Ue)return Ue;let e=Uf();if(!$f(e))throw new xe({kind:"bundle-missing",detail:"embedder-worker bundle not found - run `npm run build:cli` to emit it",path:e});let t=new Pf(e);return t.on("message",n=>{let s=an.get(n.id);s&&(an.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker] thread error:",n);let s=n instanceof Error?n:new Error(String(n));ic(s),Ue=null,Ne=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),ic(new Error(`embedder worker exited with code ${n}`))),Ue=null,Ne=!1}),Ue=t,t}function Zn(e){return new Promise((t,n)=>{let s;try{s=Hf()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}an.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function es(){return qr=qr+1>>>0,String(qr)}function Jr(e){if(!e.ok)throw e.unavailable?new xe({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function Bf(){if(!(Ne&&Ue))try{Jr(await Zn({id:es(),type:"load"})),Ne=!0}catch(e){throw Ne=!1,e}}function Wf(){return{loaded:Ne,modelId:Qn,dim:768}}async function qf(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return Jr(await Zn({id:es(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function Xf(e){if(!Ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=Jr(await Zn({id:es(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function Jf(){if(!Ue){Ne=!1;return}try{await Zn({id:es(),type:"unload"})}catch{}Ne=!1;let e=Ue;Ue=null;try{await e.terminate()}catch{}}var Ue,an,qr,Ne,ac=re(()=>{"use strict";Vn();on();Ue=null,an=new Map,qr=0,Ne=!1});var Kr={};_t(Kr,{LLAMACPP_MODEL_ID:()=>lc,MODEL_ID:()=>Qn,embed:()=>e_,embedQuery:()=>t_,getEmbedderStatus:()=>Zf,loadEmbedder:()=>Qf,unloadEmbedder:()=>n_});import{Worker as Gf}from"node:worker_threads";import{join as Yf}from"node:path";import{existsSync as Kf}from"node:fs";function zf(){return Yf(ht(),"dist","daemon","embedder-worker-llamacpp.js")}function cc(e){for(let t of cn.values())t.reject(e);cn.clear()}function Vf(){if(He)return He;let e=zf();if(!Kf(e))throw new xe({kind:"bundle-missing",detail:"embedder-worker-llamacpp bundle not found - run `npm run build:cli` to emit it",path:e});let t=new Gf(e);return t.on("message",n=>{let s=cn.get(n.id);s&&(cn.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker-llamacpp] thread error:",n);let s=n instanceof Error?n:new Error(String(n));cc(s),He=null,Oe=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),cc(new Error(`embedder worker exited with code ${n}`))),He=null,Oe=!1}),He=t,t}function ns(e){return new Promise((t,n)=>{let s;try{s=Vf()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}cn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function ss(){return Gr=Gr+1>>>0,String(Gr)}function Yr(e){if(!e.ok)throw e.unavailable?new xe({kind:"platform-unsupported",detail:"embedder worker reports the llama.cpp runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function Qf(){if(!(Oe&&He))try{Yr(await ns({id:ss(),type:"load"})),Oe=!0}catch(e){throw Oe=!1,e}}function Zf(){return{loaded:Oe,modelId:lc,dim:768}}async function e_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return Yr(await ns({id:ss(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function t_(e){if(!Oe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=Yr(await ns({id:ss(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function n_(){if(!He){Oe=!1;return}try{await ns({id:ss(),type:"unload"})}catch{}Oe=!1;let e=He;He=null;try{await e.terminate()}catch{}}var lc,He,cn,Gr,Oe,uc=re(()=>{"use strict";Vn();on();on();lc="BAAI/bge-base-en-v1.5-gguf-q8_0";He=null,cn=new Map,Gr=0,Oe=!1});var dc={};_t(dc,{EmbedderUnavailableError:()=>xe,embed:()=>Et,embedQuery:()=>r_,getEmbedderStatus:()=>pe,loadEmbedder:()=>Ge,unloadEmbedder:()=>o_});function s_(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?Kr:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
735
+ `),ts)}var ln,Ge,pe,Et,r_,o_,bt=re(()=>{"use strict";ac();uc();on();ln=s_(),Ge=ln.loadEmbedder,pe=ln.getEmbedderStatus,Et=ln.embed,r_=ln.embedQuery,o_=ln.unloadEmbedder});import{writeFileSync as ih}from"node:fs";import{join as ah}from"node:path";function it(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function yt(e,t){let n=it(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=h(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?{tag:n,added:!1}:(s.transaction(()=>{s.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,n,r),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,n,r)})(),Mc(),{tag:n,added:!0})}function Ic(e,t){let n=it(t);if(!n)return{tag:"",removed:!1};let s=h(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?(s.transaction(()=>{s.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,n),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,n,r)})(),Mc(),{tag:n,removed:!0}):{tag:n,removed:!1}}function mn(e){return h().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function Tt(){return h().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
736
+ GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function Mc(){try{V();let e=h(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),n=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),s={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:n};ih(ch,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var ch,wt=re(()=>{"use strict";q();ne();ch=ah(W,"tags.json")});function lh(e,t){let n=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)s.add(Math.floor(o*r));return Array.from(s).sort((o,a)=>o-a).slice(0,t).map(o=>n[o])}function Rt(e){let t=h(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let a=e.sessionIds.map((c,u)=>`@sid_${u}`).join(", ");r+=` AND s.id IN (${a})`,e.sessionIds.forEach((c,u)=>{n[`sid_${u}`]=c})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
674
737
  NULLIF(sa.alias, '') AS alias,
675
738
  COALESCE(s.first_user_message, '') AS first_user_message
676
739
  FROM sessions s
@@ -680,15 +743,15 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
680
743
  ORDER BY COALESCE(s.started_at, '') DESC
681
744
  LIMIT @limit`).all(n).map(a=>{let c=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
682
745
  FROM messages WHERE session_id = ?
683
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=w_(c,5).map(m=>`${m.role}: ${m.content_text.slice(0,400)}`).join(`
746
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a.id),d=lh(c,5).map(p=>`${p.role}: ${p.content_text.slice(0,400)}`).join(`
684
747
  ---
685
- `);return{id:a.id,project:a.project,git_branch:a.git_branch,alias:a.alias,first_user_message:a.first_user_message,message_sample:d,current_tags:zt(a.id)}})}var Fn=ae(()=>{"use strict";B();pt()});import{z as be}from"zod";function yr(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
686
- `)}function A_(e){let t=e.mode==="detailed";return[`Summarize Claude Recall session ${e.sessionId} using the MCP tools available to you.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "condensed" to get the transcript as markdown.`,t?"2. Write a 1-paragraph overview (\u22643 sentences) of what this session was for, then 5-8 bullet points covering:":"2. Write 3-5 bullet points covering:"," - What was accomplished (shipped, decided, learned)"," - What was tried and abandoned"," - Any explicit open questions or follow-ups","","Rules:",'- Be concrete. Name files, functions, and decisions. Avoid vague "discussed X".',"- If nothing was actually shipped or decided, say so plainly.",'- Reply with just the summary \u2014 no preamble, no "Here is the summary:".'].join(`
687
- `)}function N_(e){return[`Extract every architectural or product decision made in Claude Recall session ${e.sessionId}.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "full" (so tool I/O is included \u2014 sometimes the decision lives in a commit message or diff).`,"2. For each distinct decision, emit one block:",""," - **Decision:** one sentence."," - **Why:** the stated rationale (quote if possible).",' - **Alternatives considered:** bullet list, or "none mentioned".',' - **Where it landed:** file path / function name / commit SHA if identifiable, otherwise "TBD".',"","Rules:","- Include only decisions that were actually made, not ideas merely discussed.","- If an alternative was rejected, list it with a one-line reason.","- Group related decisions under a short heading when useful.",'- If the session made zero real decisions, reply exactly with: "No decisions made in this session."',"- No preamble, no closing, just the decision blocks."].join(`
688
- `)}function L_(e){let t=e.limit??5;return[`Find ${t} Recall sessions most similar to session ${e.sessionId}.`,"",`1. Call \`get_session\` with id "${e.sessionId}" \u2014 note its alias, first user message, tags, and git branch.`,'2. Derive 2-3 short search queries from that content (topic words, library names, error strings \u2014 NOT generic words like "fix" or "add").',`3. Call \`search\` once per query (limit: ${Math.max(5,t*2)}). Dedupe hits by session_id.`,"4. Also call `list_sessions` with the same tag(s) if the target session has any (pick the most specific tag).","5. Union the results. Exclude the target session itself. Rank by a mix of:"," - Shared tags (strongest signal)"," - Matching search hits across multiple queries"," - Recency as a tiebreaker","",`6. Return the top ${t} as a numbered list. For each:`," - **<short_id> \xB7 <project>** \u2014 <alias or first_user_message truncated>",' - One sentence on WHY it is similar (not a generic "same topic" \u2014 be specific).',"","If fewer than 2 genuinely-similar sessions exist, say so rather than padding with weak matches.","No preamble. Just the ranked list."].join(`
689
- `)}function Ha(e){return wr.find(t=>t.name===e)}var R_,k_,x_,O_,C_,v_,I_,j_,wr,Rr=ae(()=>{"use strict";R_={project:be.string().optional().describe("Exact project name match (optional)."),collectionId:be.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:be.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:be.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:be.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:be.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:be.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};k_={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:be.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};x_={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};O_={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:be.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};C_={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:R_,build:yr,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},v_={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:k_,build:A_,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},I_={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:x_,build:N_,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},j_={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:O_,build:L_,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},wr=[C_,v_,I_,j_]});function Pn(e,t){let n=Kt.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}function Wa(e,t){let n=Kt.get(e);return n||(n=new Set,Kt.set(e,n)),n.add(t),()=>{let s=Kt.get(e);s&&(s.delete(t),s.size===0&&Kt.delete(e))}}var Kt,kr=ae(()=>{"use strict";Kt=new Map});import{existsSync as M_,statSync as D_}from"node:fs";import{delimiter as F_,join as P_}from"node:path";function gt(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(F_).filter(Boolean),n=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let s of t)for(let r of n){let o=P_(s,r);try{if(M_(o)&&D_(o).isFile())return o}catch{}}return null}function $n(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var Un=ae(()=>{"use strict"});var et={};xn(et,{_resetClaudePathCacheForTests:()=>H_,buildScanPrompt:()=>Xa,isClaudeCliAvailable:()=>pe,runClaudeCliScan:()=>Ar,spawnClaudePrompt:()=>Qe});import{spawn as $_}from"node:child_process";function qa(){if(_t!==void 0&&Vt!==void 0)return{path:_t,available:Vt};let e=gt("claude");return _t=e??"claude",Vt=e!==null,{path:_t,available:Vt}}function B_(){return qa().path}function pe(){return qa().available}function H_(){_t=void 0,Vt=void 0}function Xa(e){return yr({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 W_(e,t){let n=t.get(e);return n||e.slice(0,8)}function q_(e){try{return mt(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 X_(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return o=>{let a=o.trim();if(!a.startsWith("{"))return;let c;try{c=JSON.parse(a)}catch{return}if(!c||typeof c!="object")return;let u=c;if(!(u.type!=="assistant"||!u.message?.content))for(let d of u.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let m=d.input,h=typeof m?.sessionId=="string"?m.sessionId:null;!h||r.has(h)||(r.add(h),Pn(t,{type:"progress",current:r.size,total:n,sessionId:h,sessionLabel:W_(h,s)}))}}}function J_(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
748
+ `);return{id:a.id,project:a.project,git_branch:a.git_branch,alias:a.alias,first_user_message:a.first_user_message,message_sample:d,current_tags:mn(a.id)}})}var cs=re(()=>{"use strict";q();wt()});import{z as be}from"zod";function oo(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
749
+ `)}function ph(e){let t=e.mode==="detailed";return[`Summarize Claude Recall session ${e.sessionId} using the MCP tools available to you.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "condensed" to get the transcript as markdown.`,t?"2. Write a 1-paragraph overview (\u22643 sentences) of what this session was for, then 5-8 bullet points covering:":"2. Write 3-5 bullet points covering:"," - What was accomplished (shipped, decided, learned)"," - What was tried and abandoned"," - Any explicit open questions or follow-ups","","Rules:",'- Be concrete. Name files, functions, and decisions. Avoid vague "discussed X".',"- If nothing was actually shipped or decided, say so plainly.",'- Reply with just the summary \u2014 no preamble, no "Here is the summary:".'].join(`
750
+ `)}function gh(e){return[`Extract every architectural or product decision made in Claude Recall session ${e.sessionId}.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "full" (so tool I/O is included \u2014 sometimes the decision lives in a commit message or diff).`,"2. For each distinct decision, emit one block:",""," - **Decision:** one sentence."," - **Why:** the stated rationale (quote if possible).",' - **Alternatives considered:** bullet list, or "none mentioned".',' - **Where it landed:** file path / function name / commit SHA if identifiable, otherwise "TBD".',"","Rules:","- Include only decisions that were actually made, not ideas merely discussed.","- If an alternative was rejected, list it with a one-line reason.","- Group related decisions under a short heading when useful.",'- If the session made zero real decisions, reply exactly with: "No decisions made in this session."',"- No preamble, no closing, just the decision blocks."].join(`
751
+ `)}function _h(e){let t=e.limit??5;return[`Find ${t} Recall sessions most similar to session ${e.sessionId}.`,"",`1. Call \`get_session\` with id "${e.sessionId}" \u2014 note its alias, first user message, tags, and git branch.`,'2. Derive 2-3 short search queries from that content (topic words, library names, error strings \u2014 NOT generic words like "fix" or "add").',`3. Call \`search\` once per query (limit: ${Math.max(5,t*2)}). Dedupe hits by session_id.`,"4. Also call `list_sessions` with the same tag(s) if the target session has any (pick the most specific tag).","5. Union the results. Exclude the target session itself. Rank by a mix of:"," - Shared tags (strongest signal)"," - Matching search hits across multiple queries"," - Recency as a tiebreaker","",`6. Return the top ${t} as a numbered list. For each:`," - **<short_id> \xB7 <project>** \u2014 <alias or first_user_message truncated>",' - One sentence on WHY it is similar (not a generic "same topic" \u2014 be specific).',"","If fewer than 2 genuinely-similar sessions exist, say so rather than padding with weak matches.","No preamble. Just the ranked list."].join(`
752
+ `)}function jc(e){return io.find(t=>t.name===e)}var uh,dh,mh,fh,hh,Eh,bh,Sh,io,ao=re(()=>{"use strict";uh={project:be.string().optional().describe("Exact project name match (optional)."),collectionId:be.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:be.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:be.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:be.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:be.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:be.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};dh={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:be.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};mh={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};fh={sessionId:be.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:be.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};hh={name:"auto_tag_sessions",title:"Auto-tag Recall sessions",description:"Have the agent auto-tag Recall sessions using the Recall MCP tools. Scope can be restricted to a project, collection, or single session.",argsSchema:uh,build:oo,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Eh={name:"summarize_session",title:"Summarize a session",description:"Produce a concise, concrete summary of one session \u2014 what shipped, what was tried, what's still open.",argsSchema:dh,build:ph,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},bh={name:"extract_decisions",title:"Extract architectural decisions",description:"Scan a session and emit one structured block per architectural / product decision: what, why, alternatives, where it landed.",argsSchema:mh,build:gh,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Sh={name:"find_similar_sessions",title:"Find similar sessions",description:"Given a session, find other sessions that touched the same topic / library / error \u2014 ranked with reasons.",argsSchema:fh,build:_h,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},io=[hh,Eh,bh,Sh]});function ls(e,t){let n=gn.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}function Dc(e,t){let n=gn.get(e);return n||(n=new Set,gn.set(e,n)),n.add(t),()=>{let s=gn.get(e);s&&(s.delete(t),s.size===0&&gn.delete(e))}}var gn,co=re(()=>{"use strict";gn=new Map});import{existsSync as yh,statSync as Th}from"node:fs";import{delimiter as wh,join as Rh}from"node:path";function kt(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(wh).filter(Boolean),n=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let s of t)for(let r of n){let o=Rh(s,r);try{if(yh(o)&&Th(o).isFile())return o}catch{}}return null}function us(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var ds=re(()=>{"use strict"});var ct={};_t(ct,{_resetClaudePathCacheForTests:()=>Nh,buildScanPrompt:()=>Fc,isClaudeCliAvailable:()=>me,runClaudeCliScan:()=>lo,spawnClaudePrompt:()=>at});import{spawn as kh}from"node:child_process";function Pc(){if(At!==void 0&&fn!==void 0)return{path:At,available:fn};let e=kt("claude");return At=e??"claude",fn=e!==null,{path:At,available:fn}}function xh(){return Pc().path}function me(){return Pc().available}function Nh(){At=void 0,fn=void 0}function Fc(e){return oo({project:e.project,collectionId:e.collectionId,sessionId:e.sessionIds&&e.sessionIds.length===1?e.sessionIds[0]:void 0,untaggedOnly:e.untaggedOnly,limit:e.limit})}function Oh(e,t){let n=t.get(e);return n||e.slice(0,8)}function Ch(e){try{return Rt(e).map(n=>({id:n.id,label:n.alias&&n.alias.trim().length>0?n.alias:n.first_user_message&&n.first_user_message.trim().length>0?n.first_user_message.slice(0,60):n.id.slice(0,8)}))}catch{return[]}}function Lh(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return o=>{let a=o.trim();if(!a.startsWith("{"))return;let c;try{c=JSON.parse(a)}catch{return}if(!c||typeof c!="object")return;let u=c;if(!(u.type!=="assistant"||!u.message?.content))for(let d of u.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let p=d.input,f=typeof p?.sessionId=="string"?p.sessionId:null;!f||r.has(f)||(r.add(f),ls(t,{type:"progress",current:r.size,total:n,sessionId:f,sessionLabel:Oh(f,s)}))}}}function vh(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
690
753
  `);for(;s!==-1;){let r=t.slice(0,s);t=t.slice(s+1),r.length>0&&e(r),s=t.indexOf(`
691
- `)}}}async function Ar(e,t={},n){let s=!!t.scanId,r=s?q_(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return s&&t.scanId&&(c=X_({scanId:t.scanId,total:a,labelTable:o})),Ja({prompt:Xa(e),allowedTools:U_.split(","),opts:t,onProgress:n,onStdoutLine:c,outputFormat:s?"stream-json":"json"})}async function Qe(e,t,n={},s){return Ja({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function Ja(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return a==="stream-json"&&c.push("--verbose"),s.model&&c.push("--model",s.model),new Promise(u=>{let d=B_(),m=$_(d,c,{stdio:["ignore","pipe","pipe"],shell:$n(d)||process.platform==="win32"&&_t==="claude"}),h=[],b=[],S=o?J_(o):void 0;m.stdout.on("data",R=>{h.push(R),S&&S(R)}),m.stderr.on("data",R=>{if(b.push(R),r){let O=R.toString("utf8").trim();O&&r(O)}});let T=setTimeout(()=>{m.kill("SIGKILL")},1800*1e3);m.on("close",R=>{clearTimeout(T),u({success:R===0,stdout:Buffer.concat(h).toString("utf8"),stderr:Buffer.concat(b).toString("utf8"),exitCode:R})}),m.on("error",R=>{clearTimeout(T),u({success:!1,stdout:"",stderr:String(R),exitCode:null})})})}var U_,_t,Vt,ye=ae(()=>{"use strict";Fn();Rr();kr();Un();U_=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var lc={};xn(lc,{RetentionConfigSchema:()=>jr,readRetentionConfig:()=>Mr,writeRetentionConfig:()=>zn});import{existsSync as rc,mkdirSync as jf,readFileSync as Mf,writeFileSync as Df}from"node:fs";import{homedir as Ff}from"node:os";import{join as oc}from"node:path";import{z as Gn}from"zod";function ic(){return process.env.RECALL_HOME??oc(Ff(),".recall")}function Pf(){let e=ic();rc(e)||jf(e,{recursive:!0})}function ac(){return oc(ic(),"config.json")}function cc(){let e=ac();if(!rc(e))return{};try{return JSON.parse(Mf(e,"utf8"))}catch(t){let n=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${n}`),{}}}function Mr(){let e=cc().retention;if(!e)return{...Yn};let t=jr.safeParse({...Yn,...e});return t.success?t.data:{...Yn}}function zn(e){Pf();let t=cc(),n=jr.parse({...Yn,...t.retention??{},...e}),s={...t,retention:n};return Df(ac(),JSON.stringify(s,null,2)),n}var jr,Yn,Dr=ae(()=>{"use strict";jr=Gn.object({autoArchiveEnabled:Gn.boolean().default(!1),autoArchiveAfterDays:Gn.number().int().min(7).max(3650).default(90),lastRunAt:Gn.string().nullable().default(null)}),Yn={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Oe from"chalk";import{formatDistanceToNowStrict as Vk,parseISO as Zk}from"date-fns";var we,Kn=ae(()=>{"use strict";we={dim:Oe.gray,bold:Oe.bold,project:Oe.cyan,user:Oe.blue,assistant:Oe.green,tool:Oe.magenta,warn:Oe.yellow,err:Oe.red,ok:Oe.green,accent:Oe.hex("#f97316")}});import{existsSync as $f}from"node:fs";import{join as Uf}from"node:path";function Qt(){if(uc&&$f(Zt))return;z();let e=f(),t=Zt.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
754
+ `)}}}async function lo(e,t={},n){let s=!!t.scanId,r=s?Ch(e):[],o=new Map(r.map(u=>[u.id,u.label])),a=r.length,c;return s&&t.scanId&&(c=Lh({scanId:t.scanId,total:a,labelTable:o})),$c({prompt:Fc(e),allowedTools:Ah.split(","),opts:t,onProgress:n,onStdoutLine:c,outputFormat:s?"stream-json":"json"})}async function at(e,t,n={},s){return $c({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function $c(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:o,outputFormat:a}=e,c=["-p",t,"--output-format",a,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return a==="stream-json"&&c.push("--verbose"),s.model&&c.push("--model",s.model),new Promise(u=>{let d=xh(),p=kh(d,c,{stdio:["ignore","pipe","pipe"],shell:us(d)||process.platform==="win32"&&At==="claude"}),f=[],S=[],y=o?vh(o):void 0;p.stdout.on("data",R=>{f.push(R),y&&y(R)}),p.stderr.on("data",R=>{if(S.push(R),r){let k=R.toString("utf8").trim();k&&r(k)}});let b=setTimeout(()=>{p.kill("SIGKILL")},1800*1e3);p.on("close",R=>{clearTimeout(b),u({success:R===0,stdout:Buffer.concat(f).toString("utf8"),stderr:Buffer.concat(S).toString("utf8"),exitCode:R})}),p.on("error",R=>{clearTimeout(b),u({success:!1,stdout:"",stderr:String(R),exitCode:null})})})}var Ah,At,fn,Te=re(()=>{"use strict";cs();ao();co();ds();Ah=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var nl={};_t(nl,{RetentionConfigSchema:()=>Eo,readRetentionConfig:()=>bo,writeRetentionConfig:()=>Ss});import{existsSync as Vc,mkdirSync as bE,readFileSync as SE,writeFileSync as yE}from"node:fs";import{homedir as TE}from"node:os";import{join as Qc}from"node:path";import{z as Es}from"zod";function Zc(){return process.env.RECALL_HOME??Qc(TE(),".recall")}function wE(){let e=Zc();Vc(e)||bE(e,{recursive:!0})}function el(){return Qc(Zc(),"config.json")}function tl(){let e=el();if(!Vc(e))return{};try{return JSON.parse(SE(e,"utf8"))}catch(t){let n=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${n}`),{}}}function bo(){let e=tl().retention;if(!e)return{...bs};let t=Eo.safeParse({...bs,...e});return t.success?t.data:{...bs}}function Ss(e){wE();let t=tl(),n=Eo.parse({...bs,...t.retention??{},...e}),s={...t,retention:n};return yE(el(),JSON.stringify(s,null,2)),n}var Eo,bs,So=re(()=>{"use strict";Eo=Es.object({autoArchiveEnabled:Es.boolean().default(!1),autoArchiveAfterDays:Es.number().int().min(7).max(3650).default(90),lastRunAt:Es.string().nullable().default(null)}),bs={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null}});import Le from"chalk";import{formatDistanceToNowStrict as iO,parseISO as aO}from"date-fns";var we,ys=re(()=>{"use strict";we={dim:Le.gray,bold:Le.bold,project:Le.cyan,user:Le.blue,assistant:Le.green,tool:Le.magenta,warn:Le.yellow,err:Le.red,ok:Le.green,accent:Le.hex("#f97316")}});import{existsSync as RE}from"node:fs";import{join as kE}from"node:path";function hn(){if(sl&&RE(_n))return;V();let e=h(),t=_n.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
692
755
  CREATE TABLE IF NOT EXISTS archive.messages_archive (
693
756
  uuid TEXT PRIMARY KEY,
694
757
  session_id TEXT NOT NULL,
@@ -703,12 +766,12 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
703
766
  archived_at TEXT NOT NULL DEFAULT (datetime('now'))
704
767
  );
705
768
  CREATE INDEX IF NOT EXISTS archive.idx_messages_archive_session ON messages_archive(session_id);
706
- `)}finally{e.exec("DETACH DATABASE archive")}uc=!0}function dc(){let e=f();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;Qt();let n=Zt.replace(/'/g,"''"),s=0;e.exec(`ATTACH DATABASE '${n}' AS archive`);try{e.transaction(()=>{s=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
769
+ `)}finally{e.exec("DETACH DATABASE archive")}sl=!0}function rl(){let e=h();if(e.prepare("SELECT COUNT(*) AS n FROM messages_archive").get().n===0)return 0;hn();let n=_n.replace(/'/g,"''"),s=0;e.exec(`ATTACH DATABASE '${n}' AS archive`);try{e.transaction(()=>{s=e.prepare(`INSERT OR IGNORE INTO archive.messages_archive
707
770
  (uuid, session_id, parent_uuid, type, role, timestamp,
708
771
  is_sidechain, content_text, tool_names, raw_json, archived_at)
709
772
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
710
773
  is_sidechain, content_text, tool_names, raw_json, archived_at
711
- FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return s}var Zt,uc,Fr=ae(()=>{"use strict";ee();B();Zt=Uf(W,"archive.sqlite"),uc=!1});var mc={};xn(mc,{runArchive:()=>Yf});function Pr(e){Qt();let t=f(),n=Zt.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${n}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function Bf(){return Pr(e=>{let t=e.prepare(`SELECT
774
+ FROM main.messages_archive`).run().changes,e.prepare("DELETE FROM main.messages_archive").run()})()}finally{e.exec("DETACH DATABASE archive")}return s}var _n,sl,yo=re(()=>{"use strict";ne();q();_n=kE(W,"archive.sqlite"),sl=!1});var il={};_t(il,{runArchive:()=>IE});function To(e){hn();let t=h(),n=_n.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${n}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function AE(){return To(e=>{let t=e.prepare(`SELECT
712
775
  SUM(CASE WHEN archive_status = 'archived' THEN 1 ELSE 0 END) AS archived,
713
776
  SUM(CASE WHEN archive_status != 'archived' THEN 1 ELSE 0 END) AS live
714
777
  FROM sessions`).get(),n=e.prepare("SELECT COUNT(*) AS n FROM messages").get().n,s=e.prepare(`SELECT
@@ -717,15 +780,15 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
717
780
  SELECT MAX(timestamp) AS t FROM main.messages_archive WHERE timestamp IS NOT NULL
718
781
  UNION ALL
719
782
  SELECT MAX(timestamp) AS t FROM archive.messages_archive WHERE timestamp IS NOT NULL
720
- )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function pc(e){return e?e.slice(0,10):"\u2014"}function Hf(){let e=Bf();return console.log(we.dim("\u2014 Archive state \u2014")),console.log(` Live sessions ${e.liveSessions.toLocaleString()}`),console.log(` Archived sessions ${e.archivedSessions.toLocaleString()}`),console.log(` Live messages ${e.liveMessages.toLocaleString()}`),console.log(` Archived messages ${e.archivedMessages.toLocaleString()}`),console.log(` Oldest live ${pc(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${pc(e.newestArchivedTimestamp)}`),console.log(""),console.log(we.dim(" recall archive run --before YYYY-MM-DD")),console.log(we.dim(" recall archive restore <session-id>")),0}function Wf(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let n=f().prepare(`SELECT s.id, s.ended_at, s.message_count
783
+ )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function ol(e){return e?e.slice(0,10):"\u2014"}function xE(){let e=AE();return console.log(we.dim("\u2014 Archive state \u2014")),console.log(` Live sessions ${e.liveSessions.toLocaleString()}`),console.log(` Archived sessions ${e.archivedSessions.toLocaleString()}`),console.log(` Live messages ${e.liveMessages.toLocaleString()}`),console.log(` Archived messages ${e.archivedMessages.toLocaleString()}`),console.log(` Oldest live ${ol(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${ol(e.newestArchivedTimestamp)}`),console.log(""),console.log(we.dim(" recall archive run --before YYYY-MM-DD")),console.log(we.dim(" recall archive restore <session-id>")),0}function NE(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let n=h().prepare(`SELECT s.id, s.ended_at, s.message_count
721
784
  FROM sessions s
722
785
  WHERE s.archive_status != 'archived'
723
- AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(n.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let s=n.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${n.length.toLocaleString()} session(s), ${s.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(we.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=n.map(c=>c.id),o=Date.now(),a=Pr(c=>c.transaction(d=>{let m=0,h=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
786
+ AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(n.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let s=n.reduce((c,u)=>c+(u.message_count??0),0);if(console.log(`${n.length.toLocaleString()} session(s), ${s.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(we.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=n.map(c=>c.id),o=Date.now(),a=To(c=>c.transaction(d=>{let p=0,f=c.prepare(`INSERT OR IGNORE INTO archive.messages_archive
724
787
  (uuid, session_id, parent_uuid, type, role, timestamp,
725
788
  is_sidechain, content_text, tool_names, raw_json, archived_at)
726
789
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
727
790
  is_sidechain, content_text, tool_names, raw_json, datetime('now')
728
- FROM messages WHERE session_id = ?`),b=c.prepare("DELETE FROM messages WHERE session_id = ?"),S=c.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let T of d){h.run(T);let R=b.run(T);m+=Number(R.changes??0),S.run(T)}return m})(r));return console.log(`Archived ${n.length.toLocaleString()} session(s), moved ${a.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(we.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function qf(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let n=f().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!n)return console.error(`Session ${e} not found.`),1;if(n.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${n.archive_status}).`),1;let s=Pr(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
791
+ FROM messages WHERE session_id = ?`),S=c.prepare("DELETE FROM messages WHERE session_id = ?"),y=c.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let b of d){f.run(b);let R=S.run(b);p+=Number(R.changes??0),y.run(b)}return p})(r));return console.log(`Archived ${n.length.toLocaleString()} session(s), moved ${a.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(we.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function OE(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let n=h().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!n)return console.error(`Session ${e} not found.`),1;if(n.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${n.archive_status}).`),1;let s=To(r=>r.transaction(()=>{let a=r.prepare(`INSERT OR IGNORE INTO messages
729
792
  (uuid, session_id, parent_uuid, type, role, timestamp,
730
793
  is_sidechain, content_text, tool_names, raw_json)
731
794
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
@@ -735,32 +798,32 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
735
798
  is_sidechain, content_text, tool_names, raw_json)
736
799
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
737
800
  is_sidechain, content_text, tool_names, raw_json
738
- FROM archive.messages_archive WHERE session_id = ?`),u=r.prepare("DELETE FROM main.messages_archive WHERE session_id = ?"),d=r.prepare("DELETE FROM archive.messages_archive WHERE session_id = ?"),m=Number(a.run(e).changes??0),h=Number(c.run(e).changes??0);return u.run(e),d.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),m+h})());return console.log(`Restored ${s.toLocaleString()} message(s) for session ${e}.`),0}function Xf(){let e=Mr();return console.log(we.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?we.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function Jf(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let n=zn({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${n.autoArchiveAfterDays} days).`),console.log(we.dim(" The daemon will run a daily archive pass on the next tick.")),0}function Gf(){return zn({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function Yf(e){let t=e._action??"list";if(t==="list"||t==="stats")return Hf();if(t==="run")return e.before?Wf({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return qf(e._sessionId??"");if(t==="auto"){let n=e._subAction??"status";return n==="status"?Xf():n==="on"||n==="enable"?Jf(e.after):n==="off"||n==="disable"?Gf():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
801
+ FROM archive.messages_archive WHERE session_id = ?`),u=r.prepare("DELETE FROM main.messages_archive WHERE session_id = ?"),d=r.prepare("DELETE FROM archive.messages_archive WHERE session_id = ?"),p=Number(a.run(e).changes??0),f=Number(c.run(e).changes??0);return u.run(e),d.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),p+f})());return console.log(`Restored ${s.toLocaleString()} message(s) for session ${e}.`),0}function CE(){let e=bo();return console.log(we.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?we.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function LE(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let n=Ss({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${n.autoArchiveAfterDays} days).`),console.log(we.dim(" The daemon will run a daily archive pass on the next tick.")),0}function vE(){return Ss({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function IE(e){let t=e._action??"list";if(t==="list"||t==="stats")return xE();if(t==="run")return e.before?NE({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return OE(e._sessionId??"");if(t==="auto"){let n=e._subAction??"status";return n==="status"?CE():n==="on"||n==="enable"?LE(e.after):n==="off"||n==="disable"?vE():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
739
802
  list \u2014 show archive counts
740
803
  run --before YYYY-MM-DD [--dry-run] \u2014 move sessions older than DATE
741
804
  restore <session-id> \u2014 pull a session back from archive
742
- auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var gc=ae(()=>{"use strict";Kn();B();Fr();Dr()});import{cpus as c0}from"node:os";import{Hono as vw}from"hono";import{serve as Iw}from"@hono/node-server";ee();import{existsSync as Cm,readFileSync as vm,writeFileSync as C0,unlinkSync as v0}from"node:fs";import{join as Im}from"node:path";var Pi=Im(W,"license.json");function Ht(){if(!Cm(Pi))return null;try{let e=vm(Pi,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as jm,importSPKI as Mm}from"jose";var $i=`-----BEGIN PUBLIC KEY-----
805
+ auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}var al=re(()=>{"use strict";ys();q();yo();So()});import{cpus as cA}from"node:os";import{Hono as kk}from"hono";import{serve as Ak}from"@hono/node-server";ne();import{existsSync as tf,readFileSync as nf,writeFileSync as LA,unlinkSync as vA}from"node:fs";import{join as sf}from"node:path";var La=sf(W,"license.json");function sn(){if(!tf(La))return null;try{let e=nf(La,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as rf,importSPKI as of}from"jose";var va=`-----BEGIN PUBLIC KEY-----
743
806
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
744
807
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
745
808
  -----END PUBLIC KEY-----
746
- `,or="ES256",Ui="clauderecall.com",Bi="clauderecall-cli";var Nn=null;async function Dm(){return Nn||(Nn=await Mm($i,or),Nn)}async function Hi(e){try{let t=await Dm(),{payload:n}=await jm(e,t,{issuer:Ui,audience:Bi,algorithms:[or]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Fm}from"node:crypto";import{hostname as Pm,userInfo as $m,platform as Um,arch as Bm}from"node:os";function Wi(){let e="unknown";try{e=$m().username}catch{}let t=[Pm(),e,Um(),Bm()];return Fm("sha256").update(t.join("\0")).digest("hex")}ee();import{existsSync as Hm,readFileSync as Wm,writeFileSync as qm}from"node:fs";import{join as Xm}from"node:path";function qi(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),n;try{n=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let s=n.hostname==="127.0.0.1"||n.hostname==="localhost"||n.hostname==="::1";if(n.protocol==="https:"||n.protocol==="http:"&&s)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var ir=Xm(W,"license-check.json"),Jm=1440*60*1e3,Gm=720*60*60*1e3,Ym=1e4;function Xi(){if(!Hm(ir))return null;try{let e=JSON.parse(Wm(ir,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function zm(e){z(),qm(ir,JSON.stringify(e,null,2)+`
747
- `,{mode:384})}async function Km(e,t){let n=null,s=null;try{n=new AbortController,s=setTimeout(()=>n?.abort(),Ym);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:n.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{s&&clearTimeout(s)}}async function Ji(e,t={}){let n=Xi(),s=t.apiUrl??`${qi()}/api/license/check`,r=n?.license_key===e,o=!n||!r||Date.now()-new Date(n.last_checked_at).getTime()>=Jm;if(!t.force&&!o)return n;let a=await Km(e,s);if(!a)return r?n:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return zm(c),c}function Gi(e){let t=Xi();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()>Gm?{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 Vm=Date.UTC(2026,5,1,7,0,0);var Zm=1440*60*1e3,V0=60*Zm;async function Ke(){let e=Ht();if(!e)return{tier:"free"};let t=await Hi(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Wi())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=Gi(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:Qm(e,t.claims)}async function Yi(e){let t=Ht();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let n=await Ji(t.license_key,{force:e?.force??!1});return n?{ran:!0,revoked:n.revoked,reason:n.reason,last_checked_at:n.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function Qm(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function zi(){return(await Ke()).tier==="pro"}B();B();import{createHash as Sg}from"node:crypto";B();ee();import{writeFileSync as rg,readFileSync as fR,existsSync as og,mkdirSync as ig,readdirSync as hR,unlinkSync as ER}from"node:fs";import{join as na}from"node:path";import{randomUUID as sa}from"node:crypto";var lr=na(W,"bug-patterns");function ag(){z(),og(lr)||ig(lr,{recursive:!0})}function $e(e){return{id:e.id,signature_hash:e.signature_hash,example_message:e.example_message,occurrence_count:e.occurrence_count,first_seen_at:e.first_seen_at,last_seen_at:e.last_seen_at,resolved_in_session_id:e.resolved_in_session_id,fix_summary:e.fix_summary}}function ra(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function oa(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=f(),n=new Date().toISOString(),s=e.id??sa(),r=e.first_seen_at??n,o=e.last_seen_at??n,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
809
+ `,Fr="ES256",Ia="clauderecall.com",Ma="clauderecall-cli";var Yn=null;async function af(){return Yn||(Yn=await of(va,Fr),Yn)}async function ja(e){try{let t=await af(),{payload:n}=await rf(e,t,{issuer:Ia,audience:Ma,algorithms:[Fr]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as cf}from"node:crypto";import{hostname as lf,userInfo as uf,platform as df,arch as pf}from"node:os";function Da(){let e="unknown";try{e=uf().username}catch{}let t=[lf(),e,df(),pf()];return cf("sha256").update(t.join("\0")).digest("hex")}ne();import{existsSync as mf,readFileSync as gf,writeFileSync as ff}from"node:fs";import{join as _f}from"node:path";function Pa(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),n;try{n=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let s=n.hostname==="127.0.0.1"||n.hostname==="localhost"||n.hostname==="::1";if(n.protocol==="https:"||n.protocol==="http:"&&s)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var $r=_f(W,"license-check.json"),hf=1440*60*1e3,Ef=720*60*60*1e3,bf=1e4;function Fa(){if(!mf($r))return null;try{let e=JSON.parse(gf($r,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Sf(e){V(),ff($r,JSON.stringify(e,null,2)+`
810
+ `,{mode:384})}async function yf(e,t){let n=null,s=null;try{n=new AbortController,s=setTimeout(()=>n?.abort(),bf);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:n.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{s&&clearTimeout(s)}}async function $a(e,t={}){let n=Fa(),s=t.apiUrl??`${Pa()}/api/license/check`,r=n?.license_key===e,o=!n||!r||Date.now()-new Date(n.last_checked_at).getTime()>=hf;if(!t.force&&!o)return n;let a=await yf(e,s);if(!a)return r?n:null;let c={license_key:e,last_checked_at:new Date().toISOString(),revoked:a.revoked,reason:a.reason??null};return Sf(c),c}function Ua(e){let t=Fa();return!t||t.license_key!==e?null:t.revoked?{revoked:!0,reason:t.reason?`license revoked: ${t.reason}`:"license revoked by issuer"}:Date.now()-new Date(t.last_checked_at).getTime()>Ef?{revoked:!0,reason:"license has not been validated with the server in 30+ days. Reconnect to the internet and run `recall license check`"}:null}var Tf=Date.UTC(2026,5,1,7,0,0);var wf=1440*60*1e3,VA=60*wf;async function rt(){let e=sn();if(!e)return{tier:"free"};let t=await ja(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Da())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=Ua(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:Rf(e,t.claims)}async function Ha(e){let t=sn();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let n=await $a(t.license_key,{force:e?.force??!1});return n?{ran:!0,revoked:n.revoked,reason:n.reason,last_checked_at:n.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function Rf(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function Ba(){return(await rt()).tier==="pro"}q();q();import{createHash as i_}from"node:crypto";q();ne();import{writeFileSync as Of,readFileSync as _x,existsSync as Cf,mkdirSync as Lf,readdirSync as hx,unlinkSync as Ex}from"node:fs";import{join as Ga}from"node:path";import{randomUUID as Ya}from"node:crypto";var Wr=Ga(W,"bug-patterns");function vf(){V(),Cf(Wr)||Lf(Wr,{recursive:!0})}function Je(e){return{id:e.id,signature_hash:e.signature_hash,example_message:e.example_message,occurrence_count:e.occurrence_count,first_seen_at:e.first_seen_at,last_seen_at:e.last_seen_at,resolved_in_session_id:e.resolved_in_session_id,fix_summary:e.fix_summary}}function Ka(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function za(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=h(),n=new Date().toISOString(),s=e.id??Ya(),r=e.first_seen_at??n,o=e.last_seen_at??n,a=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
748
811
  (id, signature_hash, example_message, occurrence_count,
749
812
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
750
813
  VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(s,e.signature_hash,e.example_message,a.length,r,o);let u=t.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
751
814
  VALUES (?, ?, ?)
752
- ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(s,d,n)})();let c=ia(s);if(!c)throw new Error("createCluster succeeded but read-back failed");return Wt(s),c}function ia(e){let t=f(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:$e(n),members:s.map(ra)}}function aa(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let n=f();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;n.transaction(()=>{let c=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
815
+ ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of a)u.run(s,d,n)})();let c=Va(s);if(!c)throw new Error("createCluster succeeded but read-back failed");return rn(s),c}function Va(e){let t=h(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:Je(n),members:s.map(Ka)}}function Qa(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let n=h();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;n.transaction(()=>{let c=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
753
816
  VALUES (?, ?, ?)
754
817
  ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let u of Array.from(new Set(t)))c.run(e,u,r).changes>0&&(o+=1);if(o>0){let u=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;n.prepare(`UPDATE bug_pattern_clusters
755
818
  SET occurrence_count = ?, last_seen_at = ?
756
- WHERE id = ?`).run(u,r,e)}})(),o>0&&Wt(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:$e(a),added:o}}function ca(e={}){let t=f(),n=[],s=[];typeof e.minOccurrenceCount=="number"&&(n.push("c.occurrence_count >= ?"),s.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&n.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(n.push(`EXISTS (
819
+ WHERE id = ?`).run(u,r,e)}})(),o>0&&rn(e);let a=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Je(a),added:o}}function Za(e={}){let t=h(),n=[],s=[];typeof e.minOccurrenceCount=="number"&&(n.push("c.occurrence_count >= ?"),s.push(Math.max(1,Math.floor(e.minOccurrenceCount)))),typeof e.hasResolved=="boolean"&&n.push(e.hasResolved?"c.resolved_in_session_id IS NOT NULL":"c.resolved_in_session_id IS NULL"),e.project&&(n.push(`EXISTS (
757
820
  SELECT 1 FROM bug_pattern_members m
758
821
  JOIN sessions s ON s.id = m.session_id
759
822
  JOIN projects p ON p.id = s.project_id
760
823
  WHERE m.cluster_id = c.id AND p.name = ?
761
824
  )`),s.push(e.project));let r=n.length>0?`WHERE ${n.join(" AND ")}`:"",o=t.prepare(`SELECT COUNT(*) AS n FROM bug_pattern_clusters c ${r}`).get(...s),a=Math.max(1,Math.min(5e3,e.limit??100)),c=Math.max(0,Math.floor(e.offset??0));return{clusters:t.prepare(`SELECT c.* FROM bug_pattern_clusters c ${r}
762
825
  ORDER BY c.occurrence_count DESC, c.last_seen_at DESC
763
- LIMIT ? OFFSET ?`).all(...s,a,c).map($e),total:o.n}}function cg(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,n=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:n,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function la(e){let t=f(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
826
+ LIMIT ? OFFSET ?`).all(...s,a,c).map(Je),total:o.n}}function If(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,n=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:n,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function ec(e){let t=h(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
764
827
  NULLIF(sa.alias, '') AS alias,
765
828
  s.auto_title,
766
829
  s.first_user_message,
@@ -771,31 +834,31 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
771
834
  LEFT JOIN session_aliases sa ON sa.session_id = m.session_id
772
835
  LEFT JOIN projects p ON p.id = s.project_id
773
836
  WHERE m.cluster_id = ?
774
- ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:$e(n),members:s.map(cg)}}function ua(e,t,n){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof n!="string"||!n.trim())throw new Error("fixSummary is required");let s=f();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!s.prepare("SELECT 1 FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?").get(e,t))throw new Error(`session ${t} is not a member of cluster ${e}`);s.prepare(`UPDATE bug_pattern_clusters
837
+ ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:Je(n),members:s.map(If)}}function tc(e,t,n){if(!e)throw new Error("clusterId is required");if(!t)throw new Error("sessionId is required");if(typeof n!="string"||!n.trim())throw new Error("fixSummary is required");let s=h();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);if(!s.prepare("SELECT 1 FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?").get(e,t))throw new Error(`session ${t} is not a member of cluster ${e}`);s.prepare(`UPDATE bug_pattern_clusters
775
838
  SET resolved_in_session_id = ?, fix_summary = ?
776
- WHERE id = ?`).run(t,n.trim(),e),Wt(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:$e(a)}}function da(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("memberSessionIds must be a non-empty array");let n=f(),s=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=n.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
777
- WHERE cluster_id = ? AND session_id IN (${o})`).all(e,...r);if(a.length===0)throw new Error(`none of the supplied session_ids are members of cluster ${e}`);let c=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;if(a.length>=c)throw new Error(`cannot split: would leave the original cluster empty (move ${a.length} of ${c})`);let u=sa(),d=new Date().toISOString(),m=[];n.transaction(()=>{n.prepare(`INSERT INTO bug_pattern_clusters
839
+ WHERE id = ?`).run(t,n.trim(),e),rn(e);let a=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Je(a)}}function nc(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("memberSessionIds must be a non-empty array");let n=h(),s=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)throw new Error(`cluster ${e} not found`);let r=Array.from(new Set(t)),o=r.map(()=>"?").join(","),a=n.prepare(`SELECT session_id, matched_at FROM bug_pattern_members
840
+ WHERE cluster_id = ? AND session_id IN (${o})`).all(e,...r);if(a.length===0)throw new Error(`none of the supplied session_ids are members of cluster ${e}`);let c=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;if(a.length>=c)throw new Error(`cannot split: would leave the original cluster empty (move ${a.length} of ${c})`);let u=Ya(),d=new Date().toISOString(),p=[];n.transaction(()=>{n.prepare(`INSERT INTO bug_pattern_clusters
778
841
  (id, signature_hash, example_message, occurrence_count,
779
842
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
780
- VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(u,s.signature_hash,s.example_message,a.length,s.first_seen_at,d);let S=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
781
- VALUES (?, ?, ?)`),T=n.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let O of a)S.run(u,O.session_id,O.matched_at),T.run(e,O.session_id);let R=c-a.length;n.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(R,e),m=n.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),Wt(e),Wt(u);let h=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),b=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:$e(h),split:$e(b),movedMembers:m.map(ra)}}function Wt(e){try{ag();let t=ia(e);if(!t)return;let n=na(lr,`${e}.json`),s={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};rg(n,JSON.stringify(s,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function pa(e){return e?f().prepare(`SELECT * FROM bug_pattern_clusters
843
+ VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(u,s.signature_hash,s.example_message,a.length,s.first_seen_at,d);let y=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
844
+ VALUES (?, ?, ?)`),b=n.prepare("DELETE FROM bug_pattern_members WHERE cluster_id = ? AND session_id = ?");for(let k of a)y.run(u,k.session_id,k.matched_at),b.run(e,k.session_id);let R=c-a.length;n.prepare("UPDATE bug_pattern_clusters SET occurrence_count = ? WHERE id = ?").run(R,e),p=n.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ?").all(u)})(),rn(e),rn(u);let f=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e),S=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(u);return{original:Je(f),split:Je(S),movedMembers:p.map(Ka)}}function rn(e){try{vf();let t=Va(e);if(!t)return;let n=Ga(Wr,`${e}.json`),s={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};Of(n,JSON.stringify(s,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function sc(e){return e?h().prepare(`SELECT * FROM bug_pattern_clusters
782
845
  WHERE signature_hash = ?
783
- ORDER BY last_seen_at DESC, id ASC`).all(e).map($e):[]}function ma(e){if(!e)return new Set;let n=f().prepare(`SELECT DISTINCT m.session_id
846
+ ORDER BY last_seen_at DESC, id ASC`).all(e).map(Je):[]}function rc(e){if(!e)return new Set;let n=h().prepare(`SELECT DISTINCT m.session_id
784
847
  FROM bug_pattern_members m
785
848
  JOIN bug_pattern_clusters c ON c.id = m.cluster_id
786
- WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var Tg=/\b0x[0-9a-fA-F]+\b/g,yg=/\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g,wg=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,Rg=/:\d+:\d+/g,kg=/\bline\s+\d+\b/gi,Ag=/\bcolumn\s+\d+\b/gi,xg=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,Ng=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,Og=/\b\d{4,}\b/g,Lg=/(['"`])[^'"`\n]{1,128}\1/g;function Cg(e){if(!e)return"";let t=String(e);return t=t.replace(Tg,"<hex>"),t=t.replace(yg,"<uuid>"),t=t.replace(wg,"<ts>"),t=t.replace(Rg,":<line>:<col>"),t=t.replace(kg,"line <n>"),t=t.replace(Ag,"column <n>"),t=t.replace(xg,"pid <n>"),t=t.replace(Ng,"port <n>"),t=t.replace(Og,"<num>"),t=t.replace(Lg,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function vg(e){let t=(e.error_type??"unknown").toLowerCase().trim(),n=Cg(e.snippet??e.message_hash??""),s=`${t}|${n}`;return Sg("sha256").update(s).digest("hex").slice(0,16)}function Ig(e){let t=f(),n=["oi.bug_signatures IS NOT NULL"],s=[];e&&(n.push("p.name = ?"),s.push(e));let r=`WHERE ${n.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
849
+ WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var a_=/\b0x[0-9a-fA-F]+\b/g,c_=/\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g,l_=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,u_=/:\d+:\d+/g,d_=/\bline\s+\d+\b/gi,p_=/\bcolumn\s+\d+\b/gi,m_=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,g_=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,f_=/\b\d{4,}\b/g,__=/(['"`])[^'"`\n]{1,128}\1/g;function h_(e){if(!e)return"";let t=String(e);return t=t.replace(a_,"<hex>"),t=t.replace(c_,"<uuid>"),t=t.replace(l_,"<ts>"),t=t.replace(u_,":<line>:<col>"),t=t.replace(d_,"line <n>"),t=t.replace(p_,"column <n>"),t=t.replace(m_,"pid <n>"),t=t.replace(g_,"port <n>"),t=t.replace(f_,"<num>"),t=t.replace(__,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function E_(e){let t=(e.error_type??"unknown").toLowerCase().trim(),n=h_(e.snippet??e.message_hash??""),s=`${t}|${n}`;return i_("sha256").update(s).digest("hex").slice(0,16)}function b_(e){let t=h(),n=["oi.bug_signatures IS NOT NULL"],s=[];e&&(n.push("p.name = ?"),s.push(e));let r=`WHERE ${n.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
787
850
  p.name AS project,
788
851
  s.started_at AS started_at,
789
852
  oi.bug_signatures AS bug_signatures
790
853
  FROM session_output_index oi
791
854
  LEFT JOIN sessions s ON s.id = oi.session_id
792
855
  LEFT JOIN projects p ON p.id = s.project_id
793
- ${r}`).all(...s),a=[];for(let c of o){if(!c.bug_signatures)continue;let u=[];try{let d=JSON.parse(c.bug_signatures);Array.isArray(d)&&(u=d)}catch{continue}for(let d of u){if(!d||typeof d!="object"||!(d.snippet??"").trim())continue;let h=vg(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:h})}}return a}function jg(e){let t=new Map;for(let s of e){let r=t.get(s.fingerprint);r||(r=[],t.set(s.fingerprint,r)),r.push(s)}let n=[];for(let[s,r]of t){let o=new Set,a=[];for(let m of r)o.has(m.session_id)||(o.add(m.session_id),a.push(m));let c=[...a].sort((m,h)=>{let b=m.started_at??"",S=h.started_at??"";return b&&S?b<S?-1:b>S?1:0:b?-1:S?1:0}),u=c.find(m=>m.started_at),d=[...c].reverse().find(m=>m.started_at);n.push({fingerprint:s,example_message:a[0].signature.snippet??a[0].signature.message_hash??"",members:a,first_seen_at:u?.started_at??new Date().toISOString(),last_seen_at:d?.started_at??new Date().toISOString()})}return n}function Mg(e,t){if(e.length!==t.length)return 0;let n=0,s=0,r=0;for(let a=0;a<e.length;a++)n+=e[a]*t[a],s+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(s)*Math.sqrt(r);return o>0?n/o:0}function Dg(e){let{records:t,vectors:n,epsilon:s,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let m=0;m<o;m++){let h=[];for(let b=0;b<o;b++){if(m===b)continue;1-Mg(n[m],n[b])<=s&&h.push(b)}a.push(h)}let c=new Array(o).fill(!1),u=new Array(o).fill(-1),d=[];for(let m=0;m<o;m++){if(c[m])continue;c[m]=!0;let h=a[m];if(h.length<r)continue;let b=d.length;d.push({members:[t[m]]}),u[m]=b;let S=[...h];for(;S.length>0;){let T=S.shift();if(!c[T]&&(c[T]=!0,a[T].length>=r))for(let R of a[T])(!c[R]||u[R]===-1)&&S.push(R);u[T]===-1&&(u[T]=b,d[b].members.push(t[T]))}}return d}async function Fg(e,t,n,s){if(e.length===0)return[];let r=e.map(u=>{let d=u.signature.snippet??u.signature.message_hash??"";return`${u.signature.error_type??""}: ${d}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let a=Dg({records:e,vectors:o,epsilon:n,minPts:s}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,m=[];for(let O of u.members)d.has(O.session_id)||(d.add(O.session_id),m.push(O));if(m.length===0)continue;let b=`sem:${[...m.map(O=>O.fingerprint)].sort()[0]}`,S=[...m].sort((O,L)=>{let x=O.started_at??"",F=L.started_at??"";return x<F?-1:x>F?1:0}),T=S.find(O=>O.started_at),R=[...S].reverse().find(O=>O.started_at);c.push({fingerprint:b,example_message:m[0].signature.snippet??m[0].signature.message_hash??"",members:m,first_seen_at:T?.started_at??new Date().toISOString(),last_seen_at:R?.started_at??new Date().toISOString()})}return c}function ba(e,t){let n={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let s of e){if(s.members.length<t)continue;let r=pa(s.fingerprint),o=ma(s.fingerprint),a=s.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=oa({signature_hash:s.fingerprint,example_message:s.example_message.slice(0,256),member_session_ids:s.members.map(m=>m.session_id),first_seen_at:s.first_seen_at,last_seen_at:s.last_seen_at});n.clusters_created+=1,n.members_added+=d.members.length,n.cluster_ids.push(d.cluster.id);continue}if(a.length===0){n.cluster_ids.push(r[0].id);continue}let c=r[0],u=aa(c.id,a);u.added>0&&(n.clusters_merged+=1,n.members_added+=u.added),n.cluster_ids.push(c.id)}return n}async function Sa(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),n=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),s=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=Ig(e.project),a=new Set(o.map(x=>x.session_id)),c=jg(o),u=[],d=!1;if(e.semantic){let x=e.embedder??null;if(!x)try{x=await Ug()}catch(F){let $=(F instanceof Error?F.message:String(F)).split(`
794
- `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${$}
795
- Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(x){let F=[];for(let A of c)A.members.length===1&&F.push(A.members[0]);F.length>=2&&(u=await Fg(F,x,s,r))}}let m=c.filter(x=>x.members.length>=t),h=u.filter(x=>x.members.length>=t),b=ba(m,t),S=ba(h,t),T=[...b.cluster_ids,...S.cluster_ids],R=Array.from(new Set(T)),O=[];if(R.length>0){let x=f(),F=R.map(()=>"?").join(","),A=x.prepare(`SELECT * FROM bug_pattern_clusters
796
- WHERE id IN (${F})
856
+ ${r}`).all(...s),a=[];for(let c of o){if(!c.bug_signatures)continue;let u=[];try{let d=JSON.parse(c.bug_signatures);Array.isArray(d)&&(u=d)}catch{continue}for(let d of u){if(!d||typeof d!="object"||!(d.snippet??"").trim())continue;let f=E_(d);a.push({session_id:c.session_id,project:c.project,started_at:c.started_at,signature:d,fingerprint:f})}}return a}function S_(e){let t=new Map;for(let s of e){let r=t.get(s.fingerprint);r||(r=[],t.set(s.fingerprint,r)),r.push(s)}let n=[];for(let[s,r]of t){let o=new Set,a=[];for(let p of r)o.has(p.session_id)||(o.add(p.session_id),a.push(p));let c=[...a].sort((p,f)=>{let S=p.started_at??"",y=f.started_at??"";return S&&y?S<y?-1:S>y?1:0:S?-1:y?1:0}),u=c.find(p=>p.started_at),d=[...c].reverse().find(p=>p.started_at);n.push({fingerprint:s,example_message:a[0].signature.snippet??a[0].signature.message_hash??"",members:a,first_seen_at:u?.started_at??new Date().toISOString(),last_seen_at:d?.started_at??new Date().toISOString()})}return n}function y_(e,t){if(e.length!==t.length)return 0;let n=0,s=0,r=0;for(let a=0;a<e.length;a++)n+=e[a]*t[a],s+=e[a]*e[a],r+=t[a]*t[a];let o=Math.sqrt(s)*Math.sqrt(r);return o>0?n/o:0}function T_(e){let{records:t,vectors:n,epsilon:s,minPts:r}=e,o=t.length;if(o===0)return[];let a=[];for(let p=0;p<o;p++){let f=[];for(let S=0;S<o;S++){if(p===S)continue;1-y_(n[p],n[S])<=s&&f.push(S)}a.push(f)}let c=new Array(o).fill(!1),u=new Array(o).fill(-1),d=[];for(let p=0;p<o;p++){if(c[p])continue;c[p]=!0;let f=a[p];if(f.length<r)continue;let S=d.length;d.push({members:[t[p]]}),u[p]=S;let y=[...f];for(;y.length>0;){let b=y.shift();if(!c[b]&&(c[b]=!0,a[b].length>=r))for(let R of a[b])(!c[R]||u[R]===-1)&&y.push(R);u[b]===-1&&(u[b]=S,d[S].members.push(t[b]))}}return d}async function w_(e,t,n,s){if(e.length===0)return[];let r=e.map(u=>{let d=u.signature.snippet??u.signature.message_hash??"";return`${u.signature.error_type??""}: ${d}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let a=T_({records:e,vectors:o,epsilon:n,minPts:s}),c=[];for(let u of a){if(u.members.length===0)continue;let d=new Set,p=[];for(let k of u.members)d.has(k.session_id)||(d.add(k.session_id),p.push(k));if(p.length===0)continue;let S=`sem:${[...p.map(k=>k.fingerprint)].sort()[0]}`,y=[...p].sort((k,M)=>{let P=k.started_at??"",G=M.started_at??"";return P<G?-1:P>G?1:0}),b=y.find(k=>k.started_at),R=[...y].reverse().find(k=>k.started_at);c.push({fingerprint:S,example_message:p[0].signature.snippet??p[0].signature.message_hash??"",members:p,first_seen_at:b?.started_at??new Date().toISOString(),last_seen_at:R?.started_at??new Date().toISOString()})}return c}function pc(e,t){let n={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let s of e){if(s.members.length<t)continue;let r=sc(s.fingerprint),o=rc(s.fingerprint),a=s.members.map(d=>d.session_id).filter(d=>!o.has(d));if(r.length===0){let d=za({signature_hash:s.fingerprint,example_message:s.example_message.slice(0,256),member_session_ids:s.members.map(p=>p.session_id),first_seen_at:s.first_seen_at,last_seen_at:s.last_seen_at});n.clusters_created+=1,n.members_added+=d.members.length,n.cluster_ids.push(d.cluster.id);continue}if(a.length===0){n.cluster_ids.push(r[0].id);continue}let c=r[0],u=Qa(c.id,a);u.added>0&&(n.clusters_merged+=1,n.members_added+=u.added),n.cluster_ids.push(c.id)}return n}async function mc(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),n=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),s=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=b_(e.project),a=new Set(o.map(P=>P.session_id)),c=S_(o),u=[],d=!1;if(e.semantic){let P=e.embedder??null;if(!P)try{P=await A_()}catch(G){let v=(G instanceof Error?G.message:String(G)).split(`
857
+ `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${v}
858
+ Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),d=!0}if(P){let G=[];for(let D of c)D.members.length===1&&G.push(D.members[0]);G.length>=2&&(u=await w_(G,P,s,r))}}let p=c.filter(P=>P.members.length>=t),f=u.filter(P=>P.members.length>=t),S=pc(p,t),y=pc(f,t),b=[...S.cluster_ids,...y.cluster_ids],R=Array.from(new Set(b)),k=[];if(R.length>0){let P=h(),G=R.map(()=>"?").join(","),D=P.prepare(`SELECT * FROM bug_pattern_clusters
859
+ WHERE id IN (${G})
797
860
  ORDER BY occurrence_count DESC, last_seen_at DESC
798
- LIMIT ?`).all(...R,n);for(let $ of A)O.push({id:$.id,signature_hash:$.signature_hash,example_message:$.example_message,occurrence_count:$.occurrence_count,first_seen_at:$.first_seen_at,last_seen_at:$.last_seen_at,resolved_in_session_id:$.resolved_in_session_id,fix_summary:$.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:m.length,semantic_groups:h.length,clusters_created:b.clusters_created+S.clusters_created,clusters_merged:b.clusters_merged+S.clusters_merged,members_added:b.members_added+S.members_added,semantic_skipped:d},clusters:O}}var Pg=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:n}=await Promise.resolve().then(()=>(ct(),Ea));return n().loaded||await t(),e},$g=Pg;async function Ug(){return $g()}B();B();ee();import{writeFileSync as Ta,readFileSync as $R,existsSync as ya,mkdirSync as wa,readdirSync as UR}from"node:fs";import{join as In}from"node:path";var Bg=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),Ra=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),Hg=new Set(["pending","approved","rejected"]),Wg=new Set(["L1","L2","L3","L4","user"]),mr=In(W,"links"),gr=In(W,"suggestions"),qg=In(gr,"index.json");function Xg(){z(),ya(mr)||wa(mr,{recursive:!0})}function Jg(){z(),ya(gr)||wa(gr,{recursive:!0})}function ka(e){try{return JSON.parse(e)}catch{return e}}function jn(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:ka(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function _r(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:ka(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function Aa(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function fr(e){if(!Bg.has(e))throw new Error(`invalid link_type: ${e}`)}function Gg(e){if(!Ra.has(e))throw new Error(`invalid source: ${e}`)}function xa(e){if(!Wg.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Na(e,t){if(!e||!t)throw new Error("source_session_id and target_session_id are required");if(e===t)throw new Error("a session cannot link to itself")}function Oa(e){Na(e.source_session_id,e.target_session_id),fr(e.link_type),Gg(e.source),Aa(e.confidence);let t=f(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
861
+ LIMIT ?`).all(...R,n);for(let v of D)k.push({id:v.id,signature_hash:v.signature_hash,example_message:v.example_message,occurrence_count:v.occurrence_count,first_seen_at:v.first_seen_at,last_seen_at:v.last_seen_at,resolved_in_session_id:v.resolved_in_session_id,fix_summary:v.fix_summary})}return{progress:{total_sessions:a.size,total_signatures:o.length,exact_match_groups:p.length,semantic_groups:f.length,clusters_created:S.clusters_created+y.clusters_created,clusters_merged:S.clusters_merged+y.clusters_merged,members_added:S.members_added+y.members_added,semantic_skipped:d},clusters:k}}var R_=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:n}=await Promise.resolve().then(()=>(bt(),dc));return n().loaded||await t(),e},k_=R_;async function A_(){return k_()}q();q();ne();import{writeFileSync as gc,readFileSync as Jx,existsSync as fc,mkdirSync as _c,readdirSync as Gx}from"node:fs";import{join as rs}from"node:path";var x_=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),hc=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),N_=new Set(["pending","approved","rejected"]),O_=new Set(["L1","L2","L3","L4","user"]),zr=rs(W,"links"),Vr=rs(W,"suggestions"),C_=rs(Vr,"index.json");function L_(){V(),fc(zr)||_c(zr,{recursive:!0})}function v_(){V(),fc(Vr)||_c(Vr,{recursive:!0})}function Ec(e){try{return JSON.parse(e)}catch{return e}}function os(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,source:e.source,evidence:Ec(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Qr(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,evidence:Ec(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function bc(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function Zr(e){if(!x_.has(e))throw new Error(`invalid link_type: ${e}`)}function I_(e){if(!hc.has(e))throw new Error(`invalid source: ${e}`)}function Sc(e){if(!O_.has(e))throw new Error(`invalid inferred_by: ${e}`)}function yc(e,t){if(!e||!t)throw new Error("source_session_id and target_session_id are required");if(e===t)throw new Error("a session cannot link to itself")}function Tc(e){yc(e.source_session_id,e.target_session_id),Zr(e.link_type),I_(e.source),bc(e.confidence);let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null),r=e.approved?1:0;t.prepare(`INSERT INTO session_links
799
862
  (source_session_id, target_session_id, link_type,
800
863
  confidence, source, evidence, approved,
801
864
  created_at, updated_at)
@@ -808,11 +871,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
808
871
  updated_at = excluded.updated_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,e.source,s,r,n,n);let o=t.prepare(`SELECT * FROM session_links
809
872
  WHERE source_session_id = ?
810
873
  AND target_session_id = ?
811
- AND link_type = ?`).get(e.source_session_id,e.target_session_id,e.link_type);if(!o)throw new Error("createLink succeeded but read-back failed");return Er(e.source_session_id),jn(o)}function Mn(e={}){let t=f(),n=[],s=[];e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.linkType&&(fr(e.linkType),n.push("link_type = ?"),s.push(e.linkType)),e.approvedOnly&&n.push("approved = 1");let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
874
+ AND link_type = ?`).get(e.source_session_id,e.target_session_id,e.link_type);if(!o)throw new Error("createLink succeeded but read-back failed");return to(e.source_session_id),os(o)}function is(e={}){let t=h(),n=[],s=[];e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.linkType&&(Zr(e.linkType),n.push("link_type = ?"),s.push(e.linkType)),e.approvedOnly&&n.push("approved = 1");let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
812
875
  ORDER BY confidence DESC, updated_at DESC
813
- LIMIT ?`).all(...s,o).map(jn)}function Jt(e){return f().prepare(`SELECT * FROM session_links
876
+ LIMIT ?`).all(...s,o).map(os)}function un(e){return h().prepare(`SELECT * FROM session_links
814
877
  WHERE source_session_id = ? OR target_session_id = ?
815
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(jn)}function La(e){let t=f(),n=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!n)return{removed:0,sourceSessionId:null};let s=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return s.changes>0&&Er(n.source_session_id),{removed:s.changes,sourceSessionId:n.source_session_id}}function Gt(e){Na(e.source_session_id,e.target_session_id),fr(e.link_type),Aa(e.confidence),xa(e.inferred_by);let t=f(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
878
+ ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(os)}function wc(e){let t=h(),n=t.prepare("SELECT source_session_id FROM session_links WHERE id = ?").get(e);if(!n)return{removed:0,sourceSessionId:null};let s=t.prepare("DELETE FROM session_links WHERE id = ?").run(e);return s.changes>0&&to(n.source_session_id),{removed:s.changes,sourceSessionId:n.source_session_id}}function dn(e){yc(e.source_session_id,e.target_session_id),Zr(e.link_type),bc(e.confidence),Sc(e.inferred_by);let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
816
879
  (source_session_id, target_session_id, link_type,
817
880
  confidence, evidence, status, inferred_by,
818
881
  created_at, decided_at)
@@ -832,9 +895,9 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
832
895
  WHERE source_session_id = ?
833
896
  AND target_session_id = ?
834
897
  AND link_type = ?
835
- AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!r)throw new Error("createSuggestion succeeded but read-back failed");return Ca(),_r(r)}function lt(e={}){let t=f(),n=[],s=[];if(e.status){if(!Hg.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&&(xa(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
898
+ AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!r)throw new Error("createSuggestion succeeded but read-back failed");return Rc(),Qr(r)}function St(e={}){let t=h(),n=[],s=[];if(e.status){if(!N_.has(e.status))throw new Error(`invalid status: ${e.status}`);n.push("status = ?"),s.push(e.status)}e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.inferredBy&&(Sc(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
836
899
  ORDER BY confidence DESC, created_at DESC
837
- LIMIT ?`).all(...s,o).map(_r)}function hr(e,t,n={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let s=n.source??"manual";if(!Ra.has(s))throw new Error(`invalid source: ${s}`);let r=f(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let a=new Date().toISOString(),c;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
900
+ LIMIT ?`).all(...s,o).map(Qr)}function eo(e,t,n={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let s=n.source??"manual";if(!hc.has(s))throw new Error(`invalid source: ${s}`);let r=h(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let a=new Date().toISOString(),c;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
838
901
  SET status = ?, decided_at = ?
839
902
  WHERE id = ?`).run(t,a,e),t==="approved"&&(r.prepare(`INSERT INTO session_links
840
903
  (source_session_id, target_session_id, link_type,
@@ -849,7 +912,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
849
912
  updated_at = excluded.updated_at`).run(o.source_session_id,o.target_session_id,o.link_type,o.confidence,s,o.evidence,a,a),c=r.prepare(`SELECT * FROM session_links
850
913
  WHERE source_session_id = ?
851
914
  AND target_session_id = ?
852
- AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),Ca(),t==="approved"&&Er(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:_r(u),link:c?jn(c):null}}function Er(e){try{Xg();let t=Mn({sourceSessionId:e}),n=In(mr,`${e}.json`);if(t.length===0)return;let s={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};Ta(n,JSON.stringify(s,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function Ca(){try{Jg();let e=lt({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};Ta(qg,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}B();ee();import{writeFileSync as Yg,readFileSync as JR,existsSync as zg,mkdirSync as Kg,readdirSync as GR}from"node:fs";import{join as va}from"node:path";var br=va(W,"output-index");function Vg(){z(),zg(br)||Kg(br,{recursive:!0})}function Yt(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Zg(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function Ia(e){return{session_id:e.session_id,files_written:Yt(e.files_written),brands_mentioned:Yt(e.brands_mentioned),terms_introduced:Yt(e.terms_introduced),plan_ids_referenced:Yt(e.plan_ids_referenced),bug_signatures:Yt(e.bug_signatures),raw_extraction:Zg(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function ja(e){if(!e.session_id)throw new Error("session_id is required");let t=f(),n=new Date().toISOString(),s=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),a=JSON.stringify(e.plan_ids_referenced??[]),c=JSON.stringify(e.bug_signatures??[]),u=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),d=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
915
+ AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),Rc(),t==="approved"&&to(o.source_session_id);let u=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:Qr(u),link:c?os(c):null}}function to(e){try{L_();let t=is({sourceSessionId:e}),n=rs(zr,`${e}.json`);if(t.length===0)return;let s={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};gc(n,JSON.stringify(s,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function Rc(){try{v_();let e=St({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};gc(C_,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}q();ne();import{writeFileSync as M_,readFileSync as Zx,existsSync as j_,mkdirSync as D_,readdirSync as eN}from"node:fs";import{join as kc}from"node:path";var no=kc(W,"output-index");function P_(){V(),j_(no)||D_(no,{recursive:!0})}function pn(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function F_(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function Ac(e){return{session_id:e.session_id,files_written:pn(e.files_written),brands_mentioned:pn(e.brands_mentioned),terms_introduced:pn(e.terms_introduced),plan_ids_referenced:pn(e.plan_ids_referenced),bug_signatures:pn(e.bug_signatures),raw_extraction:F_(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function xc(e){if(!e.session_id)throw new Error("session_id is required");let t=h(),n=new Date().toISOString(),s=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),a=JSON.stringify(e.plan_ids_referenced??[]),c=JSON.stringify(e.bug_signatures??[]),u=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),d=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
853
916
  (session_id, files_written, brands_mentioned, terms_introduced,
854
917
  plan_ids_referenced, bug_signatures, raw_extraction,
855
918
  extracted_at, extractor_version)
@@ -862,11 +925,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
862
925
  bug_signatures = excluded.bug_signatures,
863
926
  raw_extraction = excluded.raw_extraction,
864
927
  extracted_at = excluded.extracted_at,
865
- extractor_version = excluded.extractor_version`).run(e.session_id,s,r,o,a,c,u,n,d);let m=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!m)throw new Error("setOutputIndex succeeded but read-back failed");let h=Ia(m);return Qg(e.session_id),h}function Ve(e){let n=f().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return n?Ia(n):null}function Qg(e){try{Vg();let t=Ve(e);if(!t)return;let n=va(br,`${e}.json`),s={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};Yg(n,JSON.stringify(s,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var Sr={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},e_=2,t_=.25,n_=5,s_=60,r_=25;function Dn(e){return e.trim().toLowerCase()}function o_(e){let t=new Set;for(let n of e.files_written)t.add(`file:${Dn(n)}`);for(let n of e.brands_mentioned)t.add(`brand:${Dn(n)}`);for(let n of e.terms_introduced)t.add(`term:${Dn(n)}`);for(let n of e.plan_ids_referenced)t.add(`plan:${Dn(n)}`);return t}function i_(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/s_);return Math.max(.2,t)}function a_(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 c_(e){let t=Ve(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(n=>n.term),plan_ids_referenced:t.plan_ids_referenced}:null}function l_(e){return f().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
928
+ extractor_version = excluded.extractor_version`).run(e.session_id,s,r,o,a,c,u,n,d);let p=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!p)throw new Error("setOutputIndex succeeded but read-back failed");let f=Ac(p);return $_(e.session_id),f}function ot(e){let n=h().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return n?Ac(n):null}function $_(e){try{P_();let t=ot(e);if(!t)return;let n=kc(no,`${e}.json`),s={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};M_(n,JSON.stringify(s,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var so={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},U_=2,H_=.25,B_=5,W_=60,q_=25;function as(e){return e.trim().toLowerCase()}function X_(e){let t=new Set;for(let n of e.files_written)t.add(`file:${as(n)}`);for(let n of e.brands_mentioned)t.add(`brand:${as(n)}`);for(let n of e.terms_introduced)t.add(`term:${as(n)}`);for(let n of e.plan_ids_referenced)t.add(`plan:${as(n)}`);return t}function J_(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/W_);return Math.max(.2,t)}function G_(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function Y_(e){let t=ot(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(n=>n.term),plan_ids_referenced:t.plan_ids_referenced}:null}function K_(e){return h().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
866
929
  FROM sessions s
867
930
  JOIN session_output_index oi ON oi.session_id = s.id
868
931
  WHERE s.project_id = ?
869
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function u_(e,t){let n=new Map,s=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let a=t.get(o.id);if(!a)continue;let c=o_(a);if(c.size!==0){s.set(o.id,c);for(let u of c){let d=n.get(u);d?d.push(o.id):n.set(u,[o.id])}}}return{posting:n,vocab:s,startedAt:r}}function d_(e,t){let n=t.vocab.get(e),s=t.startedAt.get(e)??null;if(!n||!s)return[];let r=new Map;for(let a of n){let c=t.posting.get(a);if(c)for(let u of c){if(u===e)continue;let d=t.startedAt.get(u);if(!d||d>=s)continue;let m=r.get(u);m?m.push(a):r.set(u,[a])}}let o=[];for(let[a,c]of r){let u=c.length;if(u<e_)continue;let d=t.startedAt.get(a)??null,m=a_(s,d),h=i_(m),b=Math.min(1,u/n_*h);if(b<t_)continue;let S=c.slice(0,12);o.push({target_session_id:a,matched_terms:S,overlap:u,days_apart:Math.round(m*10)/10,recency:Math.round(h*1e3)/1e3,confidence:Math.round(b*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,r_)}async function Ma(e){if(Sr.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=l_(e.projectId),n=new Map;for(let a of t){let c=c_(a.id);c&&n.set(a.id,c)}let s=u_(t,n),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let a of t){if(e.signal?.aborted)break;r.current_session_id=a.id,e.onProgress?.({...r});let c=d_(a.id,s);for(let u of c)try{let d=Gt({source_session_id:a.id,target_session_id:u.target_session_id,link_type:"citation",confidence:u.confidence,evidence:{matched_terms:u.matched_terms,overlap_count:u.overlap,recency:u.recency,days_apart:u.days_apart},inferred_by:"L2"});o.push(d.id),r.suggestions_created+=1}catch(d){console.error("[citation-inference] createSuggestion failed:",d)}r.processed_sessions+=1,e.onProgress?.({...r})}return r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:o}}B();var p_=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,m_=[/\bv\d+\.\d+(?:\.[A-Za-z]+)?\b/g,/\bPhase\s+[A-Ha-h]\b/g,/\bL\d+\b/g,/MASTER-PLAN-[A-Za-z-]+/g],g_=.95,__=.85;var Tr=50;function f_(e){if(!e)return[];let t=new Set,n=[],s=e.match(p_);if(!s)return n;for(let r of s){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),n.push(o),n.length>=Tr))break}return n}function h_(e){if(!e)return[];let t=new Set,n=[];for(let s of m_){s.lastIndex=0;let r=e.match(s);if(r){for(let o of r){let a=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!a||a.length>64)&&!t.has(a)&&(t.add(a),n.push(a),n.length>=Tr))break}if(n.length>=Tr)break}}return n}function Da(e){let t=f();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function Fa(e){let t=f();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
932
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function z_(e,t){let n=new Map,s=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let a=t.get(o.id);if(!a)continue;let c=X_(a);if(c.size!==0){s.set(o.id,c);for(let u of c){let d=n.get(u);d?d.push(o.id):n.set(u,[o.id])}}}return{posting:n,vocab:s,startedAt:r}}function V_(e,t){let n=t.vocab.get(e),s=t.startedAt.get(e)??null;if(!n||!s)return[];let r=new Map;for(let a of n){let c=t.posting.get(a);if(c)for(let u of c){if(u===e)continue;let d=t.startedAt.get(u);if(!d||d>=s)continue;let p=r.get(u);p?p.push(a):r.set(u,[a])}}let o=[];for(let[a,c]of r){let u=c.length;if(u<U_)continue;let d=t.startedAt.get(a)??null,p=G_(s,d),f=J_(p),S=Math.min(1,u/B_*f);if(S<H_)continue;let y=c.slice(0,12);o.push({target_session_id:a,matched_terms:y,overlap:u,days_apart:Math.round(p*10)/10,recency:Math.round(f*1e3)/1e3,confidence:Math.round(S*1e3)/1e3})}return o.sort((a,c)=>c.confidence-a.confidence),o.slice(0,q_)}async function Nc(e){if(so.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=K_(e.projectId),n=new Map;for(let a of t){let c=Y_(a.id);c&&n.set(a.id,c)}let s=z_(t,n),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let a of t){if(e.signal?.aborted)break;r.current_session_id=a.id,e.onProgress?.({...r});let c=V_(a.id,s);for(let u of c)try{let d=dn({source_session_id:a.id,target_session_id:u.target_session_id,link_type:"citation",confidence:u.confidence,evidence:{matched_terms:u.matched_terms,overlap_count:u.overlap,recency:u.recency,days_apart:u.days_apart},inferred_by:"L2"});o.push(d.id),r.suggestions_created+=1}catch(d){console.error("[citation-inference] createSuggestion failed:",d)}r.processed_sessions+=1,e.onProgress?.({...r})}return r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:o}}q();var Q_=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,Z_=[/\bv\d+\.\d+(?:\.[A-Za-z]+)?\b/g,/\bPhase\s+[A-Ha-h]\b/g,/\bL\d+\b/g,/MASTER-PLAN-[A-Za-z-]+/g],eh=.95,th=.85;var ro=50;function nh(e){if(!e)return[];let t=new Set,n=[],s=e.match(Q_);if(!s)return n;for(let r of s){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),n.push(o),n.length>=ro))break}return n}function sh(e){if(!e)return[];let t=new Set,n=[];for(let s of Z_){s.lastIndex=0;let r=e.match(s);if(r){for(let o of r){let a=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!a||a.length>64)&&!t.has(a)&&(t.add(a),n.push(a),n.length>=ro))break}if(n.length>=ro)break}}return n}function Oc(e){let t=h();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function Cc(e){let t=h();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
870
933
  s.project_id
871
934
  FROM messages m
872
935
  JOIN sessions s ON s.id = m.session_id
@@ -879,13 +942,17 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
879
942
  JOIN sessions s ON s.id = m.session_id
880
943
  WHERE m.is_sidechain = 0
881
944
  AND m.content_text IS NOT NULL
882
- AND length(m.content_text) > 0`).all()}function E_(e){let t=f(),n=typeof e=="number"?"WHERE s.project_id = ?":"",s=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
945
+ AND length(m.content_text) > 0`).all()}function rh(e){let t=h(),n=typeof e=="number"?"WHERE s.project_id = ?":"",s=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
883
946
  s.project_id AS project_id,
884
947
  oi.plan_ids_referenced AS plan_ids_json
885
948
  FROM session_output_index oi
886
949
  JOIN sessions s ON s.id = oi.session_id
887
- ${n}`).all(...s)}function b_(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>typeof n=="string")}catch{}return[]}function Pa(e={}){if(Sr.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Da(e.projectId),n=new Set(t.map(o=>o.id)),s=new Map;for(let o of t)s.set(o.id,o.project_id);let r=0;for(let o of Fa(e.projectId)){if(e.signal?.aborted)break;let a=f_(o.content_text);if(a.length===0)continue;let c=s.get(o.session_id);if(c!==void 0)for(let u of a){if(u===o.session_id)continue;let d=s.get(u);if(!(d===void 0&&!n.has(u))&&!(d!==void 0&&c!==void 0&&d!==c))try{Gt({source_session_id:o.session_id,target_session_id:u,link_type:"citation",confidence:g_,evidence:{matched_uuid:u,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function $a(e={}){let t=E_(e.projectId);if(t.length===0)return{created:0};let n=new Map;for(let c of t){let u=b_(c.plan_ids_json);for(let d of u){let m=d.trim().toLowerCase();if(!m)continue;let h=n.get(m);h?h.push({id:c.session_id,project_id:c.project_id}):n.set(m,[{id:c.session_id,project_id:c.project_id}])}}if(n.size===0)return{created:0};let s=Da(e.projectId),r=new Map;for(let c of s)r.set(c.id,c.project_id);let o=0,a=new Map;for(let c of Fa(e.projectId)){if(e.signal?.aborted)break;let u=h_(c.content_text);if(u.length===0)continue;let d=r.get(c.session_id);if(d!==void 0)for(let m of u){let h=n.get(m);if(h)for(let b of h){if(b.id===c.session_id||b.project_id!==d)continue;let S=a.get(c.session_id);S||(S=new Map,a.set(c.session_id,S));let T=S.get(b.id);T||(T=new Set,S.set(b.id,T)),T.add(m)}}}for(let[c,u]of a)for(let[d,m]of u)try{Gt({source_session_id:c,target_session_id:d,link_type:"citation",confidence:__,evidence:{matched_plan_ids:Array.from(m).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}B();ye();import{existsSync as Z_,mkdirSync as Q_,writeFileSync as ef}from"node:fs";import{homedir as tf}from"node:os";import{join as Nr}from"node:path";B();import{existsSync as Ga,mkdirSync as G_,readFileSync as Y_,writeFileSync as z_}from"node:fs";import{homedir as K_}from"node:os";import{join as Ya}from"node:path";import{z as Ne}from"zod";function za(){return process.env.RECALL_HOME??Ya(K_(),".recall")}function V_(){let e=za();Ga(e)||G_(e,{recursive:!0})}function Ka(){return Ya(za(),"config.json")}var Hn=Ne.object({enabled:Ne.boolean().default(!1),model:Ne.string().optional(),ratePerMinute:Ne.number().int().min(1).max(600).default(30),lastProcessedSessionId:Ne.string().nullable().default(null),backfillPaused:Ne.boolean().default(!1),autoExtractEnabled:Ne.boolean().default(!1),autoExtractIntervalMinutes:Ne.number().int().min(5).max(720).default(60),autoExtractBatchSize:Ne.number().int().min(1).max(20).default(1),autoResumeWorker:Ne.boolean().default(!1)}),Bn={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function Va(){let e=Ka();if(!Ga(e))return{};try{return JSON.parse(Y_(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ce(){let e=Va().semantic;if(!e)return{...Bn};let t=Hn.safeParse({...Bn,...e});return t.success?t.data:{...Bn}}function Wn(e){V_();let t=Va(),n=Hn.parse({...Bn,...t.semantic??{},...e}),s={...t,semantic:n};return z_(Ka(),JSON.stringify(s,null,2)),xr(n.enabled),n}function xr(e){try{f().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
888
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(e?"1":"0")}catch(t){let n=t instanceof Error?t.message:String(t);console.error(`[semantic-config] failed to sync semantic_enabled: ${n}`)}}var nf=1,sf=12e3,Za=3,rf=[];function of(){return process.env.RECALL_HOME??Nr(tf(),".recall")}function Qa(){return Nr(of(),"semantic")}function af(){let e=Qa();Z_(e)||Q_(e,{recursive:!0})}function cf(e){let t=f(),n=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
950
+ ${n}`).all(...s)}function oh(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>typeof n=="string")}catch{}return[]}function Lc(e={}){if(so.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Oc(e.projectId),n=new Set(t.map(o=>o.id)),s=new Map;for(let o of t)s.set(o.id,o.project_id);let r=0;for(let o of Cc(e.projectId)){if(e.signal?.aborted)break;let a=nh(o.content_text);if(a.length===0)continue;let c=s.get(o.session_id);if(c!==void 0)for(let u of a){if(u===o.session_id)continue;let d=s.get(u);if(!(d===void 0&&!n.has(u))&&!(d!==void 0&&c!==void 0&&d!==c))try{dn({source_session_id:o.session_id,target_session_id:u,link_type:"citation",confidence:eh,evidence:{matched_uuid:u,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function vc(e={}){let t=rh(e.projectId);if(t.length===0)return{created:0};let n=new Map;for(let c of t){let u=oh(c.plan_ids_json);for(let d of u){let p=d.trim().toLowerCase();if(!p)continue;let f=n.get(p);f?f.push({id:c.session_id,project_id:c.project_id}):n.set(p,[{id:c.session_id,project_id:c.project_id}])}}if(n.size===0)return{created:0};let s=Oc(e.projectId),r=new Map;for(let c of s)r.set(c.id,c.project_id);let o=0,a=new Map;for(let c of Cc(e.projectId)){if(e.signal?.aborted)break;let u=sh(c.content_text);if(u.length===0)continue;let d=r.get(c.session_id);if(d!==void 0)for(let p of u){let f=n.get(p);if(f)for(let S of f){if(S.id===c.session_id||S.project_id!==d)continue;let y=a.get(c.session_id);y||(y=new Map,a.set(c.session_id,y));let b=y.get(S.id);b||(b=new Set,y.set(S.id,b)),b.add(p)}}}for(let[c,u]of a)for(let[d,p]of u)try{dn({source_session_id:c,target_session_id:d,link_type:"citation",confidence:th,evidence:{matched_plan_ids:Array.from(p).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}q();Te();import{existsSync as Fh,mkdirSync as $h,writeFileSync as Uh}from"node:fs";import{homedir as Hh}from"node:os";import{join as po}from"node:path";Br();q();ne();import{existsSync as Uc,mkdirSync as Ih,readFileSync as Mh,writeFileSync as jh}from"node:fs";import{homedir as Dh}from"node:os";import{join as Hc}from"node:path";import{z as Ce}from"zod";function Bc(){return process.env.RECALL_HOME??Hc(Dh(),".recall")}function Ph(){let e=Bc();Uc(e)||Ih(e,{recursive:!0})}function Wc(){return Hc(Bc(),"config.json")}var ms=Ce.object({enabled:Ce.boolean().default(!1),model:Ce.string().optional(),ratePerMinute:Ce.number().int().min(1).max(600).default(30),lastProcessedSessionId:Ce.string().nullable().default(null),backfillPaused:Ce.boolean().default(!1),autoExtractEnabled:Ce.boolean().default(!1),autoExtractIntervalMinutes:Ce.number().int().min(5).max(720).default(60),autoExtractBatchSize:Ce.number().int().min(1).max(20).default(1),autoResumeWorker:Ce.boolean().default(!1)}),ps={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function qc(){let e=Wc();if(!Uc(e))return{};try{return JSON.parse(Mh(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ae(){let e=qc().semantic;if(!e)return{...ps};let t=ms.safeParse({...ps,...e});return t.success?t.data:{...ps}}function gs(e,t="unknown"){Ph();let n=qc(),s=ms.parse({...ps,...n.semantic??{},...e}),r={...n,semantic:s};return jh(Wc(),JSON.stringify(r,null,2)),uo(s.enabled,t),s}function uo(e,t="unknown"){let n=e?"1":"0",s=null;try{h().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
951
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=pooled at=${new Date().toISOString()}`);return}catch(r){s=r;let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] pooled getDb() failed while syncing semantic_enabled=${n}: ${o} \u2014 falling back to raw connection`)}try{let r=new Kn(st);try{r.exec(`CREATE TABLE IF NOT EXISTS app_settings (
952
+ key TEXT PRIMARY KEY,
953
+ value TEXT NOT NULL
954
+ );`),r.prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
955
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=fallback at=${new Date().toISOString()}`)}finally{r.close()}}catch(r){let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] raw-connection fallback ALSO failed for semantic_enabled=${n}: ${o} (original: ${s instanceof Error?s.message:String(s)})`)}}var Bh=1,Wh=12e3,Xc=3,qh=[];function Xh(){return process.env.RECALL_HOME??po(Hh(),".recall")}function Jc(){return po(Xh(),"semantic")}function Jh(){let e=Jc();Fh(e)||$h(e,{recursive:!0})}function Gh(e){let t=h(),n=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
889
956
  p.name AS project,
890
957
  NULLIF(sa.alias, '') AS alias
891
958
  FROM sessions s
@@ -894,10 +961,10 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
894
961
  WHERE s.id = ?`).get(e);if(!n)return null;let s=t.prepare(`SELECT role, content_text
895
962
  FROM messages
896
963
  WHERE session_id = ? AND is_sidechain = 0
897
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){if(!a.content_text)continue;let c=a.role??"system",u=a.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!u)continue;let d=u.length>1500?u.slice(0,1500)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>sf)break;r.push(m),o+=m.length}return{id:n.id,alias:n.alias,project:n.project,firstUserMessage:n.first_user_message,excerpt:r.join(`
964
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){if(!a.content_text)continue;let c=a.role??"system",u=a.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!u)continue;let d=u.length>1500?u.slice(0,1500)+"\u2026":u,p=`${c}: ${d}`;if(o+p.length>Wh)break;r.push(p),o+=p.length}return{id:n.id,alias:n.alias,project:n.project,firstUserMessage:n.first_user_message,excerpt:r.join(`
898
965
 
899
- `),messageCount:n.message_count}}function lf(e){return["You are summarizing a Claude Code session for a local semantic-search index. The summary will be stored as plain text and matched against future natural-language queries.","",`Session: ${e.alias??e.id}`,`Project: ${e.project}`,e.firstUserMessage?`Opening prompt: ${e.firstUserMessage}`:"","","Transcript excerpt (truncated):","---",e.excerpt,"---","","Output a single JSON object on one line, with no Markdown fences and no commentary:",'{"summary": "<3 sentences describing what the user was trying to do, what was built or debugged, and the outcome>", "keywords": ["<concept>", "<technology>", "<problem>", ...]}',"","Constraints:","- summary: 3 sentences, plain prose, no bullet points",'- keywords: 10\u201315 lowercase tokens, multi-word entries hyphenated (e.g. "memory-leak"); no duplicates; no generic words like "code" or "session"',"- Output JSON only. Do not echo this prompt."].filter(Boolean).join(`
900
- `)}function uf(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1);try{let o=JSON.parse(r),a=typeof o.summary=="string"?o.summary.trim():"",u=(Array.isArray(o.keywords)?o.keywords:[]).filter(d=>typeof d=="string").map(d=>d.trim().toLowerCase()).filter(d=>d.length>0&&d.length<64);return!a||u.length===0?null:{summary:a,keywords:Array.from(new Set(u)).slice(0,20)}}catch{return null}}function df(e){let t=f(),n=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
966
+ `),messageCount:n.message_count}}function Yh(e){return["You are summarizing a Claude Code session for a local semantic-search index. The summary will be stored as plain text and matched against future natural-language queries.","",`Session: ${e.alias??e.id}`,`Project: ${e.project}`,e.firstUserMessage?`Opening prompt: ${e.firstUserMessage}`:"","","Transcript excerpt (truncated):","---",e.excerpt,"---","","Output a single JSON object on one line, with no Markdown fences and no commentary:",'{"summary": "<3 sentences describing what the user was trying to do, what was built or debugged, and the outcome>", "keywords": ["<concept>", "<technology>", "<problem>", ...]}',"","Constraints:","- summary: 3 sentences, plain prose, no bullet points",'- keywords: 10\u201315 lowercase tokens, multi-word entries hyphenated (e.g. "memory-leak"); no duplicates; no generic words like "code" or "session"',"- Output JSON only. Do not echo this prompt."].filter(Boolean).join(`
967
+ `)}function Kh(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1);try{let o=JSON.parse(r),a=typeof o.summary=="string"?o.summary.trim():"",u=(Array.isArray(o.keywords)?o.keywords:[]).filter(d=>typeof d=="string").map(d=>d.trim().toLowerCase()).filter(d=>d.length>0&&d.length<64);return!a||u.length===0?null:{summary:a,keywords:Array.from(new Set(u)).slice(0,20)}}catch{return null}}function zh(e){let t=h(),n=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
901
968
  (session_id, summary, keywords, model, source_message_count, generated_at)
902
969
  VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
903
970
  ON CONFLICT(session_id) DO UPDATE SET
@@ -905,16 +972,16 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
905
972
  keywords = excluded.keywords,
906
973
  model = excluded.model,
907
974
  source_message_count = excluded.source_message_count,
908
- generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:n,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),af();let s=Nr(Qa(),`${e.sessionId}.json`);ef(s,JSON.stringify({version:nf,session_id:e.sessionId,summary:e.summary,keywords:e.keywords,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt},null,2))}var qn=null;function pf(){let t=ce().ratePerMinute,n=t/6e4;return(!qn||qn.capacity!==t)&&(qn={tokens:t,capacity:t,refillPerMs:n,lastRefill:Date.now()}),qn}function mf(e){let t=Date.now(),n=t-e.lastRefill;n>0&&(e.tokens=Math.min(e.capacity,e.tokens+n*e.refillPerMs),e.lastRefill=t)}async function gf(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=pf();if(mf(t),t.tokens>=1){t.tokens-=1;return}let n=1-t.tokens,s=Math.max(50,Math.ceil(n/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(s,5e3)))}}async function Xn(e,t={}){let n=ce();if(!n.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!pe())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let s=cf(e);if(!s)return{sessionId:e,ok:!1,reason:"session-not-found"};if(s.messageCount<Za)return{sessionId:e,ok:!1,reason:"too-short"};if(!s.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await gf(t.signal);let r=lf(s),o=await Qe(r,rf,{model:n.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:n.model??null};let a=uf(o.stdout);return a?(df({sessionId:s.id,summary:a.summary,keywords:a.keywords,model:n.model??null,sourceMessageCount:s.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:n.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:n.model??null}}async function Jn(e={}){let t=ce();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let n=f(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let a=n.prepare(`SELECT s.id
975
+ generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:n,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),Jh();let s=po(Jc(),`${e.sessionId}.json`);Uh(s,JSON.stringify({version:Bh,session_id:e.sessionId,summary:e.summary,keywords:e.keywords,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt},null,2))}var fs=null;function Vh(){let t=ae().ratePerMinute,n=t/6e4;return(!fs||fs.capacity!==t)&&(fs={tokens:t,capacity:t,refillPerMs:n,lastRefill:Date.now()}),fs}function Qh(e){let t=Date.now(),n=t-e.lastRefill;n>0&&(e.tokens=Math.min(e.capacity,e.tokens+n*e.refillPerMs),e.lastRefill=t)}async function Zh(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=Vh();if(Qh(t),t.tokens>=1){t.tokens-=1;return}let n=1-t.tokens,s=Math.max(50,Math.ceil(n/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(s,5e3)))}}async function _s(e,t={}){let n=ae();if(!n.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!me())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let s=Gh(e);if(!s)return{sessionId:e,ok:!1,reason:"session-not-found"};if(s.messageCount<Xc)return{sessionId:e,ok:!1,reason:"too-short"};if(!s.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await Zh(t.signal);let r=Yh(s),o=await at(r,qh,{model:n.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:n.model??null};let a=Kh(o.stdout);return a?(zh({sessionId:s.id,summary:a.summary,keywords:a.keywords,model:n.model??null,sourceMessageCount:s.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:n.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:n.model??null}}async function hs(e={}){let t=ae();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let n=h(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let a=n.prepare(`SELECT s.id
909
976
  FROM sessions s
910
977
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
911
978
  WHERE ${o}
912
979
  ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
913
- LIMIT @limit`).all(r),c={total:a.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(c);for(let{id:u}of a){if(e.signal?.aborted||ce().backfillPaused)break;c.currentSessionId=u,e.onProgress?.({...c});try{(await Xn(u,{signal:e.signal})).ok?c.ok+=1:c.failed+=1}catch(m){c.failed+=1,console.error("[semantic.backfill] failed for",u,m)}c.processed+=1,Wn({lastProcessedSessionId:u}),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function ec(e){if(!ce().enabled)return;let s=f().prepare(`SELECT s.message_count, s.ended_at,
980
+ LIMIT @limit`).all(r),c={total:a.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(c);for(let{id:u}of a){if(e.signal?.aborted||ae().backfillPaused)break;c.currentSessionId=u,e.onProgress?.({...c});try{(await _s(u,{signal:e.signal})).ok?c.ok+=1:c.failed+=1}catch(p){c.failed+=1,console.error("[semantic.backfill] failed for",u,p)}c.processed+=1,gs({lastProcessedSessionId:u},"pipeline"),e.onProgress?.({...c})}return c.currentSessionId=null,e.onProgress?.({...c}),c}async function Gc(e){if(!ae().enabled)return;let s=h().prepare(`SELECT s.message_count, s.ended_at,
914
981
  ss.generated_at, ss.source_message_count
915
982
  FROM sessions s
916
983
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
917
- WHERE s.id = ?`).get(e);if(s&&!(s.message_count<Za)&&!(s.generated_at&&s.source_message_count!=null&&s.source_message_count>=s.message_count))try{await Xn(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function Or(){let e=ce(),t=f(),n=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,s=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:pe(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:n,processedSessions:s,pendingSessions:Math.max(0,n-s),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}B();import{createHash as Ef}from"node:crypto";var _f=[{name:"Anthropic API key",regex:/sk-ant-[a-zA-Z0-9_\-]{40,}/g,severity:"high"},{name:"OpenAI API key",regex:/sk-(?:proj-)?[a-zA-Z0-9]{32,}/g,severity:"high"},{name:"AWS access key ID",regex:/AKIA[0-9A-Z]{16}/g,severity:"high"},{name:"GitHub PAT",regex:/gh[pousr]_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Stripe live/test key",regex:/(?:sk|rk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,severity:"high"},{name:"Slack token",regex:/xox[abprs]-[A-Za-z0-9\-]{10,}/g,severity:"high"},{name:"Google API key",regex:/AIza[0-9A-Za-z_\-]{35}/g,severity:"high"},{name:"Private key block",regex:/-----BEGIN (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,severity:"high"},{name:"Apify token",regex:/apify_api_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Notion integration",regex:/(?:secret_|ntn_)[A-Za-z0-9]{40,}/g,severity:"high"},{name:"Vercel token",regex:/vercel_[A-Za-z0-9]{24,}/g,severity:"high"},{name:"Supabase service key",regex:/sbp_[A-Za-z0-9]{40,}/g,severity:"high"},{name:"SendGrid key",regex:/SG\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}/g,severity:"high"},{name:"Mailgun key",regex:/key-[a-f0-9]{32}/g,severity:"high"},{name:"Twilio SID",regex:/AC[a-f0-9]{32}/g,severity:"high"},{name:"Discord bot token",regex:/[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27,38}/g,severity:"high"},{name:"npm token",regex:/npm_[A-Za-z0-9]{36}/g,severity:"high"},{name:"HuggingFace token",regex:/hf_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"Replicate token",regex:/r8_[A-Za-z0-9]{32,}/g,severity:"high"},{name:"Figma token",regex:/figd_[A-Za-z0-9_\-]{30,}/g,severity:"high"},{name:"Linear key",regex:/lin_api_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"DigitalOcean token",regex:/dop_v1_[a-f0-9]{64}/g,severity:"high"},{name:"Generic provider token",regex:/\b[a-z][a-z0-9]{2,20}_(?:api|pat|token|sk|pk|key|auth)_(?=[A-Za-z0-9_\-]*\d)[A-Za-z0-9_\-]{20,}\b/g,severity:"high"},{name:"Bearer token",regex:/\b[Bb]earer\s+[A-Za-z0-9_\-\.=]{24,}\b/g,severity:"medium"},{name:"Slack webhook URL",regex:/https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Discord webhook URL",regex:/https:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_\-]{40,}/g,severity:"high"},{name:"Teams webhook URL",regex:/https:\/\/[a-zA-Z0-9.\-]+\.webhook\.office\.com\/webhookb2\/[A-Za-z0-9@_\-\/]{30,}/g,severity:"high"},{name:"Secret near keyword",regex:/\b(?:webhook[_\s\-]?secret|signing[_\s\-]?secret|webhook[_\s\-]?signing[_\s\-]?secret|api[_\s\-]?secret|client[_\s\-]?secret|private[_\s\-]?key|access[_\s\-]?token|auth[_\s\-]?token|api[_\s\-]?key)\b[\s\S]{0,200}?\b(?:[a-fA-F0-9]{32,}|[A-Za-z0-9+/_\-]{20,}(?:\.[A-Za-z0-9+/_\-]{10,}){1,2}|[A-Za-z0-9+/_\-]{40,}={0,2})\b/gi,severity:"high"},{name:"JWT",regex:/eyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}/g,severity:"medium"},{name:"URL with password",regex:/https?:\/\/[^:\s/@]+:[^@\s]{6,}@[^\s/]+/g,severity:"high"},{name:"Password assignment",regex:/(?<![A-Za-z])(?:password|passwd|pwd|secret|token|api[_\-]?key|access[_\-]?key|auth[_\-]?token|webhook[_\-]?secret|client[_\-]?secret|private[_\-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_\-+/=]{16,}/gi,severity:"high"}];function ff(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),n=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-n.length))}${n}`}function hf(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(36)}function Se(e){if(!e)return{redacted:e,count:0};let t=e,n=0,s=new Set;for(let r of _f)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${hf(o)}`;return s.has(a)||(s.add(a),n+=1),`[REDACTED ${r.name}: ${ff(o)}]`});return{redacted:t,count:n}}ye();var vr=1,tt="claude-haiku-4-5-20251001",bf=3,Sf=32e3,tc=2e3,Tf=30,yf=30,wf=30,Rf=30;function kf(e){let n=f().prepare(`SELECT s.id,
984
+ WHERE s.id = ?`).get(e);if(s&&!(s.message_count<Xc)&&!(s.generated_at&&s.source_message_count!=null&&s.source_message_count>=s.message_count))try{await _s(e)}catch(r){console.error("[semantic] processSession error for",e,r)}}function mo(){let e=ae(),t=h(),n=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,s=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:me(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:n,processedSessions:s,pendingSessions:Math.max(0,n-s),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}q();import{createHash as sE}from"node:crypto";var eE=[{name:"Anthropic API key",regex:/sk-ant-[a-zA-Z0-9_\-]{40,}/g,severity:"high"},{name:"OpenAI API key",regex:/sk-(?:proj-)?[a-zA-Z0-9]{32,}/g,severity:"high"},{name:"AWS access key ID",regex:/AKIA[0-9A-Z]{16}/g,severity:"high"},{name:"GitHub PAT",regex:/gh[pousr]_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Stripe live/test key",regex:/(?:sk|rk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,severity:"high"},{name:"Slack token",regex:/xox[abprs]-[A-Za-z0-9\-]{10,}/g,severity:"high"},{name:"Google API key",regex:/AIza[0-9A-Za-z_\-]{35}/g,severity:"high"},{name:"Private key block",regex:/-----BEGIN (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,severity:"high"},{name:"Apify token",regex:/apify_api_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Notion integration",regex:/(?:secret_|ntn_)[A-Za-z0-9]{40,}/g,severity:"high"},{name:"Vercel token",regex:/vercel_[A-Za-z0-9]{24,}/g,severity:"high"},{name:"Supabase service key",regex:/sbp_[A-Za-z0-9]{40,}/g,severity:"high"},{name:"SendGrid key",regex:/SG\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}/g,severity:"high"},{name:"Mailgun key",regex:/key-[a-f0-9]{32}/g,severity:"high"},{name:"Twilio SID",regex:/AC[a-f0-9]{32}/g,severity:"high"},{name:"Discord bot token",regex:/[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27,38}/g,severity:"high"},{name:"npm token",regex:/npm_[A-Za-z0-9]{36}/g,severity:"high"},{name:"HuggingFace token",regex:/hf_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"Replicate token",regex:/r8_[A-Za-z0-9]{32,}/g,severity:"high"},{name:"Figma token",regex:/figd_[A-Za-z0-9_\-]{30,}/g,severity:"high"},{name:"Linear key",regex:/lin_api_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"DigitalOcean token",regex:/dop_v1_[a-f0-9]{64}/g,severity:"high"},{name:"Generic provider token",regex:/\b[a-z][a-z0-9]{2,20}_(?:api|pat|token|sk|pk|key|auth)_(?=[A-Za-z0-9_\-]*\d)[A-Za-z0-9_\-]{20,}\b/g,severity:"high"},{name:"Bearer token",regex:/\b[Bb]earer\s+[A-Za-z0-9_\-\.=]{24,}\b/g,severity:"medium"},{name:"Slack webhook URL",regex:/https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Discord webhook URL",regex:/https:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_\-]{40,}/g,severity:"high"},{name:"Teams webhook URL",regex:/https:\/\/[a-zA-Z0-9.\-]+\.webhook\.office\.com\/webhookb2\/[A-Za-z0-9@_\-\/]{30,}/g,severity:"high"},{name:"Secret near keyword",regex:/\b(?:webhook[_\s\-]?secret|signing[_\s\-]?secret|webhook[_\s\-]?signing[_\s\-]?secret|api[_\s\-]?secret|client[_\s\-]?secret|private[_\s\-]?key|access[_\s\-]?token|auth[_\s\-]?token|api[_\s\-]?key)\b[\s\S]{0,200}?\b(?:[a-fA-F0-9]{32,}|[A-Za-z0-9+/_\-]{20,}(?:\.[A-Za-z0-9+/_\-]{10,}){1,2}|[A-Za-z0-9+/_\-]{40,}={0,2})\b/gi,severity:"high"},{name:"JWT",regex:/eyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}/g,severity:"medium"},{name:"URL with password",regex:/https?:\/\/[^:\s/@]+:[^@\s]{6,}@[^\s/]+/g,severity:"high"},{name:"Password assignment",regex:/(?<![A-Za-z])(?:password|passwd|pwd|secret|token|api[_\-]?key|access[_\-]?key|auth[_\-]?token|webhook[_\-]?secret|client[_\-]?secret|private[_\-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_\-+/=]{16,}/gi,severity:"high"}];function tE(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),n=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-n.length))}${n}`}function nE(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(36)}function Se(e){if(!e)return{redacted:e,count:0};let t=e,n=0,s=new Set;for(let r of eE)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let a=`${r.name}::${nE(o)}`;return s.has(a)||(s.add(a),n+=1),`[REDACTED ${r.name}: ${tE(o)}]`});return{redacted:t,count:n}}Te();var _o=1,lt="claude-haiku-4-5-20251001",rE=3,oE=32e3,Yc=2e3,iE=30,aE=30,cE=30,lE=30;function uE(e){let n=h().prepare(`SELECT s.id,
918
985
  NULLIF(sa.alias, '') AS alias,
919
986
  s.auto_title,
920
987
  s.auto_title_source,
@@ -925,15 +992,15 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
925
992
  FROM sessions s
926
993
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
927
994
  LEFT JOIN projects p ON p.id = s.project_id
928
- WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function nc(e,t={}){if(e.message_count<bf)return{eligible:!1,reason:"too-short"};let n=e.title_quality;if(n==="programmatic"||n==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let s=Ve(e.id);if(s&&s.extractor_version>=vr)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function Af(e){let t=kf(e);if(!t)return null;let s=f().prepare(`SELECT role, content_text
995
+ WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function Kc(e,t={}){if(e.message_count<rE)return{eligible:!1,reason:"too-short"};let n=e.title_quality;if(n==="programmatic"||n==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let s=ot(e.id);if(s&&s.extractor_version>=_o)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function dE(e){let t=uE(e);if(!t)return null;let s=h().prepare(`SELECT role, content_text
929
996
  FROM messages
930
997
  WHERE session_id = ?
931
998
  AND is_sidechain = 0
932
999
  AND content_text IS NOT NULL
933
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>tc?u.slice(0,tc)+"\u2026":u,m=`${c}: ${d}`;if(o+m.length>Sf)break;r.push(m),o+=m.length}return{meta:t,excerpt:r.join(`
1000
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let a of s){let c=a.role??"system",u=a.content_text.trim();if(!u)continue;let d=u.length>Yc?u.slice(0,Yc)+"\u2026":u,p=`${c}: ${d}`;if(o+p.length>oE)break;r.push(p),o+=p.length}return{meta:t,excerpt:r.join(`
934
1001
 
935
- `)}}function xf(e){let{meta:t}=e;return["You are extracting a structured Output Index from a Claude Code session for a local knowledge graph.","","Read the transcript and produce a single JSON object with EXACTLY these fields. Output JSON only \u2014 no Markdown fences, no commentary, no explanation.","","Fields (every field MUST appear; use [] for empty):","- files_written: array of strings. Relative or absolute file paths the assistant created, edited, or extensively discussed (Write/Edit tool calls or paths quoted verbatim).",'- brands_mentioned: array of strings. Distinct proper-noun company / product / brand names mentioned. Examples of valid: "Glaser Group", "Apollo", "TikTok", "Cloudflare R2". NOT generic terms like "the company" or "users".','- terms_introduced: array of objects { "term": "<lowercase phrase>", "freq": <int> }. Distinctive multi-word noun phrases the session introduced or extensively discussed. Skip generic words. Cap at 30 entries.','- plan_ids_referenced: array of strings. Planning identifiers like "v0.18.A", "Phase D", "L3", "MASTER-PLAN-cognitive-graph". Empty array if none.','- bug_signatures: array of objects { "error_type": "<class or null>", "snippet": "<first line of the error>", "file": "<path or null>" }. Errors actually encountered in the session (not hypothetical). The error_type is the exception class (e.g. "TypeError", "ReferenceError") or null if unspecified.',"","Hard constraints:","- Do NOT fabricate. If a field has no concrete content from the transcript, output an empty array.","- Output JSON ONLY. No backticks, no markdown, no preamble.","- terms_introduced \u2264 30 entries; brands_mentioned \u2264 30 distinct entries; plan_ids_referenced \u2264 30; bug_signatures \u2264 30.","",`Session: ${t.alias??t.id.slice(0,8)}`,`Project: ${t.project??"unknown"}`,t.first_user_message?`Opening prompt: ${t.first_user_message.slice(0,500)}`:"","","Transcript excerpt (may be truncated):","---",e.excerpt,"---"].filter(Boolean).join(`
936
- `)}function Nf(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function Lr(e,t,n=!1){if(!Array.isArray(e))return[];let s=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=n?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!s.has(a)&&(s.add(a),r.push(a),r.length>=t))break}return r}function Of(e,t){if(!Array.isArray(e))return[];let n=new Set,s=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,a=r.freq;if(typeof o!="string")continue;let c=o.trim().toLowerCase();if(!c||c.length>128||n.has(c))continue;n.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(s.push({term:c,frequency:u}),s.length>=t)break}return s}function Lf(e,t){if(!Array.isArray(e))return[];let n=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.error_type,o=s.snippet,a=s.file,c=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",u=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!u)continue;let d=typeof a=="string"&&a.trim().length>0?a.trim().slice(0,256):null,m=Ef("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(n.push({error_type:c,message_hash:m,snippet:u,file:d}),n.length>=t)break}return n}function Cf(e){let t=e.trim();try{let b=JSON.parse(t);typeof b.result=="string"&&(t=b.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1),o;try{o=JSON.parse(r)}catch{return null}let a=Lr(o.files_written,200),c=Lr(o.brands_mentioned,yf),u=Of(o.terms_introduced,Tf),d=Lr(o.plan_ids_referenced,wf),m=Lf(o.bug_signatures,Rf);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&m.length===0&&!Nf(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:m}}var Cr=null;async function vf(e,t){return Cr?Cr(e,t):Qe(e,[],{model:t})}async function Ir(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let n=Af(e);if(!n)return{session_id:e,ok:!1,skipped:"session-not-found"};let s=nc(n.meta,{force:t.force});if(!s.eligible)return{session_id:e,ok:!1,skipped:s.reason};if(!Cr&&!pe())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=xf(n),o=t.model??tt,a=await vf(r,o);if(!a.success){let m=(a.stderr||a.stdout||"").slice(0,400),h=m?Se(m).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:h}}let c=Cf(a.stdout);if(!c){let m=a.stdout.slice(0,400),h=m?Se(m).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:h}}let u=If(a.stdout),d=ja({session_id:e,files_written:c.files_written,brands_mentioned:c.brands_mentioned,terms_introduced:c.terms_introduced,plan_ids_referenced:c.plan_ids_referenced,bug_signatures:c.bug_signatures,raw_extraction:{model:o,usage:u,raw_response_excerpt:a.stdout.slice(0,4e3)},extractor_version:vr});return{session_id:e,ok:!0,index:d,usage:u}}function If(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let n=t.usage,s={};return typeof n.input_tokens=="number"&&(s.input_tokens=n.input_tokens),typeof n.output_tokens=="number"&&(s.output_tokens=n.output_tokens),s}}catch{}return null}function ft(e={}){let t=f(),n=[],s=[];typeof e.projectId=="number"&&(n.push("s.project_id = ?"),s.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=n.length?`WHERE ${n.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
1002
+ `)}}function pE(e){let{meta:t}=e;return["You are extracting a structured Output Index from a Claude Code session for a local knowledge graph.","","Read the transcript and produce a single JSON object with EXACTLY these fields. Output JSON only \u2014 no Markdown fences, no commentary, no explanation.","","Fields (every field MUST appear; use [] for empty):","- files_written: array of strings. Relative or absolute file paths the assistant created, edited, or extensively discussed (Write/Edit tool calls or paths quoted verbatim).",'- brands_mentioned: array of strings. Distinct proper-noun company / product / brand names mentioned. Examples of valid: "Glaser Group", "Apollo", "TikTok", "Cloudflare R2". NOT generic terms like "the company" or "users".','- terms_introduced: array of objects { "term": "<lowercase phrase>", "freq": <int> }. Distinctive multi-word noun phrases the session introduced or extensively discussed. Skip generic words. Cap at 30 entries.','- plan_ids_referenced: array of strings. Planning identifiers like "v0.18.A", "Phase D", "L3", "MASTER-PLAN-cognitive-graph". Empty array if none.','- bug_signatures: array of objects { "error_type": "<class or null>", "snippet": "<first line of the error>", "file": "<path or null>" }. Errors actually encountered in the session (not hypothetical). The error_type is the exception class (e.g. "TypeError", "ReferenceError") or null if unspecified.',"","Hard constraints:","- Do NOT fabricate. If a field has no concrete content from the transcript, output an empty array.","- Output JSON ONLY. No backticks, no markdown, no preamble.","- terms_introduced \u2264 30 entries; brands_mentioned \u2264 30 distinct entries; plan_ids_referenced \u2264 30; bug_signatures \u2264 30.","",`Session: ${t.alias??t.id.slice(0,8)}`,`Project: ${t.project??"unknown"}`,t.first_user_message?`Opening prompt: ${t.first_user_message.slice(0,500)}`:"","","Transcript excerpt (may be truncated):","---",e.excerpt,"---"].filter(Boolean).join(`
1003
+ `)}function mE(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function go(e,t,n=!1){if(!Array.isArray(e))return[];let s=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let a=n?o.trim().toLowerCase():o.trim();if(!(!a||a.length>256)&&!s.has(a)&&(s.add(a),r.push(a),r.length>=t))break}return r}function gE(e,t){if(!Array.isArray(e))return[];let n=new Set,s=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,a=r.freq;if(typeof o!="string")continue;let c=o.trim().toLowerCase();if(!c||c.length>128||n.has(c))continue;n.add(c);let u=typeof a=="number"&&Number.isFinite(a)&&a>0?Math.floor(a):1;if(s.push({term:c,frequency:u}),s.length>=t)break}return s}function fE(e,t){if(!Array.isArray(e))return[];let n=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.error_type,o=s.snippet,a=s.file,c=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",u=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!u)continue;let d=typeof a=="string"&&a.trim().length>0?a.trim().slice(0,256):null,p=sE("sha256").update(`${c}::${u}`).digest("hex").slice(0,12);if(n.push({error_type:c,message_hash:p,snippet:u,file:d}),n.length>=t)break}return n}function _E(e){let t=e.trim();try{let S=JSON.parse(t);typeof S.result=="string"&&(t=S.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1),o;try{o=JSON.parse(r)}catch{return null}let a=go(o.files_written,200),c=go(o.brands_mentioned,aE),u=gE(o.terms_introduced,iE),d=go(o.plan_ids_referenced,cE),p=fE(o.bug_signatures,lE);return a.length===0&&c.length===0&&u.length===0&&d.length===0&&p.length===0&&!mE(o.files_written)?null:{files_written:a,brands_mentioned:c,terms_introduced:u,plan_ids_referenced:d,bug_signatures:p}}var fo=null;async function hE(e,t){return fo?fo(e,t):at(e,[],{model:t})}async function ho(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let n=dE(e);if(!n)return{session_id:e,ok:!1,skipped:"session-not-found"};let s=Kc(n.meta,{force:t.force});if(!s.eligible)return{session_id:e,ok:!1,skipped:s.reason};if(!fo&&!me())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=pE(n),o=t.model??lt,a=await hE(r,o);if(!a.success){let p=(a.stderr||a.stdout||"").slice(0,400),f=p?Se(p).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:a.exitCode,stderr_excerpt:f}}let c=_E(a.stdout);if(!c){let p=a.stdout.slice(0,400),f=p?Se(p).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:a.exitCode,stderr_excerpt:f}}let u=EE(a.stdout),d=xc({session_id:e,files_written:c.files_written,brands_mentioned:c.brands_mentioned,terms_introduced:c.terms_introduced,plan_ids_referenced:c.plan_ids_referenced,bug_signatures:c.bug_signatures,raw_extraction:{model:o,usage:u,raw_response_excerpt:a.stdout.slice(0,4e3)},extractor_version:_o});return{session_id:e,ok:!0,index:d,usage:u}}function EE(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let n=t.usage,s={};return typeof n.input_tokens=="number"&&(s.input_tokens=n.input_tokens),typeof n.output_tokens=="number"&&(s.output_tokens=n.output_tokens),s}}catch{}return null}function xt(e={}){let t=h(),n=[],s=[];typeof e.projectId=="number"&&(n.push("s.project_id = ?"),s.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=n.length?`WHERE ${n.join(" AND ")}`:"",a=t.prepare(`SELECT s.id,
937
1004
  NULLIF(sa.alias, '') AS alias,
938
1005
  s.auto_title,
939
1006
  s.auto_title_source,
@@ -945,33 +1012,33 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
945
1012
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
946
1013
  LEFT JOIN projects p ON p.id = s.project_id
947
1014
  ${o}
948
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(...s),c=[],u=new Map;for(let d of a){let m={...d,alias_source:d.alias?"manual":null},h=nc(m,{force:e.force});if(!h.eligible){let b=h.reason??"session-not-found";u.set(b,(u.get(b)??0)+1);continue}if(c.push(m),c.length>=r)break}return{eligible:c,skipped:u}}async function sc(e={}){let t=ft({projectId:e.projectId,limit:e.limit,force:e.force}),n={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())n.skipped+=r;e.onProgress?.({...n});let s=[];for(let r of t.eligible){if(e.signal?.aborted)break;n.current_session_id=r.id,e.onProgress?.({...n});let o=await Ir(r.id,{model:e.model,force:e.force,signal:e.signal});s.push(o),e.onResult?.(o),o.ok?n.ok+=1:o.skipped?n.skipped+=1:n.failed+=1,o.usage?.input_tokens&&(n.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(n.total_output_tokens+=o.usage.output_tokens),n.processed+=1,e.onProgress?.({...n})}return n.current_session_id=null,e.onProgress?.({...n}),{progress:n,results:s}}ye();var ne="[daemon:inference]",$r=!1,Ur=!1,Br=!1,Hr=!1,Wr=!1,_c=0,qr=!1,Xr=!1,Vn=3,nt=0,ht=!1,Zn=null,Et=null,fc=!1;function zf(){return f().prepare("SELECT id, name FROM projects").all()}async function hc(){if($r)return;$r=!0;let e=Date.now();try{let n=(await Sa({minClusterSize:2})).progress;(n.clusters_created||n.clusters_merged||n.members_added)&&console.log(`${ne} bug-patterns: created=${n.clusters_created} merged=${n.clusters_merged} members_added=${n.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${ne} bug-patterns failed:`,t)}finally{$r=!1}}async function Ec(){if(Ur)return;Ur=!0;let e=Date.now();try{let t=0,n=0;for(let s of zf())try{let r=await Ma({projectId:s.id});t+=r.progress.suggestions_created,n+=1}catch(r){console.error(`${ne} citations failed for project "${s.name}":`,r)}t>0&&console.log(`${ne} citations: ${t} suggestion(s) across ${n} project(s) (${Date.now()-e}ms)`)}catch(t){console.error(`${ne} citations failed:`,t)}finally{Ur=!1}}function bc(){if(Br)return;Br=!0;let e=Date.now();try{let t=Pa({}),n=$a({});t.created+n.created>0&&console.log(`${ne} l1: uuid=${t.created} plan=${n.created} (${Date.now()-e}ms)`)}catch(t){console.error(`${ne} l1 failed:`,t)}finally{Br=!1}}async function Sc(){if(Hr)return;let e=ce();if(!e.enabled||e.backfillPaused)return;Hr=!0;let t=Date.now();try{let n=await Jn({limit:25});n.processed>0&&console.log(`${ne} backfill: processed=${n.processed} ok=${n.ok} failed=${n.failed} (${Date.now()-t}ms)`)}catch(n){console.error(`${ne} backfill failed:`,n)}finally{Hr=!1}}async function Tc(){if(Wr)return;let e=ce();if(e.autoExtractEnabled&&!fc&&ht&&(Kf(),console.log(`${ne} auto-extract: circuit breaker reset (config toggled on).`)),fc=e.autoExtractEnabled,!e.autoExtractEnabled||ht)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-_c<t)return;if(!pe()){qr||(console.log(`${ne} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),qr=!0);return}if(qr=!1,!await zi().catch(()=>!1)){Xr||(console.log(`${ne} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),Xr=!0);return}Xr=!1,Wr=!0,_c=Date.now();let s=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=ft({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,m=new Map,h=null;for(let T of o){let R=await Ir(T.id,{model:tt});if(R.ok)a+=1;else if(!R.skipped){c+=1;let O=R.failed??"unknown";m.set(O,(m.get(O)??0)+1),!h&&R.stderr_excerpt&&(h=R.stderr_excerpt)}R.usage?.input_tokens&&(u+=R.usage.input_tokens),R.usage?.output_tokens&&(d+=R.usage.output_tokens)}let b=m.size>0?" reasons="+Array.from(m.entries()).map(([T,R])=>`${T}:${R}`).join(","):"";if(console.log(`${ne} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-s}ms)${b}`),h){let R=Se(h).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${ne} auto-extract: first failure excerpt: ${R}`)}o.length>0&&u===0&&d===0?(nt+=1,nt>=Vn&&(ht=!0,Zn=Date.now(),Et=`${Vn} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${ne} auto-extract: CIRCUIT BREAKER tripped \u2014 ${Et}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(nt=0)}catch(r){console.error(`${ne} auto-extract failed:`,r),nt+=1,nt>=Vn&&(ht=!0,Zn=Date.now(),Et=`${Vn} consecutive thrown errors`,console.log(`${ne} auto-extract: CIRCUIT BREAKER tripped \u2014 ${Et}. Pausing nibbler.`))}finally{Wr=!1}}function wc(){return{broken:ht,brokenAt:Zn,reason:Et,consecutiveZeroTokenRuns:nt}}function Kf(){ht=!1,Zn=null,Et=null,nt=0}var Jr=!1;async function yc(){if(Jr)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(Dr(),lc)),n=e();if(!n.autoArchiveEnabled)return;let s=1380*60*1e3;if(n.lastRunAt&&Date.now()-Date.parse(n.lastRunAt)<s)return;Jr=!0;let r=Date.now();try{let a=new Date(Date.now()-n.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(gc(),mc)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${ne} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${ne} auto-archive failed: ${a}`)}finally{Jr=!1}}function Rc(){let m=[],h=[];m.push(setTimeout(()=>{hc()},9e4)),h.push(setInterval(()=>{hc()},18e5)),m.push(setTimeout(()=>{Ec()},18e4)),h.push(setInterval(()=>{Ec()},36e5)),m.push(setTimeout(()=>bc(),12e4)),h.push(setInterval(()=>bc(),18e5)),m.push(setTimeout(()=>{Sc()},24e4)),h.push(setInterval(()=>{Sc()},9e5)),m.push(setTimeout(()=>{Tc()},3e5)),h.push(setInterval(()=>{Tc()},9e5));let b=3600*1e3,S=300*1e3;return m.push(setTimeout(()=>{yc()},S)),h.push(setInterval(()=>{yc()},b)),console.log(`${ne} scheduled: bug-patterns (30m), citations (60m), l1 (30m), backfill (15m, when enabled), auto-extract (60m, when enabled), auto-archive (24h, when enabled)`),{startupTimers:m,intervalTimers:h,stop:()=>{for(let T of m)clearTimeout(T);for(let T of h)clearInterval(T)}}}B();import{serveStatic as cm}from"@hono/node-server/serve-static";import{existsSync as jw,readFileSync as Oi}from"node:fs";import{stat as Mw,readFile as Dw,realpath as Fw}from"node:fs/promises";import{timingSafeEqual as Pw}from"node:crypto";import{homedir as $w}from"node:os";import{dirname as vi,join as sr}from"node:path";import{fileURLToPath as Ii}from"node:url";import{existsSync as aN,readFileSync as cN,statSync as lN,statfsSync as Ob}from"node:fs";Kn();B();ee();ee();B();import{watch as ib}from"chokidar";import{readdirSync as ab,statSync as bo}from"node:fs";import{createReadStream as Vf}from"node:fs";import{createInterface as Zf}from"node:readline";function Qn(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function zr(e){let t=e?.usage;if(!t||typeof t!="object")return null;let n={inputTokens:Qn(t.input_tokens),outputTokens:Qn(t.output_tokens),cacheCreateTokens:Qn(t.cache_creation_input_tokens),cacheReadTokens:Qn(t.cache_read_input_tokens)};return n.inputTokens===0&&n.outputTokens===0&&n.cacheCreateTokens===0&&n.cacheReadTokens===0?null:n}var Qf=/\x1B\[[0-9;]*[a-zA-Z]/g;function Gr(e){return e.replace(Qf,"")}var Yr=12e3;function kc(e,t){if(e.length<=Yr)return e;let n=e.slice(0,Yr),s=e.length-Yr;return`${n}
1015
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(...s),c=[],u=new Map;for(let d of a){let p={...d,alias_source:d.alias?"manual":null},f=Kc(p,{force:e.force});if(!f.eligible){let S=f.reason??"session-not-found";u.set(S,(u.get(S)??0)+1);continue}if(c.push(p),c.length>=r)break}return{eligible:c,skipped:u}}async function zc(e={}){let t=xt({projectId:e.projectId,limit:e.limit,force:e.force}),n={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())n.skipped+=r;e.onProgress?.({...n});let s=[];for(let r of t.eligible){if(e.signal?.aborted)break;n.current_session_id=r.id,e.onProgress?.({...n});let o=await ho(r.id,{model:e.model,force:e.force,signal:e.signal});s.push(o),e.onResult?.(o),o.ok?n.ok+=1:o.skipped?n.skipped+=1:n.failed+=1,o.usage?.input_tokens&&(n.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(n.total_output_tokens+=o.usage.output_tokens),n.processed+=1,e.onProgress?.({...n})}return n.current_session_id=null,e.onProgress?.({...n}),{progress:n,results:s}}Te();var se="[daemon:inference]",wo=!1,Ro=!1,ko=!1,Ao=!1,xo=!1,cl=0,No=!1,Oo=!1,Ts=3,ut=0,Nt=!1,ws=null,Ot=null,ll=!1;function ME(){return h().prepare("SELECT id, name FROM projects").all()}async function ul(){if(wo)return;wo=!0;let e=Date.now();try{let n=(await mc({minClusterSize:2})).progress;(n.clusters_created||n.clusters_merged||n.members_added)&&console.log(`${se} bug-patterns: created=${n.clusters_created} merged=${n.clusters_merged} members_added=${n.members_added} (${Date.now()-e}ms)`)}catch(t){console.error(`${se} bug-patterns failed:`,t)}finally{wo=!1}}async function dl(){if(Ro)return;Ro=!0;let e=Date.now();try{let t=0,n=0;for(let s of ME())try{let r=await Nc({projectId:s.id});t+=r.progress.suggestions_created,n+=1}catch(r){console.error(`${se} citations failed for project "${s.name}":`,r)}t>0&&console.log(`${se} citations: ${t} suggestion(s) across ${n} project(s) (${Date.now()-e}ms)`)}catch(t){console.error(`${se} citations failed:`,t)}finally{Ro=!1}}function pl(){if(ko)return;ko=!0;let e=Date.now();try{let t=Lc({}),n=vc({});t.created+n.created>0&&console.log(`${se} l1: uuid=${t.created} plan=${n.created} (${Date.now()-e}ms)`)}catch(t){console.error(`${se} l1 failed:`,t)}finally{ko=!1}}async function ml(){if(Ao)return;let e=ae();if(!e.enabled||e.backfillPaused)return;Ao=!0;let t=Date.now();try{let n=await hs({limit:25});n.processed>0&&console.log(`${se} backfill: processed=${n.processed} ok=${n.ok} failed=${n.failed} (${Date.now()-t}ms)`)}catch(n){console.error(`${se} backfill failed:`,n)}finally{Ao=!1}}async function gl(){if(xo)return;let e=ae();if(e.autoExtractEnabled&&!ll&&Nt&&(jE(),console.log(`${se} auto-extract: circuit breaker reset (config toggled on).`)),ll=e.autoExtractEnabled,!e.autoExtractEnabled||Nt)return;let t=e.autoExtractIntervalMinutes*60*1e3;if(Date.now()-cl<t)return;if(!me()){No||(console.log(`${se} auto-extract: claude CLI not on PATH \u2014 pausing nibbler (will retry; install Claude Code or run \`recall semantic auto-extract off\` to silence)`),No=!0);return}if(No=!1,!await Ba().catch(()=>!1)){Oo||(console.log(`${se} auto-extract: Pro license required \u2014 pausing nibbler (run \`recall semantic auto-extract off\` to silence; upgrade unlocks)`),Oo=!0);return}Oo=!1,xo=!0,cl=Date.now();let s=Date.now();try{let r=e.autoExtractBatchSize,{eligible:o}=xt({limit:r});if(o.length===0)return;let a=0,c=0,u=0,d=0,p=new Map,f=null;for(let b of o){let R=await ho(b.id,{model:lt});if(R.ok)a+=1;else if(!R.skipped){c+=1;let k=R.failed??"unknown";p.set(k,(p.get(k)??0)+1),!f&&R.stderr_excerpt&&(f=R.stderr_excerpt)}R.usage?.input_tokens&&(u+=R.usage.input_tokens),R.usage?.output_tokens&&(d+=R.usage.output_tokens)}let S=p.size>0?" reasons="+Array.from(p.entries()).map(([b,R])=>`${b}:${R}`).join(","):"";if(console.log(`${se} auto-extract: processed=${o.length} ok=${a} failed=${c} tokens=${u}+${d} (${Date.now()-s}ms)${S}`),f){let R=Se(f).redacted.replace(/\s+/g," ").trim().slice(0,300);console.log(`${se} auto-extract: first failure excerpt: ${R}`)}o.length>0&&u===0&&d===0?(ut+=1,ut>=Ts&&(Nt=!0,ws=Date.now(),Ot=`${Ts} consecutive zero-token runs (claude CLI returning no usage)`,console.log(`${se} auto-extract: CIRCUIT BREAKER tripped \u2014 ${Ot}. Pausing nibbler. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`))):(a>0||u>0||d>0)&&(ut=0)}catch(r){console.error(`${se} auto-extract failed:`,r),ut+=1,ut>=Ts&&(Nt=!0,ws=Date.now(),Ot=`${Ts} consecutive thrown errors`,console.log(`${se} auto-extract: CIRCUIT BREAKER tripped \u2014 ${Ot}. Pausing nibbler.`))}finally{xo=!1}}function _l(){return{broken:Nt,brokenAt:ws,reason:Ot,consecutiveZeroTokenRuns:ut}}function jE(){Nt=!1,ws=null,Ot=null,ut=0}var Co=!1;async function fl(){if(Co)return;let{readRetentionConfig:e,writeRetentionConfig:t}=await Promise.resolve().then(()=>(So(),nl)),n=e();if(!n.autoArchiveEnabled)return;let s=1380*60*1e3;if(n.lastRunAt&&Date.now()-Date.parse(n.lastRunAt)<s)return;Co=!0;let r=Date.now();try{let a=new Date(Date.now()-n.autoArchiveAfterDays*24*60*60*1e3).toISOString().slice(0,10),{runArchive:c}=await Promise.resolve().then(()=>(al(),il)),u=await c({_action:"run",before:a,dryRun:!1});t({lastRunAt:new Date().toISOString()}),console.log(`${se} auto-archive: cutoff=${a} exit=${u} (${Date.now()-r}ms)`)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${se} auto-archive failed: ${a}`)}finally{Co=!1}}function hl(){let p=[],f=[];p.push(setTimeout(()=>{ul()},9e4)),f.push(setInterval(()=>{ul()},18e5)),p.push(setTimeout(()=>{dl()},18e4)),f.push(setInterval(()=>{dl()},36e5)),p.push(setTimeout(()=>pl(),12e4)),f.push(setInterval(()=>pl(),18e5)),p.push(setTimeout(()=>{ml()},24e4)),f.push(setInterval(()=>{ml()},9e5)),p.push(setTimeout(()=>{gl()},3e5)),f.push(setInterval(()=>{gl()},9e5));let S=3600*1e3,y=300*1e3;return p.push(setTimeout(()=>{fl()},y)),f.push(setInterval(()=>{fl()},S)),console.log(`${se} scheduled: bug-patterns (30m), citations (60m), l1 (30m), backfill (15m, when enabled), auto-extract (60m, when enabled), auto-archive (24h, when enabled)`),{startupTimers:p,intervalTimers:f,stop:()=>{for(let b of p)clearTimeout(b);for(let b of f)clearInterval(b)}}}q();import{serveStatic as Ig}from"@hono/node-server/serve-static";import{existsSync as xk,readFileSync as Ra}from"node:fs";import{stat as Nk,readFile as Ok,realpath as Ck}from"node:fs/promises";import{timingSafeEqual as Lk}from"node:crypto";import{homedir as vk}from"node:os";import{dirname as xa,join as Dr}from"node:path";import{fileURLToPath as Na}from"node:url";import{existsSync as wi,readdirSync as NT,readFileSync as bd,statSync as OT,statfsSync as Sd}from"node:fs";import{homedir as CT}from"node:os";import{join as yd}from"node:path";ys();q();ne();ne();q();import{watch as dy}from"chokidar";import{readdirSync as py,statSync as Vs}from"node:fs";import{createReadStream as DE}from"node:fs";import{createInterface as PE}from"node:readline";function Rs(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Io(e){let t=e?.usage;if(!t||typeof t!="object")return null;let n={inputTokens:Rs(t.input_tokens),outputTokens:Rs(t.output_tokens),cacheCreateTokens:Rs(t.cache_creation_input_tokens),cacheReadTokens:Rs(t.cache_read_input_tokens)};return n.inputTokens===0&&n.outputTokens===0&&n.cacheCreateTokens===0&&n.cacheReadTokens===0?null:n}var FE=/\x1B\[[0-9;]*[a-zA-Z]/g;function Lo(e){return e.replace(FE,"")}var vo=12e3;function El(e,t){if(e.length<=vo)return e;let n=e.slice(0,vo),s=e.length-vo;return`${n}
949
1016
 
950
- \u27E8\u2026 ${s.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function eh(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function th(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let n of e.content)if(n&&typeof n=="object"){let s=n;s.type==="text"&&typeof s.text=="string"?t.push(s.text):s.type==="image"&&t.push("[image]")}return t.join(`
951
- `)}return""}function nh(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Gr(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],n=[];for(let s of e.content)if(!(!s||typeof s!="object")){if(s.type==="text"&&typeof s.text=="string"){t.push(Gr(s.text));continue}if(s.type==="tool_use"&&typeof s.name=="string"){n.push(s.name);let r=s.input!=null?eh(s.input):"",o=kc(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${s.name}\`**
1017
+ \u27E8\u2026 ${s.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function $E(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function UE(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let n of e.content)if(n&&typeof n=="object"){let s=n;s.type==="text"&&typeof s.text=="string"?t.push(s.text):s.type==="image"&&t.push("[image]")}return t.join(`
1018
+ `)}return""}function HE(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Lo(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],n=[];for(let s of e.content)if(!(!s||typeof s!="object")){if(s.type==="text"&&typeof s.text=="string"){t.push(Lo(s.text));continue}if(s.type==="tool_use"&&typeof s.name=="string"){n.push(s.name);let r=s.input!=null?$E(s.input):"",o=El(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${s.name}\`**
952
1019
 
953
1020
  \`\`\`json
954
1021
  ${o}
955
- \`\`\``);continue}if(s.type==="tool_result"){let r=Gr(th(s));if(r){let o=kc(r,"tool result");t.push(`**Tool result**
1022
+ \`\`\``);continue}if(s.type==="tool_result"){let r=Lo(UE(s));if(r){let o=El(r,"tool result");t.push(`**Tool result**
956
1023
 
957
1024
  \`\`\`
958
1025
  ${o}
959
1026
  \`\`\``)}else t.push("_(tool result was empty or image-only)_");continue}if(s.type==="image"){t.push("_(image)_");continue}t.push(`_(unknown block: ${s.type})_`)}return{text:t.join(`
960
1027
 
961
- `),toolNames:n}}async function*Ac(e){let t=Vf(e,{encoding:"utf8"}),n=Zf({input:t,crlfDelay:1/0});for await(let s of n){if(!s.trim())continue;let r;try{r=JSON.parse(s)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:a}=nh(r.message);yield{uuid:r.uuid,parentUuid:r.parentUuid??null,sessionId:r.sessionId,type:r.type??"unknown",role:r.message?.role??null,timestamp:r.timestamp??null,isSidechain:r.isSidechain===!0,cwd:r.cwd??null,gitBranch:r.gitBranch??null,version:r.version??null,contentText:o,toolNames:a,raw:s,usage:zr(r.message),model:r.message?.model??null}}}import{basename as Cl,join as cb}from"node:path";import{execFile as kh}from"node:child_process";import{promisify as Ah}from"node:util";import{existsSync as xh}from"node:fs";import{basename as Nh}from"node:path";ee();import{existsSync as sh,readFileSync as rh,writeFileSync as oh}from"node:fs";import{join as ih}from"node:path";import{z as fe}from"zod";var Kr=ih(W,"terminals.json"),xc=1440*60*1e3,ah=3e4,ch=6e4,lh=fe.object({shell_pid:fe.number(),tab_name:fe.string(),cwd:fe.string().nullable().optional(),opened_at:fe.string(),last_seen_at:fe.string()}),uh=fe.object({schema:fe.string().optional(),saved_at:fe.string().optional(),terminals:fe.array(lh).max(500).default([]),sessions_by_pid:fe.record(fe.string(),fe.array(fe.string()).max(50)).optional().default({})}),Nc=/^[⠀-⣿✳\s]+/,Oc=/^\d+(\.\d+){1,3}$/;function le(e){let t=e.trim();return!!(!t||Nc.test(t)||Oc.test(t))}function bt(e){let t=e.trim();if(!t||Oc.test(t))return null;let n=t.replace(Nc,"").trim();return n.length>0?n:null}function Vr(e,t){if(!le(e))return e;let n=bt(e);return n||(t&&!le(t)?t:e)}function dh(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var Zr=class{entries=new Map;origins=new Map;sessionsByPid=new Map;pendingClaudeStarts=[];deferredLinks=new Map;pidOwnership=new Map;loaded=!1;ensureLoaded(){if(!this.loaded&&(this.loaded=!0,!!sh(Kr)))try{let t=rh(Kr,"utf8"),n=JSON.parse(t),s=uh.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,a]of Object.entries(r.sessions_by_pid??{})){let c=Number(o);if(!Number.isFinite(c))continue;let u=new Set;for(let d of a)d.length>0&&u.add(d);u.size>0&&this.sessionsByPid.set(c,u)}this.gc()}catch{}}save(){try{z();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};oh(Kr,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=Vr(t.tab_name,s?.tab_name),o=s?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:n};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=Vr(n,s.tab_name),o={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>ch?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=Vr(o.tab_name,a?.tab_name),u=a?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:c,opened_at:u,last_seen_at:n}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=dh({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()-ah;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()-xc;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()-xc;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,o=0;for(let[a,c]of this.sessionsByPid){let u=this.entries.get(a);if(u){let d=Date.parse(u.last_seen_at);if(Number.isFinite(d)&&d>=s)continue}o+=c.size,this.sessionsByPid.delete(a),u&&(this.entries.delete(a),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let o=this.sessionsByPid.get(s);o&&(n+=o.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},M=new Zr;B();ee();import{writeFileSync as ph}from"node:fs";import{join as mh}from"node:path";var gh=mh(W,"aliases.json");function Qr(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Te(e){return f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function _h(){return f().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:Qr(t.previous_aliases)}))}function he(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=f(),r=new Date().toISOString(),o=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=Qr(o.previous_aliases),o.alias!==n&&a.push({alias:o.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
1028
+ `),toolNames:n}}async function*bl(e){let t=DE(e,{encoding:"utf8"}),n=PE({input:t,crlfDelay:1/0});for await(let s of n){if(!s.trim())continue;let r;try{r=JSON.parse(s)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:a}=HE(r.message);yield{uuid:r.uuid,parentUuid:r.parentUuid??null,sessionId:r.sessionId,type:r.type??"unknown",role:r.message?.role??null,timestamp:r.timestamp??null,isSidechain:r.isSidechain===!0,cwd:r.cwd??null,gitBranch:r.gitBranch??null,version:r.version??null,contentText:o,toolNames:a,raw:s,usage:Io(r.message),model:r.message?.model??null}}}import{basename as zs,join as my}from"node:path";import{execFile as ub}from"node:child_process";import{promisify as db}from"node:util";import{existsSync as pb}from"node:fs";import{basename as mb}from"node:path";ne();import{existsSync as BE,readFileSync as WE,writeFileSync as qE}from"node:fs";import{join as XE}from"node:path";import{z as _e}from"zod";var Mo=XE(W,"terminals.json"),Sl=1440*60*1e3,JE=3e4,GE=6e4,YE=_e.object({shell_pid:_e.number(),tab_name:_e.string(),cwd:_e.string().nullable().optional(),opened_at:_e.string(),last_seen_at:_e.string()}),KE=_e.object({schema:_e.string().optional(),saved_at:_e.string().optional(),terminals:_e.array(YE).max(500).default([]),sessions_by_pid:_e.record(_e.string(),_e.array(_e.string()).max(50)).optional().default({})}),yl=/^[⠀-⣿✳\s]+/,Tl=/^\d+(\.\d+){1,3}$/;function le(e){let t=e.trim();return!!(!t||yl.test(t)||Tl.test(t))}function Ct(e){let t=e.trim();if(!t||Tl.test(t))return null;let n=t.replace(yl,"").trim();return n.length>0?n:null}function jo(e,t){if(!le(e))return e;let n=Ct(e);return n||(t&&!le(t)?t:e)}function zE(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var Do=class{entries=new Map;origins=new Map;sessionsByPid=new Map;pendingClaudeStarts=[];deferredLinks=new Map;pidOwnership=new Map;loaded=!1;ensureLoaded(){if(!this.loaded&&(this.loaded=!0,!!BE(Mo)))try{let t=WE(Mo,"utf8"),n=JSON.parse(t),s=KE.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,a]of Object.entries(r.sessions_by_pid??{})){let c=Number(o);if(!Number.isFinite(c))continue;let u=new Set;for(let d of a)d.length>0&&u.add(d);u.size>0&&this.sessionsByPid.set(c,u)}this.gc()}catch{}}save(){try{V();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};qE(Mo,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=jo(t.tab_name,s?.tab_name),o=s?.opened_at??t.opened_at,a={...t,tab_name:r,opened_at:o,last_seen_at:n};return this.entries.set(t.shell_pid,a),this.gc(),this.save(),a}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=jo(n,s.tab_name),o={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>GE?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let o of t){let a=this.entries.get(o.shell_pid),c=jo(o.tab_name,a?.tab_name),u=a?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:c,opened_at:u,last_seen_at:n}),a?(a.tab_name!==c||a.cwd!==o.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=zE({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(n.kind==="pid-match"||n.kind==="singleton-cwd"){let s=this.pendingClaudeStarts.indexOf(n.entry);s>=0&&this.pendingClaudeStarts.splice(s,1)}return n}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,n,s,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:n,queued_at:Date.now(),cwd:s,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,n])=>({session_id:t,...n}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[n,s]of this.deferredLinks)s.queued_at<t&&this.deferredLinks.delete(n)}gcPending(){let t=Date.now()-JE;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(n=>{let s=Date.parse(n.started_at);return Number.isFinite(s)&&s>=t}))}outputTails=new Map;setOutputTail(t,n,s){this.ensureLoaded(),this.outputTails.set(t,{text:n,captured_at:s})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,n){this.ensureLoaded(),this.origins.set(t,n),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-Sl;for(let[n,s]of this.entries){let r=Date.parse(s.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(n)}}gcOrigins(){let t=Date.now()-Sl;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,o=0;for(let[a,c]of this.sessionsByPid){let u=this.entries.get(a);if(u){let d=Date.parse(u.last_seen_at);if(Number.isFinite(d)&&d>=s)continue}o+=c.size,this.sessionsByPid.delete(a),u&&(this.entries.delete(a),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(a){a.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let o=this.sessionsByPid.get(s);o&&(n+=o.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},j=new Do;q();ne();import{writeFileSync as VE}from"node:fs";import{join as QE}from"node:path";var ZE=QE(W,"aliases.json");function Po(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function ye(e){return h().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function eb(){return h().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:Po(t.previous_aliases)}))}function he(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=h(),r=new Date().toISOString(),o=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),a=[];return o&&(a=Po(o.previous_aliases),o.alias!==n&&a.push({alias:o.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
962
1029
  VALUES (?, ?, ?, ?)
963
1030
  ON CONFLICT(session_id) DO UPDATE SET
964
1031
  alias = excluded.alias,
965
1032
  updated_at = excluded.updated_at,
966
- previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(a)),Lc(),{session_id:e,alias:n,updated_at:r,previous_aliases:a}}function es(e){let t=f(),n=new Date().toISOString(),s=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!s)return;let r=Qr(s.previous_aliases);r.push({alias:s.alias,replaced_at:n}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
967
- WHERE session_id = ?`).run(n,JSON.stringify(r),e),Lc()}function Lc(){try{z();let e=_h(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};ph(gh,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}B();import{execFile as fh}from"node:child_process";import{readFile as hh}from"node:fs/promises";import{promisify as Eh}from"node:util";var bh=Eh(fh),Cc=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function Sh(e){let t={};for(let n of Cc){let s=e[n];typeof s=="string"&&s.length>0&&(t[n]=s)}return t}function Th(e){let t=Date.now();if(e.CURSOR_TRACE_ID)return{editor:"cursor",label:"Cursor",detectedAt:t};if(e.VSCODE_PID||e.VSCODE_INJECTION)return{editor:"vscode",label:"VS Code",detectedAt:t};let n=e.TERM_PROGRAM;return n==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:n==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:n==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:n==="WezTerm"?{editor:"wezterm",label:"WezTerm",detectedAt:t}:e.WT_SESSION?{editor:"windows-terminal",label:"Windows Terminal",detectedAt:t}:e.KITTY_WINDOW_ID?{editor:"kitty",label:"Kitty",detectedAt:t}:e.TERM==="alacritty"||e.ALACRITTY_SOCKET?{editor:"alacritty",label:"Alacritty",detectedAt:t}:null}async function yh(e){if(process.platform==="linux")try{let t=await hh(`/proc/${e}/environ`,"utf8");return wh(t)}catch{return{}}try{let{stdout:t}=await bh("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return Rh(t)}catch{return{}}}function wh(e){let t={};for(let n of e.split("\0")){if(!n)continue;let s=n.indexOf("=");if(s<=0)continue;let r=n.slice(0,s),o=n.slice(s+1);t[r]=o}return t}function Rh(e){let t={},n=new Set(Cc),s=e.replace(/\s+/g," ").trim().split(" ");for(let r of s){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);n.has(a)&&(t[a]=r.slice(o+1))}return t}async function vc(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await yh(e),n=Sh(t);return Th(n)}catch{return null}}var ns=Ah(kh),ts;function Oh(){if(ts!==void 0)return ts;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(xh(t))return ts=t,t;return ts=null,null}var Lh=3,Ch=3600*1e3,en=new Map;function Mc(){let e=M.all(),t=e.map(n=>n.shell_pid).sort((n,s)=>n-s).join(",");return`${e.length}:${t}`}function vh(e){let t=en.get(e);return t?Date.now()-t.lastAt>Ch?(en.delete(e),!1):t.refusals<Lh?!1:t.fingerprint===Mc():!1}function Ic(e){let t=Mc(),n=en.get(e);n&&n.fingerprint===t?(n.refusals+=1,n.lastAt=Date.now()):en.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function Ih(e){en.delete(e)}async function jh(e){let t=Oh();if(!t)return null;try{let{stdout:n}=await ns(t,["-Fpc",e],{timeout:2e3}),s=n.split(`
968
- `),r=null,o=null,a=null;for(let c of s)c.startsWith("p")?(a=Number(c.slice(1)),Number.isFinite(a)&&a>0?r==null&&(r=a):a=null):c.startsWith("c")&&a!=null&&c.slice(1).trim()==="claude"&&o==null&&(o=a);return o??r}catch{return null}}async function jc(e){try{let{stdout:t}=await ns("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),n=Number(t.trim());return Number.isFinite(n)&&n>0?n:null}catch{return null}}async function Mh(e){try{let{stdout:t}=await ns("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),n=Date.parse(t.trim());return Number.isFinite(n)?n:null}catch{return null}}var Dh=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu","node","deno","bun","python","python3","ruby","ts-node","tsx","go","cargo","java","php","irb","pry","iex","terminal","shell","console"]);function de(e){let t=e.trim().toLowerCase();if(!t)return!0;let n=t.replace(/^[-/]+/,"").replace(/^.*\//,"").replace(/\s*\(\d+\)\s*$/,"").trim();return Dh.has(n)}function St(e){let t=e.tabName?.trim();return t&&!de(t)&&!le(t)?t:null}function Fh(e){try{return f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function ss(e){let t=Nh(e,".jsonl");if(Te(t)||vh(t))return;let n=Fh(t),s=await jh(e),r=s?await jc(s):null,o=s?await vc(s):null;o&&M.setOrigin(t,o);let a=null,c=null,u=null,d=M.takePendingMatched({shellPid:r,cwd:n?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${n?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),Ic(t);return}if(d.kind==="pid-match"||d.kind==="singleton-cwd"){let T=d.entry;c=T.shell_pid,u=d.kind==="pid-match"?"pending-pid":"pending-cwd",a=M.get(T.shell_pid)??{shell_pid:T.shell_pid,tab_name:T.tab_name,cwd:T.cwd,opened_at:T.started_at,last_seen_at:T.started_at}}let m=null;if(!a&&s){let T=s;for(let R=0;R<4&&T!=null;R++){let O=await jc(T);if(!O)break;let L=M.get(O);if(L){a=L,c=O,u="ppid";break}m==null&&(m=O),T=O}}if(!a&&n?.cwd){let T=n.cwd.replace(/\/+$/,""),R=M.all().filter(O=>O.cwd&&O.cwd.replace(/\/+$/,"")===T);R.length===1?(a=R[0],c=a.shell_pid,u="cwd"):R.length>=2&&(console.log(`[correlator] ${R.length} registered terminals in ${T} for ${t.slice(0,8)} \u2014 refusing to guess; heuristic title will display`),Ic(t))}let h=null;if(a?.tab_name&&!de(a.tab_name)&&!le(a.tab_name))h=a.tab_name;else if(a?.tab_name&&le(a.tab_name)){let T=bt(a.tab_name);T&&!de(T)&&(h=T)}if(!h&&!(u==="pending-pid"||u==="pending-cwd")&&a&&n?.cwd){let T=n.cwd.replace(/\/+$/,""),R=M.all().filter(O=>O.shell_pid!==c&&O.cwd&&O.cwd.replace(/\/+$/,"")===T&&!de(O.tab_name)&&!le(O.tab_name)).sort((O,L)=>Date.parse(L.last_seen_at)-Date.parse(O.last_seen_at))[0];R&&(h=R.tab_name)}let S=St({tabName:h,origin:o,cwd:n?.cwd??null,gitBranch:n?.git_branch??null});if(m!=null&&!c&&M.deferSessionLink(t,m,n?.cwd??null,n?.git_branch??null),!!S)try{he(t,S),Ih(t);let T=!!h&&!de(h)&&!le(h)&&S===h.trim();c!=null&&M.linkSession(t,c);let R=u==="pending-pid"?"pending PID-exact match":u==="pending-cwd"?"pending cwd-singleton match":u??"unknown";console.log(`[correlator] auto-aliased ${t.slice(0,8)} \u2192 "${S}"`+(T?` (tab name via ${R}, shell pid ${c})`:a?` (generic shell name "${a.tab_name}" \u2192 ${o?.editor??"origin"} fallback, shell pid ${c})`:o?` (${o.editor} origin \u2014 no terminal match)`:""))}catch{}}async function Ph(){try{let{stdout:e}=await ns("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let n of e.split(`
969
- `)){let s=n.trim();if(!s)continue;let r=s.match(/^(\d+)\s+(\d+)\s+(.+)$/);if(!r)continue;let o=Number(r[1]),a=Number(r[2]),c=r[3].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&(!Number.isFinite(o)||!Number.isFinite(a)||t.set(o,a))}return t}catch{return new Map}}var $h=9e4;function Uh(e){let t=(Date.now()-$h)/1e3,n=e.replace(/\/+$/,"");return f().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
1033
+ previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(a)),wl(),{session_id:e,alias:n,updated_at:r,previous_aliases:a}}function ks(e){let t=h(),n=new Date().toISOString(),s=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!s)return;let r=Po(s.previous_aliases);r.push({alias:s.alias,replaced_at:n}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
1034
+ WHERE session_id = ?`).run(n,JSON.stringify(r),e),wl()}function wl(){try{V();let e=eb(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};VE(ZE,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}q();import{execFile as tb}from"node:child_process";import{readFile as nb}from"node:fs/promises";import{promisify as sb}from"node:util";var rb=sb(tb),Rl=["CURSOR_TRACE_ID","VSCODE_PID","VSCODE_INJECTION","TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","WT_SESSION","KITTY_WINDOW_ID","ALACRITTY_SOCKET","WARP_HONOR_PS1"];function ob(e){let t={};for(let n of Rl){let s=e[n];typeof s=="string"&&s.length>0&&(t[n]=s)}return t}function ib(e){let t=Date.now();if(e.CURSOR_TRACE_ID)return{editor:"cursor",label:"Cursor",detectedAt:t};if(e.VSCODE_PID||e.VSCODE_INJECTION)return{editor:"vscode",label:"VS Code",detectedAt:t};let n=e.TERM_PROGRAM;return n==="WarpTerminal"?{editor:"warp",label:"Warp",detectedAt:t}:n==="iTerm.app"?{editor:"iterm",label:"iTerm",detectedAt:t}:n==="Apple_Terminal"?{editor:"apple-terminal",label:"Terminal",detectedAt:t}:n==="WezTerm"?{editor:"wezterm",label:"WezTerm",detectedAt:t}:e.WT_SESSION?{editor:"windows-terminal",label:"Windows Terminal",detectedAt:t}:e.KITTY_WINDOW_ID?{editor:"kitty",label:"Kitty",detectedAt:t}:e.TERM==="alacritty"||e.ALACRITTY_SOCKET?{editor:"alacritty",label:"Alacritty",detectedAt:t}:null}async function ab(e){if(process.platform==="linux")try{let t=await nb(`/proc/${e}/environ`,"utf8");return cb(t)}catch{return{}}try{let{stdout:t}=await rb("/bin/ps",["eww","-o","command=","-p",String(e)],{timeout:2e3,maxBuffer:1048576});return lb(t)}catch{return{}}}function cb(e){let t={};for(let n of e.split("\0")){if(!n)continue;let s=n.indexOf("=");if(s<=0)continue;let r=n.slice(0,s),o=n.slice(s+1);t[r]=o}return t}function lb(e){let t={},n=new Set(Rl),s=e.replace(/\s+/g," ").trim().split(" ");for(let r of s){let o=r.indexOf("=");if(o<=0)continue;let a=r.slice(0,o);n.has(a)&&(t[a]=r.slice(o+1))}return t}async function kl(e){if(!Number.isFinite(e)||e<=0)return null;try{let t=await ab(e),n=ob(t);return ib(n)}catch{return null}}var xs=db(ub),As;function gb(){if(As!==void 0)return As;let e=["/usr/sbin/lsof","/usr/bin/lsof","/opt/homebrew/bin/lsof"];for(let t of e)if(pb(t))return As=t,t;return As=null,null}var fb=3,_b=3600*1e3,En=new Map;function Ol(){let e=j.all(),t=e.map(n=>n.shell_pid).sort((n,s)=>n-s).join(",");return`${e.length}:${t}`}function hb(e){let t=En.get(e);return t?Date.now()-t.lastAt>_b?(En.delete(e),!1):t.refusals<fb?!1:t.fingerprint===Ol():!1}function Al(e){let t=Ol(),n=En.get(e);n&&n.fingerprint===t?(n.refusals+=1,n.lastAt=Date.now()):En.set(e,{refusals:1,fingerprint:t,lastAt:Date.now()})}function Eb(e){En.delete(e)}async function bb(e){let t=gb();if(!t)return null;try{let{stdout:n}=await xs(t,["-Fpc",e],{timeout:2e3}),s=n.split(`
1035
+ `),r=null,o=null,a=null;for(let c of s)c.startsWith("p")?(a=Number(c.slice(1)),Number.isFinite(a)&&a>0?r==null&&(r=a):a=null):c.startsWith("c")&&a!=null&&c.slice(1).trim()==="claude"&&o==null&&(o=a);return o??r}catch{return null}}async function xl(e){try{let{stdout:t}=await xs("/bin/ps",["-o","ppid=","-p",String(e)],{timeout:2e3}),n=Number(t.trim());return Number.isFinite(n)&&n>0?n:null}catch{return null}}async function Sb(e){try{let{stdout:t}=await xs("/bin/ps",["-o","lstart=","-p",String(e)],{timeout:2e3}),n=Date.parse(t.trim());return Number.isFinite(n)?n:null}catch{return null}}var Nl=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu","node","deno","bun","python","python3","ruby","ts-node","tsx","go","cargo","java","php","irb","pry","iex","terminal","shell","console"]);function de(e){let t=e.trim().toLowerCase();if(!t)return!0;let n=t.replace(/^[-/]+/,"").replace(/\s*\(\d+\)\s*$/,"").trim();if(Nl.has(n))return!0;if(/^[-/][\w./-]*$/.test(t)){let s=n.replace(/^.*\//,"");if(Nl.has(s))return!0}return!1}function Lt(e){let t=e.tabName?.trim();return t&&!de(t)&&!le(t)?t:null}function yb(e){try{return h().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(e)??null}catch{return null}}async function Ns(e){let t=mb(e,".jsonl");if(ye(t)||hb(t))return;let n=yb(t),s=await bb(e),r=s?await xl(s):null,o=s?await kl(s):null;o&&j.setOrigin(t,o);let a=null,c=null,u=null,d=j.takePendingMatched({shellPid:r,cwd:n?.cwd??null,withinMs:3e4});if(d.kind==="ambiguous"){console.log(`[correlator] ambiguous pending for ${t.slice(0,8)} (${d.candidates.length} candidates in ${n?.cwd??"?"}) \u2014 refusing to guess; heuristic title will display`),Al(t);return}if(d.kind==="pid-match"||d.kind==="singleton-cwd"){let b=d.entry;c=b.shell_pid,u=d.kind==="pid-match"?"pending-pid":"pending-cwd",a=j.get(b.shell_pid)??{shell_pid:b.shell_pid,tab_name:b.tab_name,cwd:b.cwd,opened_at:b.started_at,last_seen_at:b.started_at}}let p=null;if(!a&&s){let b=s;for(let R=0;R<4&&b!=null;R++){let k=await xl(b);if(!k)break;let M=j.get(k);if(M){a=M,c=k,u="ppid";break}p==null&&(p=k),b=k}}if(!a&&n?.cwd){let b=n.cwd.replace(/\/+$/,""),R=j.all().filter(k=>k.cwd&&k.cwd.replace(/\/+$/,"")===b);R.length===1?(a=R[0],c=a.shell_pid,u="cwd"):R.length>=2&&(console.log(`[correlator] ${R.length} registered terminals in ${b} for ${t.slice(0,8)} \u2014 refusing to guess; heuristic title will display`),Al(t))}let f=null;if(a?.tab_name&&!de(a.tab_name)&&!le(a.tab_name))f=a.tab_name;else if(a?.tab_name&&le(a.tab_name)){let b=Ct(a.tab_name);b&&!de(b)&&(f=b)}if(!f&&!(u==="pending-pid"||u==="pending-cwd")&&a&&n?.cwd){let b=n.cwd.replace(/\/+$/,""),R=j.all().filter(k=>k.shell_pid!==c&&k.cwd&&k.cwd.replace(/\/+$/,"")===b&&!de(k.tab_name)&&!le(k.tab_name)).sort((k,M)=>Date.parse(M.last_seen_at)-Date.parse(k.last_seen_at))[0];R&&(f=R.tab_name)}let y=Lt({tabName:f,origin:o,cwd:n?.cwd??null,gitBranch:n?.git_branch??null});if(p!=null&&!c&&j.deferSessionLink(t,p,n?.cwd??null,n?.git_branch??null),!!y)try{he(t,y),Eb(t);let b=!!f&&!de(f)&&!le(f)&&y===f.trim();c!=null&&j.linkSession(t,c);let R=u==="pending-pid"?"pending PID-exact match":u==="pending-cwd"?"pending cwd-singleton match":u??"unknown";console.log(`[correlator] auto-aliased ${t.slice(0,8)} \u2192 "${y}"`+(b?` (tab name via ${R}, shell pid ${c})`:a?` (generic shell name "${a.tab_name}" \u2192 ${o?.editor??"origin"} fallback, shell pid ${c})`:o?` (${o.editor} origin \u2014 no terminal match)`:""))}catch{}}async function Tb(){try{let{stdout:e}=await xs("/bin/ps",["-eo","pid=,ppid=,comm="],{timeout:2e3}),t=new Map;for(let n of e.split(`
1036
+ `)){let s=n.trim();if(!s)continue;let r=s.match(/^(\d+)\s+(\d+)\s+(.+)$/);if(!r)continue;let o=Number(r[1]),a=Number(r[2]),c=r[3].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&(!Number.isFinite(o)||!Number.isFinite(a)||t.set(o,a))}return t}catch{return new Map}}var wb=9e4;function Rb(e){let t=(Date.now()-wb)/1e3,n=e.replace(/\/+$/,"");return h().prepare(`SELECT s.id, NULLIF(sa.alias, '') AS alias, s.started_at AS started_at
970
1037
  FROM sessions s
971
1038
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
972
- WHERE s.cwd = ? AND s.file_mtime > ?`).all(n,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function Bh(e){let t=await import("node:fs/promises"),n="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);n=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let s=[];for(let r of n.split(`
1039
+ WHERE s.cwd = ? AND s.file_mtime > ?`).all(n,t).map(r=>({id:r.id,alias:r.alias,started_at_ms:r.started_at?Date.parse(r.started_at):null}))}async function kb(e){let t=await import("node:fs/promises"),n="";try{let r=await t.open(e,"r");try{let o=Buffer.alloc(32768),{bytesRead:a}=await r.read(o,0,o.length,0);n=o.toString("utf8",0,a)}finally{await r.close()}}catch{return[]}let s=[];for(let r of n.split(`
973
1040
  `)){if(!r.trim())continue;let o;try{o=JSON.parse(r)}catch{continue}let a=o;if(!a||a.type!=="user"&&a.type!=="assistant")continue;let c=a.message?.content,u="";if(typeof c=="string")u=c;else if(Array.isArray(c))for(let d of c)d&&typeof d=="object"&&"text"in d&&typeof d.text=="string"&&(u+=d.text+`
974
- `);if(u){for(let d of u.split(/\r?\n/)){let m=d.trim();m.length>=60&&m.length<=400&&s.push(m)}if(s.length>=8)break}}return s}async function Dc(e){if(Te(e))return null;let t=f().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let n=await Bh(t.file_path);if(n.length===0)return null;let s=t.cwd.replace(/\/+$/,""),r=M.allOutputTails(),o=[];for(let[a,c]of r){let u=M.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==s||de(u.tab_name)||le(u.tab_name))continue;let d=0;for(let m of n)c.text.includes(m)&&d++;d>0&&o.push({shell_pid:a,tab_name:u.tab_name,matched_fingerprints:d})}return o.length===0||(o.sort((a,c)=>c.matched_fingerprints-a.matched_fingerprints),o.length>1&&o[0].matched_fingerprints===o[1].matched_fingerprints)?null:o[0]}var Hh=3e4;function Fc(){let e=Date.now(),t=M.all(),n=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>Hh},s=new Map;for(let o of t){if(n(o)||!o.cwd||de(o.tab_name)||le(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=s.get(a);c||(c=[],s.set(a,c)),c.push({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd})}let r={rebound:0,ghosts:0,ambiguous:0};for(let o of t){if(!n(o))continue;let a=M.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=Te(c);if(!u)continue;let d=f().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let m=`${d.cwd.replace(/\/+$/,"")}::${u}`,h=s.get(m)??[];if(h.length===0)continue;if(h.length>1){r.ambiguous++;continue}let b=h[0];b.shell_pid!==o.shell_pid&&(M.unlinkSession(c),M.linkSession(c,b.shell_pid),r.rebound++)}}}return r}function Pc(){let e={resolved:0,expired:0},t=M.allDeferredLinks();for(let n of t){let s=M.get(n.parent_shell_pid);if(!s||de(s.tab_name)||le(s.tab_name))continue;let r=Te(n.session_id);if(r&&!M.isSessionAutoLinked(n.session_id)){M.resolveDeferredLink(n.session_id);continue}let o=M.getOrigin(n.session_id),a=St({tabName:s.tab_name,origin:o??null,cwd:n.cwd,gitBranch:n.git_branch});if(!a){M.resolveDeferredLink(n.session_id);continue}r!==a&&he(n.session_id,a),M.linkSession(n.session_id,n.parent_shell_pid),M.resolveDeferredLink(n.session_id),e.resolved++}return e}var Wh=6e4;async function rs(){let e=await Ph(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let n=[];for(let[a,c]of e){let u=M.get(c);if(!u||!u.cwd||de(u.tab_name)||le(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let m=await Mh(a);n.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:m})}let s=new Map,r=a=>{let c=s.get(a);if(c)return c;let u=Uh(a);return s.set(a,u),u},o=new Set;for(let a of n){let c=r(a.cwd).filter(d=>!o.has(d.id));if(c.length===0)continue;let u=null;if(a.startTimeMs!=null){let d=Wh;for(let m of c){if(m.started_at_ms==null)continue;let h=Math.abs(m.started_at_ms-a.startTimeMs);h<d&&(d=h,u=m)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!M.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){M.linkSession(u.id,a.shellPid);continue}try{he(u.id,a.target),M.linkSession(u.id,a.shellPid),u.alias?t.renamed++:t.linked++,console.log(`[correlator] linked ${u.id.slice(0,8)} \u2192 "${a.target}" (live sweep, claude pid ${a.claudePid}, shell pid ${a.shellPid})`)}catch{}}return t}function $c(e,t,n){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let s=e.prepare(`
1041
+ `);if(u){for(let d of u.split(/\r?\n/)){let p=d.trim();p.length>=60&&p.length<=400&&s.push(p)}if(s.length>=8)break}}return s}async function Cl(e){if(ye(e))return null;let t=h().prepare("SELECT cwd, file_path FROM sessions WHERE id = ?").get(e);if(!t?.cwd||!t.file_path)return null;let n=await kb(t.file_path);if(n.length===0)return null;let s=t.cwd.replace(/\/+$/,""),r=j.allOutputTails(),o=[];for(let[a,c]of r){let u=j.get(a);if(!u||!u.cwd||u.cwd.replace(/\/+$/,"")!==s||de(u.tab_name)||le(u.tab_name))continue;let d=0;for(let p of n)c.text.includes(p)&&d++;d>0&&o.push({shell_pid:a,tab_name:u.tab_name,matched_fingerprints:d})}return o.length===0||(o.sort((a,c)=>c.matched_fingerprints-a.matched_fingerprints),o.length>1&&o[0].matched_fingerprints===o[1].matched_fingerprints)?null:o[0]}var Ab=3e4;function Ll(){let e=Date.now(),t=j.all(),n=o=>{let a=Date.parse(o.last_seen_at);return!Number.isFinite(a)||e-a>Ab},s=new Map;for(let o of t){if(n(o)||!o.cwd||de(o.tab_name)||le(o.tab_name))continue;let a=`${o.cwd.replace(/\/+$/,"")}::${o.tab_name}`,c=s.get(a);c||(c=[],s.set(a,c)),c.push({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd})}let r={rebound:0,ghosts:0,ambiguous:0};for(let o of t){if(!n(o))continue;let a=j.sessionsFor(o.shell_pid);if(a.length!==0){r.ghosts++;for(let c of a){let u=ye(c);if(!u)continue;let d=h().prepare("SELECT cwd FROM sessions WHERE id = ?").get(c);if(!d?.cwd)continue;let p=`${d.cwd.replace(/\/+$/,"")}::${u}`,f=s.get(p)??[];if(f.length===0)continue;if(f.length>1){r.ambiguous++;continue}let S=f[0];S.shell_pid!==o.shell_pid&&(j.unlinkSession(c),j.linkSession(c,S.shell_pid),r.rebound++)}}}return r}function vl(){let e={resolved:0,expired:0},t=j.allDeferredLinks();for(let n of t){let s=j.get(n.parent_shell_pid);if(!s||de(s.tab_name)||le(s.tab_name))continue;let r=ye(n.session_id);if(r&&!j.isSessionAutoLinked(n.session_id)){j.resolveDeferredLink(n.session_id);continue}let o=j.getOrigin(n.session_id),a=Lt({tabName:s.tab_name,origin:o??null,cwd:n.cwd,gitBranch:n.git_branch});if(!a){j.resolveDeferredLink(n.session_id);continue}r!==a&&he(n.session_id,a),j.linkSession(n.session_id,n.parent_shell_pid),j.resolveDeferredLink(n.session_id),e.resolved++}return e}var xb=6e4;async function Os(){let e=await Tb(),t={scanned:e.size,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0};if(e.size===0)return t;let n=[];for(let[a,c]of e){let u=j.get(c);if(!u||!u.cwd||de(u.tab_name)||le(u.tab_name))continue;let d=u.tab_name.trim();if(!d)continue;let p=await Sb(a);n.push({claudePid:a,shellPid:c,cwd:u.cwd.replace(/\/+$/,""),target:d,startTimeMs:p})}let s=new Map,r=a=>{let c=s.get(a);if(c)return c;let u=Rb(a);return s.set(a,u),u},o=new Set;for(let a of n){let c=r(a.cwd).filter(d=>!o.has(d.id));if(c.length===0)continue;let u=null;if(a.startTimeMs!=null){let d=xb;for(let p of c){if(p.started_at_ms==null)continue;let f=Math.abs(p.started_at_ms-a.startTimeMs);f<d&&(d=f,u=p)}}if(!u&&c.length===1&&(u=c[0]),!u){t.ambiguous_cwd++;continue}if(o.add(u.id),u.alias&&!j.isSessionAutoLinked(u.id)){t.skipped_manual++;continue}if(u.alias===a.target){j.linkSession(u.id,a.shellPid);continue}try{he(u.id,a.target),j.linkSession(u.id,a.shellPid),u.alias?t.renamed++:t.linked++,console.log(`[correlator] linked ${u.id.slice(0,8)} \u2192 "${a.target}" (live sweep, claude pid ${a.claudePid}, shell pid ${a.shellPid})`)}catch{}}return t}function Il(e,t,n){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let s=e.prepare(`
975
1042
  INSERT INTO message_usage (
976
1043
  message_uuid, session_id, model,
977
1044
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -988,7 +1055,7 @@ ${o}
988
1055
  cache_create_tokens = excluded.cache_create_tokens,
989
1056
  cache_read_tokens = excluded.cache_read_tokens,
990
1057
  timestamp = excluded.timestamp
991
- `);for(let r of n)r.usage&&r.role==="assistant"&&s.run({uuid:r.uuid,session_id:t,model:r.model,input:r.usage.inputTokens,output:r.usage.outputTokens,cc:r.usage.cacheCreateTokens,cr:r.usage.cacheReadTokens,ts:r.timestamp})}function os(e,t){let n=e.prepare(`SELECT
1058
+ `);for(let r of n)r.usage&&r.role==="assistant"&&s.run({uuid:r.uuid,session_id:t,model:r.model,input:r.usage.inputTokens,output:r.usage.outputTokens,cc:r.usage.cacheCreateTokens,cr:r.usage.cacheReadTokens,ts:r.timestamp})}function Cs(e,t){let n=e.prepare(`SELECT
992
1059
  COALESCE(SUM(input_tokens), 0) AS input_tokens,
993
1060
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
994
1061
  COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
@@ -1003,11 +1070,11 @@ ${o}
1003
1070
  total_cache_create_tokens = @cc,
1004
1071
  total_cache_read_tokens = @cr,
1005
1072
  primary_model = @model
1006
- WHERE id = @id`).run({id:t,input:n.input_tokens,output:n.output_tokens,cc:n.cache_create_tokens,cr:n.cache_read_tokens,model:s?.model??null})}B();import{execFile as qh}from"node:child_process";import{promisify as Xh}from"node:util";import{stat as Jh}from"node:fs/promises";var Uc=Xh(qh),Bc=1e4,Gh="%H%x09%aI%x09%s";async function Yh(e){try{let{stdout:t}=await Uc("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:Bc});return t.trim()==="true"}catch{return!1}}async function zh(e,t,n){let s=["--no-pager","log","--all","--no-color","--since",t,"--until",n,`--pretty=format:${Gh}`],{stdout:r}=await Uc("git",s,{cwd:e,timeout:Bc,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1007
- `)){if(!c)continue;let[u,d,...m]=c.split(" ");!u||a.has(u)||(a.add(u),o.push({commit_sha:u,committed_at:d??null,subject:m.join(" ")||null}))}return o}function Kh(e){return f().prepare(`SELECT id, cwd, started_at, ended_at
1008
- FROM sessions WHERE id = ?`).get(e)??null}function Vh(e,t,n){if(n.length===0)return 0;let s=f(),r=new Date().toISOString(),o=s.prepare(`INSERT OR IGNORE INTO session_commits
1073
+ WHERE id = @id`).run({id:t,input:n.input_tokens,output:n.output_tokens,cc:n.cache_create_tokens,cr:n.cache_read_tokens,model:s?.model??null})}q();import{execFile as Nb}from"node:child_process";import{promisify as Ob}from"node:util";import{stat as Cb}from"node:fs/promises";var Ml=Ob(Nb),jl=1e4,Lb="%H%x09%aI%x09%s";async function vb(e){try{let{stdout:t}=await Ml("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:jl});return t.trim()==="true"}catch{return!1}}async function Ib(e,t,n){let s=["--no-pager","log","--all","--no-color","--since",t,"--until",n,`--pretty=format:${Lb}`],{stdout:r}=await Ml("git",s,{cwd:e,timeout:jl,maxBuffer:8*1024*1024}),o=[],a=new Set;for(let c of r.split(`
1074
+ `)){if(!c)continue;let[u,d,...p]=c.split(" ");!u||a.has(u)||(a.add(u),o.push({commit_sha:u,committed_at:d??null,subject:p.join(" ")||null}))}return o}function Mb(e){return h().prepare(`SELECT id, cwd, started_at, ended_at
1075
+ FROM sessions WHERE id = ?`).get(e)??null}function jb(e,t,n){if(n.length===0)return 0;let s=h(),r=new Date().toISOString(),o=s.prepare(`INSERT OR IGNORE INTO session_commits
1009
1076
  (session_id, commit_sha, committed_at, subject, cwd_snapshot, correlated_at)
1010
- VALUES (?, ?, ?, ?, ?, ?)`),a=0;return s.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(n),a}async function eo(e){let t=Kh(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let n=t.started_at,s=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await Jh(t.cwd)).isDirectory())return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}catch{return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}if(!await Yh(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await zh(t.cwd,n,s),a=Vh(e,t.cwd,o);return{sessionId:e,status:"ok",commitsFound:o.length,commitsInserted:a}}catch(o){return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:o.message}}}function is(e){let t=f(),n=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(n))return[];let s=`${n.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1077
+ VALUES (?, ?, ?, ?, ?, ?)`),a=0;return s.transaction(u=>{for(let d of u)o.run(e,d.commit_sha,d.committed_at,d.subject,t,r).changes>0&&(a+=1)})(n),a}async function Fo(e){let t=Mb(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let n=t.started_at,s=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await Cb(t.cwd)).isDirectory())return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}catch{return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}if(!await vb(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await Ib(t.cwd,n,s),a=jb(e,t.cwd,o);return{sessionId:e,status:"ok",commitsFound:o.length,commitsInserted:a}}catch(o){return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:o.message}}}function Ls(e){let t=h(),n=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(n))return[];let s=`${n.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1011
1078
  NULLIF(sa.alias, '') AS alias,
1012
1079
  p.name AS project,
1013
1080
  s.started_at AS startedAt,
@@ -1021,18 +1088,18 @@ ${o}
1021
1088
  LEFT JOIN session_aliases sa ON sa.session_id = sc.session_id
1022
1089
  WHERE lower(sc.commit_sha) = lower(?)
1023
1090
  OR lower(sc.commit_sha) LIKE ?
1024
- ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(n,s)}function to(e){return f().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1091
+ ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(n,s)}function $o(e){return h().prepare(`SELECT commit_sha, committed_at, subject, correlated_at
1025
1092
  FROM session_commits
1026
1093
  WHERE session_id = ?
1027
- ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var Zh=3e4;function Hc(e){try{let n=f().prepare(`SELECT MAX(correlated_at) AS last_at
1028
- FROM session_commits WHERE session_id = ?`).get(e),s=n?.last_at?Date.parse(n.last_at):0;if(s&&Date.now()-s<Zh)return}catch{}eo(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}B();ee();import{writeFileSync as oE,mkdirSync as iE,existsSync as aE}from"node:fs";import{join as Zc}from"node:path";B();var Wc=80;function qc(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(`
1029
- `)[0].trim();return n.length>Wc?n.slice(0,Wc)+"\u2026":n}function Qh(e){return f().prepare(`SELECT s.id AS id,
1094
+ ORDER BY COALESCE(committed_at, correlated_at) ASC`).all(e)}var Db=3e4;function Dl(e){try{let n=h().prepare(`SELECT MAX(correlated_at) AS last_at
1095
+ FROM session_commits WHERE session_id = ?`).get(e),s=n?.last_at?Date.parse(n.last_at):0;if(s&&Date.now()-s<Db)return}catch{}Fo(e).catch(t=>{console.error(`[git-correlator] ${e.slice(0,8)} failed:`,t)})}q();ne();import{writeFileSync as Wb,mkdirSync as qb,existsSync as Xb}from"node:fs";import{join as Jl}from"node:path";q();var Pl=80;function Fl(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let n=t.split(`
1096
+ `)[0].trim();return n.length>Pl?n.slice(0,Pl)+"\u2026":n}function Pb(e){return h().prepare(`SELECT s.id AS id,
1030
1097
  sa.alias AS alias,
1031
1098
  s.auto_title AS auto_title,
1032
1099
  s.first_user_message AS first_user_message
1033
1100
  FROM sessions s
1034
1101
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1035
- WHERE s.id = ?`).get(e)??null}function eE(e){let t=Qh(e);return t?qc(t):e.slice(0,8)}function Xc(e){if(!e)return null;let t=f(),n=t.prepare(`SELECT e.thread_id AS thread_id,
1102
+ WHERE s.id = ?`).get(e)??null}function Fb(e){let t=Pb(e);return t?Fl(t):e.slice(0,8)}function $l(e){if(!e)return null;let t=h(),n=t.prepare(`SELECT e.thread_id AS thread_id,
1036
1103
  t.name AS thread_name,
1037
1104
  e.parent_session_id AS parent_session_id,
1038
1105
  e.added_at AS added_at
@@ -1041,7 +1108,7 @@ ${o}
1041
1108
  WHERE e.session_id = ?
1042
1109
  AND t.archived = 0
1043
1110
  ORDER BY e.added_at DESC
1044
- LIMIT 1`).get(e);if(!n)return null;let s=n.parent_session_id?{id:n.parent_session_id,title:eE(n.parent_session_id)}:null,r=n.parent_session_id?[e,n.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1111
+ LIMIT 1`).get(e);if(!n)return null;let s=n.parent_session_id?{id:n.parent_session_id,title:Fb(n.parent_session_id)}:null,r=n.parent_session_id?[e,n.parent_session_id]:[e],o=r.map(()=>"?").join(", "),c=t.prepare(`SELECT e.session_id AS session_id,
1045
1112
  s.id AS id,
1046
1113
  sa.alias AS alias,
1047
1114
  s.auto_title AS auto_title,
@@ -1051,33 +1118,33 @@ ${o}
1051
1118
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1052
1119
  WHERE e.thread_id = ?
1053
1120
  AND e.session_id NOT IN (${o})
1054
- ORDER BY e.added_at ASC`).all(n.thread_id,...r).map(u=>({id:u.session_id,title:u.id?qc(u):u.session_id.slice(0,8)}));return{thread_id:n.thread_id,thread_name:n.thread_name,parent_session:s,siblings:c}}var Jc=[/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Base directory for this skill:/i,/^You are Claude Code in a fresh terminal/i,/^You are extracting a structured Output Index from a Claude/i];function no(e){return Jc.some(t=>t.test(e))}var tE=[/^You are summarizing a Claude Code session/i,/^You are extracting a structured Output Index from a Claude/i,/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Thread context:\n- This session is part of thread/i];function Gc(e){return e?tE.some(t=>t.test(e)):!1}var nE=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i,/^You are summarizing a Claude Code session/i],sE=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],rE=20;function tn(e){if(e.auto_title_source==="agent"&&e.auto_title)return"agent";if(e.has_alias)return"manual_alias";if(e.auto_title&&e.auto_title.includes(" \xB7 "))return"fixed_v0.16.1";if(!e.auto_title||e.auto_title.length<rE)return"low_signal";for(let t of Jc)if(t.test(e.auto_title))return"recursive_meta";for(let t of nE)if(t.test(e.auto_title))return"programmatic";for(let t of sE)if(t.test(e.auto_title))return"template_pending";return"clean"}function as(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function Yc(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let n=e.slice(0,t),s=e.slice(t+3),r=as(s);return!r||r===s?e:`${n} \xB7 ${r}`}var oo=Zc(W,"titles"),cE=80,lE=60,uE=100,dE=50,nn=5,cs=15,pE=500;function Qc(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 yt(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let n=mE(t,e);if(n)return n;let s=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(s&&s.length<=cE?s:t.slice(0,lE)).trim()||null}function mE(e,t){let n=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(n){let s=`/${n[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=io(r);return o?Tt(`${s} \xB7 ${o}`):s}for(let s of gE){if(!e.match(s.match))continue;let o=s.prefix,a=s.extract?s.extract(e,t):io(t);return a?s.completeFromExtract?Tt(a):Tt(`${o} \xB7 ${a}`):o}for(let s of hE){if(!e.match(s.match))continue;let o=s.extract?s.extract(e,t):ls(t);return o?s.completeFromExtract?Tt(o):Tt(`${s.prefix} \xB7 ${o}`):s.prefix}return null}var gE=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let s=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=io(t);return s&&r?`${s} \xB7 ${r}`:s||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let n=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return n?n.split("/").filter(Boolean).slice(-2).join("/")||n:null}},{match:/^(?:read|inspect) this and then begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>fE(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let n=t.match(/\.claude\/skills\/([^/\s]+)/);return n?.[1]?`[skill] ${n[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>_E(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>zc(t,"[meta] auto-title (full-arc)")},{match:/^You will receive the first \d+ user messages from a Claude Code session/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>zc(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let n=t[1],s=t[2]?.trim();return s?`Implementing ${n} \xB7 ${s}`:`Implementing ${n}`}}];function _E(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),n=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),s=t?.[1]?.trim(),r=n?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return s&&o?`[output-index] \xB7 ${s} \xB7 ${o}`:s?`[output-index] \xB7 ${s}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function zc(e,t){if(!e)return t;let n=e.indexOf("Messages:");if(n===-1)return t;let s=e.slice(n+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of s.matchAll(r)){let a=o[1]?.trim()??"";if(!a)continue;let c=a.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!c||/^<local-command-/.test(c))continue;let u=c.length>60?c.slice(0,57)+"\u2026":c;return`${t} \xB7 ${u}`}return t}function fE(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let n=t[1].split("/").filter(Boolean);return(n[n.length-1]??"").replace(/\.[^.]+$/,"")||null}var hE=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>ls(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let s=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=s?`${s} co-pilot`:"co-pilot",o=ls(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>ls(t)}];function ls(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,n;for(;(n=t.exec(e))!==null;){let s=n[2].trim();if(!s||/^(null|none|n\/a|—|-)$/i.test(s))continue;let r=s.replace(/\s+/g," ");return as(r)||r}return null}function Tt(e){return e.slice(0,uE).trim()}var EE=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],bE=[/^(?:brand|brand brief|brand summary)$/i,/^(?:known facts?|facts)$/i,/^(?:client|prospect|customer|account)$/i,/^(?:topic|subject|brief|task)$/i,/^(?:about|for|re)$/i];function so(e){let t=e.trim();return t.length<3?!0:EE.some(n=>n.test(t))}function SE(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return bE.some(n=>n.test(t))}function io(e){let t=TE(e);return t===null?null:as(t)||t}function TE(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,n,s=[];for(;(n=t.exec(e))!==null;){let u=n[2].trim(),d=n[3].trim().replace(/\s+/g," ");if(!so(d)){if(SE(u))return d;s.push(d)}}if(s.length>0)return s[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!so(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(n=o.exec(e))!==null;){let u=n[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!so(d)&&!/product context|reference/i.test(u))return d}let a=e.replace(/^Context for this run[^:]*:\s*/i,"");if(a!==e){let u=a.split(`
1121
+ ORDER BY e.added_at ASC`).all(n.thread_id,...r).map(u=>({id:u.session_id,title:u.id?Fl(u):u.session_id.slice(0,8)}));return{thread_id:n.thread_id,thread_name:n.thread_name,parent_session:s,siblings:c}}var Ul=[/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Base directory for this skill:/i,/^You are Claude Code in a fresh terminal/i,/^You are extracting a structured Output Index from a Claude/i];function Uo(e){return Ul.some(t=>t.test(e))}var $b=[/^You are summarizing a Claude Code session/i,/^You are extracting a structured Output Index from a Claude/i,/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Thread context:\n- This session is part of thread/i];function Hl(e){return e?$b.some(t=>t.test(e)):!1}var Ub=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i,/^You are summarizing a Claude Code session/i],Hb=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],Bb=20;function bn(e){if(e.auto_title_source==="agent"&&e.auto_title)return"agent";if(e.has_alias)return"manual_alias";if(e.auto_title&&e.auto_title.includes(" \xB7 "))return"fixed_v0.16.1";if(!e.auto_title||e.auto_title.length<Bb)return"low_signal";for(let t of Ul)if(t.test(e.auto_title))return"recursive_meta";for(let t of Ub)if(t.test(e.auto_title))return"programmatic";for(let t of Hb)if(t.test(e.auto_title))return"template_pending";return"clean"}function vs(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}function Bl(e){if(!e)return e;let t=e.lastIndexOf(" \xB7 ");if(t===-1)return e;let n=e.slice(0,t),s=e.slice(t+3),r=vs(s);return!r||r===s?e:`${n} \xB7 ${r}`}var Wo=Jl(W,"titles"),Jb=80,Gb=60,Yb=100,Kb=50,Sn=5,Is=15,zb=500;function Gl(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>!!n&&typeof n=="object"&&typeof n.title=="string"&&typeof n.replaced_at=="string")}catch{}return[]}function It(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let n=Vb(t,e);if(n)return n;let s=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(s&&s.length<=Jb?s:t.slice(0,Gb)).trim()||null}function Vb(e,t){let n=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(n){let s=`/${n[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=qo(r);return o?vt(`${s} \xB7 ${o}`):s}for(let s of Qb){if(!e.match(s.match))continue;let o=s.prefix,a=s.extract?s.extract(e,t):qo(t);return a?s.completeFromExtract?vt(a):vt(`${o} \xB7 ${a}`):o}for(let s of tS){if(!e.match(s.match))continue;let o=s.extract?s.extract(e,t):Ms(t);return o?s.completeFromExtract?vt(o):vt(`${s.prefix} \xB7 ${o}`):s.prefix}return null}var Qb=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let s=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=qo(t);return s&&r?`${s} \xB7 ${r}`:s||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let n=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return n?n.split("/").filter(Boolean).slice(-2).join("/")||n:null}},{match:/^(?:read|inspect) this and (?:then )?begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>eS(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let n=t.match(/\.claude\/skills\/([^/\s]+)/);return n?.[1]?`[skill] ${n[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>Zb(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>Wl(t,"[meta] auto-title (full-arc)")},{match:/^You will receive the first \d+ user messages from a Claude Code session/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>Wl(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let n=t[1],s=t[2]?.trim();return s?`Implementing ${n} \xB7 ${s}`:`Implementing ${n}`}}];function Zb(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),n=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),s=t?.[1]?.trim(),r=n?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return s&&o?`[output-index] \xB7 ${s} \xB7 ${o}`:s?`[output-index] \xB7 ${s}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function Wl(e,t){if(!e)return t;let n=e.indexOf("Messages:");if(n===-1)return t;let s=e.slice(n+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of s.matchAll(r)){let a=o[1]?.trim()??"";if(!a)continue;let c=a.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!c||/^<local-command-/.test(c))continue;let u=c.length>60?c.slice(0,57)+"\u2026":c;return`${t} \xB7 ${u}`}return t}function eS(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let n=t[1].split("/").filter(Boolean);return(n[n.length-1]??"").replace(/\.[^.]+$/,"")||null}var tS=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>Ms(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let s=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=s?`${s} co-pilot`:"co-pilot",o=Ms(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>Ms(t)}];function Ms(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,n;for(;(n=t.exec(e))!==null;){let s=n[2].trim();if(!s||/^(null|none|n\/a|—|-)$/i.test(s))continue;let r=s.replace(/\s+/g," ");return vs(r)||r}return null}function vt(e){return e.slice(0,Yb).trim()}var nS=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],sS=[/^(?:brand|brand brief|brand summary)$/i,/^(?:known facts?|facts)$/i,/^(?:client|prospect|customer|account)$/i,/^(?:topic|subject|brief|task)$/i,/^(?:about|for|re)$/i];function Ho(e){let t=e.trim();return t.length<3?!0:nS.some(n=>n.test(t))}function rS(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return sS.some(n=>n.test(t))}function qo(e){let t=oS(e);return t===null?null:vs(t)||t}function oS(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,n,s=[];for(;(n=t.exec(e))!==null;){let u=n[2].trim(),d=n[3].trim().replace(/\s+/g," ");if(!Ho(d)){if(rS(u))return d;s.push(d)}}if(s.length>0)return s[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!Ho(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(n=o.exec(e))!==null;){let u=n[1].trim().replace(/\.(md|txt|json)$/i,""),d=u.replace(/\s*\([^)]*\)\s*$/,"").trim();if(d&&!Ho(d)&&!/product context|reference/i.test(u))return d}let a=e.replace(/^Context for this run[^:]*:\s*/i,"");if(a!==e){let u=a.split(`
1055
1122
  `).map(d=>d.trim()).find(d=>d.length>=4);if(u)return u.slice(0,60)}let c=e.split(`
1056
- `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function ao(e){let t=f(),n=t.prepare(`SELECT rowid AS rid, content_text
1123
+ `).map(u=>u.trim()).find(u=>u.length>=4);return c?c.slice(0,60):null}function Xo(e){let t=h(),n=t.prepare(`SELECT rowid AS rid, content_text
1057
1124
  FROM messages
1058
1125
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1059
1126
  AND content_text IS NOT NULL AND content_text != ''
1060
1127
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1061
- LIMIT ?`).all(e,nn),s=t.prepare(`SELECT rowid AS rid, content_text
1128
+ LIMIT ?`).all(e,Sn),s=t.prepare(`SELECT rowid AS rid, content_text
1062
1129
  FROM messages
1063
1130
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
1064
1131
  AND content_text IS NOT NULL AND content_text != ''
1065
1132
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1066
- LIMIT ?`).all(e,cs),r=new Map;for(let m of n)r.set(m.rid,m.content_text);for(let m of s)r.set(m.rid,m.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let o=Array.from(r.entries()).sort((m,h)=>m[0]-h[0]).map(([,m])=>({content_text:m})),a=n.length===nn&&s.length===cs&&r.size===nn+cs,c=o.map((m,h)=>{let b=(m.content_text??"").slice(0,pE);return a&&h===nn?`--- (middle of session omitted) ---
1067
- ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1068
- `),u=null;try{u=Xc(e)}catch(m){console.error("[autoTitle] thread context resolution failed:",m),u=null}let d=[];return u&&(d.push(yE(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${nn}`,`messages (initial intent) and the last ${cs} messages (current direction).`,"Write a single descriptive title, max 50 characters, focused on what the user is","currently trying to accomplish. If initial intent and current direction differ, prefer","the current direction. Output ONLY the title, with no quotes and no trailing punctuation.","","Messages:",c),d.join(`
1069
- `)}var ro=5;function yE(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,ro).map(a=>`"${a.title}"`).join(", "),o=n>ro?`, and ${n-ro} more`:"";t.push(`- Sibling sessions (${n}): ${r}${o}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
1070
- `)}async function el(e){let t=ao(e),{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(ye(),et));if(!s())throw new Error("claude CLI not found on PATH");let r=await n(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=wE(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,dE)}function wE(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 Kc(s)}}catch{}return Kc(t)}function Kc(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function Ee(e,t,n){let s=t.trim();if(!s)return;let r=f(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1071
- FROM sessions WHERE id = ?`).get(e);if(!o||n==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===s&&o.auto_title_source===n)return;let a=Qc(o.auto_title_history),c=new Date().toISOString();o.auto_title&&o.auto_title_source&&a.push({title:o.auto_title,source:o.auto_title_source,replaced_at:c}),r.prepare(`UPDATE sessions
1133
+ LIMIT ?`).all(e,Is),r=new Map;for(let p of n)r.set(p.rid,p.content_text);for(let p of s)r.set(p.rid,p.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let o=Array.from(r.entries()).sort((p,f)=>p[0]-f[0]).map(([,p])=>({content_text:p})),a=n.length===Sn&&s.length===Is&&r.size===Sn+Is,c=o.map((p,f)=>{let S=(p.content_text??"").slice(0,zb);return a&&f===Sn?`--- (middle of session omitted) ---
1134
+ ${f+1}. ${S}`:`${f+1}. ${S}`}).join(`
1135
+ `),u=null;try{u=$l(e)}catch(p){console.error("[autoTitle] thread context resolution failed:",p),u=null}let d=[];return u&&(d.push(iS(u)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${Sn}`,`messages (initial intent) and the last ${Is} messages (current direction).`,"Write a single descriptive title, max 50 characters, focused on what the user is","currently trying to accomplish. If initial intent and current direction differ, prefer","the current direction. Output ONLY the title, with no quotes and no trailing punctuation.","","Messages:",c),d.join(`
1136
+ `)}var Bo=5;function iS(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let n=e.siblings.length;if(n>0){let r=e.siblings.slice(0,Bo).map(a=>`"${a.title}"`).join(", "),o=n>Bo?`, and ${n-Bo} more`:"";t.push(`- Sibling sessions (${n}): ${r}${o}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
1137
+ `)}async function Yl(e){let t=Xo(e),{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(Te(),ct));if(!s())throw new Error("claude CLI not found on PATH");let r=await n(t,[],{});if(!r.success)throw new Error(`claude CLI exited ${r.exitCode}: ${r.stderr.slice(-500)}`);let o=aS(r.stdout);if(!o)throw new Error("claude CLI returned an empty title");return o.slice(0,Kb)}function aS(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return ql(s)}}catch{}return ql(t)}function ql(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}function Ee(e,t,n){let s=t.trim();if(!s)return;let r=h(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1138
+ FROM sessions WHERE id = ?`).get(e);if(!o||n==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===s&&o.auto_title_source===n)return;let a=Gl(o.auto_title_history),c=new Date().toISOString();o.auto_title&&o.auto_title_source&&a.push({title:o.auto_title,source:o.auto_title_source,replaced_at:c}),r.prepare(`UPDATE sessions
1072
1139
  SET auto_title = ?,
1073
1140
  auto_title_source = ?,
1074
1141
  auto_title_generated_at = ?,
1075
1142
  auto_title_history = ?
1076
- WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(a),e),xE(e,s,n,c)}function Le(e){let t=f().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1077
- 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:Qc(t.auto_title_history)}:null}function tl(){let t=f().prepare(`SELECT id, first_user_message
1143
+ WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(a),e),dS(e,s,n,c)}function ve(e){let t=h().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
1144
+ FROM sessions WHERE id = ?`).get(e);return t?{auto_title:t.auto_title,auto_title_source:t.auto_title_source??null,auto_title_generated_at:t.auto_title_generated_at,auto_title_history:Gl(t.auto_title_history)}:null}function Kl(){let t=h().prepare(`SELECT id, first_user_message
1078
1145
  FROM sessions
1079
1146
  WHERE auto_title IS NULL
1080
- AND first_user_message IS NOT NULL`).all(),n=0;for(let s of t){let r=yt(s.first_user_message);r&&(Ee(s.id,r,"heuristic"),n+=1)}return{updated:n}}function nl(e){let t=f(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1147
+ AND first_user_message IS NOT NULL`).all(),n=0;for(let s of t){let r=It(s.first_user_message);r&&(Ee(s.id,r,"heuristic"),n+=1)}return{updated:n}}function zl(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId,e.projectId]:[],r=t.prepare(`WITH dups AS (
1081
1148
  SELECT auto_title, project_id
1082
1149
  FROM sessions
1083
1150
  WHERE auto_title IS NOT NULL
@@ -1130,7 +1197,7 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1130
1197
  AND content_text IS NOT NULL
1131
1198
  AND content_text != ''
1132
1199
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1133
- LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=null;for(let h of d){let b=h.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!b||/^<local-command-caveat>/.test(b))continue;let S=yt(b);if(S&&!Vc(S)){m=S;break}}if(!m){let h=d.map(S=>S.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(S=>S.length>0&&!/^<local-command-caveat>/.test(S)),b=h?yt(h):null;b&&Vc(b)&&(m=`[vacuous] ${b}`)}m&&(Ee(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function Vc(e){let t=e.trim();return!t||/^\*\*Tool result\*\*$/i.test(t)?!0:[/^all right\.?$/i,/^alright\.?$/i,/^inspect this\.?$/i,/^resolve this\.?$/i,/^do it\.?$/i,/^go\.?$/i,/^continue\.?$/i,/^proceed\.?$/i,/^keep going\.?$/i,/^ok\.?$/i,/^okay\.?$/i,/^yes\.?$/i,/^yep\.?$/i].some(s=>s.test(t))}function sl(e){let t=f(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1200
+ LIMIT 8`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),p=null;for(let f of d){let S=f.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();if(!S||/^<local-command-caveat>/.test(S))continue;let y=It(S);if(y&&!Xl(y)){p=y;break}}if(!p){let f=d.map(y=>y.content_text.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim()).find(y=>y.length>0&&!/^<local-command-caveat>/.test(y)),S=f?It(f):null;S&&Xl(S)&&(p=`[vacuous] ${S}`)}p&&(Ee(u.id,p,"heuristic"),c+=1)}return{scanned:a,updated:c}}function Xl(e){let t=e.trim();return!t||/^\*\*Tool result\*\*$/i.test(t)?!0:[/^all right\.?$/i,/^alright\.?$/i,/^inspect this\.?$/i,/^resolve this\.?$/i,/^do it\.?$/i,/^go\.?$/i,/^continue\.?$/i,/^proceed\.?$/i,/^keep going\.?$/i,/^ok\.?$/i,/^okay\.?$/i,/^yes\.?$/i,/^yep\.?$/i].some(s=>s.test(t))}function Vl(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1134
1201
  FROM sessions s
1135
1202
  WHERE s.auto_title_source = 'heuristic'${n}
1136
1203
  AND (
@@ -1145,47 +1212,47 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1145
1212
  AND content_text IS NOT NULL
1146
1213
  AND content_text != ''
1147
1214
  ORDER BY COALESCE(timestamp, ''), rowid ASC
1148
- LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),m=RE(d,u.auto_title);m&&(Ee(u.id,m,"heuristic"),c+=1)}return{scanned:a,updated:c}}function RE(e,t){for(let n of e){let s=kE(n.content_text);if(!s||no(s))continue;let r=yt(s);if(r){if(r===t)return null;if(!no(r))return r}}return t.startsWith("[meta]")?null:Tt(`[meta] ${t}`)}function rl(e){let t=f(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1215
+ LIMIT 10`),a=0,c=0;for(let u of r){a+=1;let d=o.all(u.id),p=cS(d,u.auto_title);p&&(Ee(u.id,p,"heuristic"),c+=1)}return{scanned:a,updated:c}}function cS(e,t){for(let n of e){let s=lS(n.content_text);if(!s||Uo(s))continue;let r=It(s);if(r){if(r===t)return null;if(!Uo(r))return r}}return t.startsWith("[meta]")?null:vt(`[meta] ${t}`)}function Ql(e){let t=h(),n=e?.projectId?" AND s.project_id = ?":"",s=e?.projectId?[e.projectId]:[],r=t.prepare(`SELECT s.id, s.auto_title
1149
1216
  FROM sessions s
1150
1217
  WHERE s.auto_title_source = 'heuristic'${n}
1151
1218
  AND s.auto_title IS NOT NULL
1152
1219
  AND s.auto_title LIKE '% \xB7 %'
1153
- AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...s),o=0,a=0;for(let c of r){o+=1;let u=Yc(c.auto_title);u!==c.auto_title&&(Ee(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function kE(e){return e.replace(/<command-(?:name|message|args|stdout|stderr)>[\s\S]*?<\/command-(?:name|message|args|stdout|stderr)>/g,"").replace(/<local-command-(?:stdout|stderr|caveat)>[\s\S]*?<\/local-command-(?:stdout|stderr|caveat)>/g,"").trim()}function AE(){z(),aE(oo)||iE(oo,{recursive:!0})}function xE(e,t,n,s){try{AE();let r=Zc(oo,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
1154
- `;oE(r,o+t+`
1155
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}import{existsSync as ol,mkdirSync as NE,readFileSync as OE,writeFileSync as LE}from"node:fs";import{homedir as CE}from"node:os";import{join as il}from"node:path";import{z as co}from"zod";function al(){return process.env.RECALL_HOME??il(CE(),".recall")}function vE(){let e=al();ol(e)||NE(e,{recursive:!0})}function cl(){return il(al(),"config.json")}var ds=co.object({heuristicEnabled:co.boolean().default(!0),agentEnabled:co.boolean().default(!1)}),us={heuristicEnabled:!0,agentEnabled:!1};function ll(){let e=cl();if(!ol(e))return{};try{return JSON.parse(OE(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function st(){let e=ll().autoTitle;if(!e)return{...us};let t=ds.safeParse({...us,...e});return t.success?t.data:{...us}}function ul(e){vE();let t=ll(),n=ds.parse({...us,...t.autoTitle??{},...e}),s={...t,autoTitle:n};return LE(cl(),JSON.stringify(s,null,2)),n}B();ee();import{randomUUID as mo}from"node:crypto";import{existsSync as $E,mkdirSync as UE,writeFileSync as bl}from"node:fs";import{homedir as BE}from"node:os";import{basename as HE,join as go}from"node:path";B();ee();import{randomUUID as IE}from"node:crypto";import{writeFileSync as jE,readFileSync as hx,existsSync as Ex}from"node:fs";import{join as ME}from"node:path";var DE=ME(W,"collections.json"),ps=8;function ms(e){return{...e}}function Re(e,t,n,s=null,r=new Date().toISOString()){f().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1156
- VALUES (?, ?, ?, ?, ?)`).run(e,s,t,n?JSON.stringify(n):null,r)}function gs(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function dl(e){if(!e)return 0;let t=0,n=e,s=new Set,r=f();for(;n;){if(s.has(n))throw new Error("collection cycle detected");s.add(n);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)break;t+=1,n=o.parent_id}return t}function FE(e,t){let n=f(),s=e,r=new Set;for(;s;){if(r.has(s))return!1;if(r.add(s),s===t)return!0;let o=n.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)return!1;s=o.parent_id}return!1}function pl(e=!1){return f().prepare(`SELECT c.*,
1220
+ AND (s.auto_title LIKE '%|%' OR s.auto_title LIKE '%(%')`).all(...s),o=0,a=0;for(let c of r){o+=1;let u=Bl(c.auto_title);u!==c.auto_title&&(Ee(c.id,u,"heuristic"),a+=1)}return{scanned:o,updated:a}}function lS(e){return e.replace(/<command-(?:name|message|args|stdout|stderr)>[\s\S]*?<\/command-(?:name|message|args|stdout|stderr)>/g,"").replace(/<local-command-(?:stdout|stderr|caveat)>[\s\S]*?<\/local-command-(?:stdout|stderr|caveat)>/g,"").trim()}function uS(){V(),Xb(Wo)||qb(Wo,{recursive:!0})}function dS(e,t,n,s){try{uS();let r=Jl(Wo,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
1221
+ `;Wb(r,o+t+`
1222
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}import{existsSync as Zl,mkdirSync as pS,readFileSync as mS,writeFileSync as gS}from"node:fs";import{homedir as fS}from"node:os";import{join as eu}from"node:path";import{z as Jo}from"zod";function tu(){return process.env.RECALL_HOME??eu(fS(),".recall")}function _S(){let e=tu();Zl(e)||pS(e,{recursive:!0})}function nu(){return eu(tu(),"config.json")}var Ds=Jo.object({heuristicEnabled:Jo.boolean().default(!0),agentEnabled:Jo.boolean().default(!1)}),js={heuristicEnabled:!0,agentEnabled:!1};function su(){let e=nu();if(!Zl(e))return{};try{return JSON.parse(mS(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function dt(){let e=su().autoTitle;if(!e)return{...js};let t=Ds.safeParse({...js,...e});return t.success?t.data:{...js}}function ru(e){_S();let t=su(),n=Ds.parse({...js,...t.autoTitle??{},...e}),s={...t,autoTitle:n};return gS(nu(),JSON.stringify(s,null,2)),n}q();ne();import{randomUUID as zo}from"node:crypto";import{existsSync as wS,mkdirSync as RS,writeFileSync as mu}from"node:fs";import{homedir as kS}from"node:os";import{basename as AS,join as Vo}from"node:path";q();ne();import{randomUUID as hS}from"node:crypto";import{writeFileSync as ES,readFileSync as AC,existsSync as xC}from"node:fs";import{join as bS}from"node:path";var SS=bS(W,"collections.json"),Ps=8;function Fs(e){return{...e}}function Re(e,t,n,s=null,r=new Date().toISOString()){h().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
1223
+ VALUES (?, ?, ?, ?, ?)`).run(e,s,t,n?JSON.stringify(n):null,r)}function $s(e){let t=h().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function ou(e){if(!e)return 0;let t=0,n=e,s=new Set,r=h();for(;n;){if(s.has(n))throw new Error("collection cycle detected");s.add(n);let o=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(n);if(!o)break;t+=1,n=o.parent_id}return t}function yS(e,t){let n=h(),s=e,r=new Set;for(;s;){if(r.has(s))return!1;if(r.add(s),s===t)return!0;let o=n.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!o)return!1;s=o.parent_id}return!1}function iu(e=!1){return h().prepare(`SELECT c.*,
1157
1224
  (SELECT COUNT(*) FROM collection_sessions cs WHERE cs.collection_id = c.id) AS session_count
1158
1225
  FROM collections c
1159
1226
  ${e?"":"WHERE c.archived_at IS NULL"}
1160
- ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(s=>({...ms(s),session_count:s.session_count}))}function Be(e){let t=f().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?ms(t):null}function ml(e,t=!0){let n=f(),s=t?lo(e):[e];if(s.length===0)return[];let r=s.map(()=>"?").join(",");return n.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1227
+ ORDER BY c.parent_id IS NOT NULL, c.parent_id, c.sort_key, LOWER(c.name)`).all().map(s=>({...Fs(s),session_count:s.session_count}))}function Ye(e){let t=h().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Fs(t):null}function au(e,t=!0){let n=h(),s=t?Go(e):[e];if(s.length===0)return[];let r=s.map(()=>"?").join(",");return n.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1161
1228
  FROM collection_sessions
1162
1229
  WHERE collection_id IN (${r})
1163
- ORDER BY added_at DESC`).all(...s)}function lo(e){let t=f(),n=[e],s=[e],r=new Set([e]);for(;s.length>0;){let o=s.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...s),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),n.push(u.id),c.push(u.id));s=c}return n}function gl(e){return f().prepare(`SELECT c.* FROM collections c
1230
+ ORDER BY added_at DESC`).all(...s)}function Go(e){let t=h(),n=[e],s=[e],r=new Set([e]);for(;s.length>0;){let o=s.map(()=>"?").join(","),a=t.prepare(`SELECT id FROM collections WHERE parent_id IN (${o})`).all(...s),c=[];for(let u of a)r.has(u.id)||(r.add(u.id),n.push(u.id),c.push(u.id));s=c}return n}function cu(e){return h().prepare(`SELECT c.* FROM collections c
1164
1231
  JOIN collection_sessions cs ON cs.collection_id = c.id
1165
1232
  WHERE cs.session_id = ? AND c.archived_at IS NULL
1166
- ORDER BY LOWER(c.name)`).all(e)}function sn(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=f(),s=new Date().toISOString(),r=IE();if(e.parent_id){if(!Be(e.parent_id))throw new Error("parent collection not found");if(dl(e.parent_id)>=ps-1)throw new Error(`max collection depth is ${ps}`)}return n.transaction(()=>{n.prepare(`INSERT INTO collections
1233
+ ORDER BY LOWER(c.name)`).all(e)}function yn(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let n=h(),s=new Date().toISOString(),r=hS();if(e.parent_id){if(!Ye(e.parent_id))throw new Error("parent collection not found");if(ou(e.parent_id)>=Ps-1)throw new Error(`max collection depth is ${Ps}`)}return n.transaction(()=>{n.prepare(`INSERT INTO collections
1167
1234
  (id, name, description, icon, color, parent_id, sort_key, created_at, updated_at, archived_at)
1168
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",s,s),Re(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,s)})(),rt(),Be(r)}function _l(e,t){let n=f(),s=gs(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():s.name,description:t.description!==void 0?t.description:s.description,icon:t.icon!==void 0?t.icon:s.icon,color:t.color!==void 0?t.color:s.color,parent_id:t.parent_id!==void 0?t.parent_id:s.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:s.sort_key};if(!o.name)throw new Error("name required");if(o.name.length>120)throw new Error("name too long (max 120 chars)");if(t.parent_id!==void 0&&t.parent_id!==s.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Be(t.parent_id))throw new Error("parent collection not found");if(FE(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(dl(t.parent_id)>=ps-1)throw new Error(`max collection depth is ${ps}`)}return n.transaction(()=>{n.prepare(`UPDATE collections
1235
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",s,s),Re(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,s)})(),pt(),Ye(r)}function lu(e,t){let n=h(),s=$s(e),r=new Date().toISOString(),o={name:t.name!==void 0?t.name.trim():s.name,description:t.description!==void 0?t.description:s.description,icon:t.icon!==void 0?t.icon:s.icon,color:t.color!==void 0?t.color:s.color,parent_id:t.parent_id!==void 0?t.parent_id:s.parent_id,sort_key:t.sort_key!==void 0?t.sort_key:s.sort_key};if(!o.name)throw new Error("name required");if(o.name.length>120)throw new Error("name too long (max 120 chars)");if(t.parent_id!==void 0&&t.parent_id!==s.parent_id&&t.parent_id){if(t.parent_id===e)throw new Error("cannot set parent to self");if(!Ye(t.parent_id))throw new Error("parent collection not found");if(yS(t.parent_id,e))throw new Error("cannot move collection into one of its descendants");if(ou(t.parent_id)>=Ps-1)throw new Error(`max collection depth is ${Ps}`)}return n.transaction(()=>{n.prepare(`UPDATE collections
1169
1236
  SET name = ?, description = ?, icon = ?, color = ?,
1170
1237
  parent_id = ?, sort_key = ?, updated_at = ?
1171
- WHERE id = ?`).run(o.name,o.description,o.icon,o.color,o.parent_id,o.sort_key,r,e),t.name!==void 0&&t.name!==s.name&&Re(e,"rename",{from:s.name,to:o.name},null,r),t.description!==void 0&&t.description!==s.description&&Re(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==s.icon||t.color!==void 0&&t.color!==s.color)&&Re(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==s.parent_id&&Re(e,"move",{from:s.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==s.sort_key&&Re(e,"reorder",{from:s.sort_key,to:o.sort_key},null,r)})(),rt(),Be(e)}function fl(e){let t=f(),n=gs(e);if(n.archived_at)return ms(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(s,s,e),Re(e,"archive",{name:n.name},null,s)})(),rt(),Be(e)}function hl(e){let t=f(),n=gs(e);if(!n.archived_at)return ms(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(s,e),Re(e,"restore",{name:n.name},null,s)})(),rt(),Be(e)}function rn(e,t,n=null,s={}){let r=f();if(gs(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let c=s.source??"manual",u=s.rule_id??null;if(c==="auto"&&!u)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
1172
- VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,n,c,u),Re(e,"add",{note:n,source:c,rule_id:u},t,d)})(),rt(),{added:!0}}function El(e,t){let n=f();if(!n.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return n.transaction(()=>{n.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),Re(e,"remove",null,t,r)})(),rt(),{removed:!0}}function _s(e){let t=f(),n=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1173
- WHERE rule_id = ?`).all(e);if(n.length===0)return{removed:0};let s=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of n)Re(o.collection_id,"remove",{rule_id:e},o.session_id,s)})(),rt(),{removed:n.length}}function PE(){return f().prepare(`SELECT id, collection_id, session_id, action, payload, at
1238
+ WHERE id = ?`).run(o.name,o.description,o.icon,o.color,o.parent_id,o.sort_key,r,e),t.name!==void 0&&t.name!==s.name&&Re(e,"rename",{from:s.name,to:o.name},null,r),t.description!==void 0&&t.description!==s.description&&Re(e,"describe",{description:o.description},null,r),(t.icon!==void 0&&t.icon!==s.icon||t.color!==void 0&&t.color!==s.color)&&Re(e,"recolor",{icon:o.icon,color:o.color},null,r),t.parent_id!==void 0&&t.parent_id!==s.parent_id&&Re(e,"move",{from:s.parent_id,to:o.parent_id},null,r),t.sort_key!==void 0&&t.sort_key!==s.sort_key&&Re(e,"reorder",{from:s.sort_key,to:o.sort_key},null,r)})(),pt(),Ye(e)}function uu(e){let t=h(),n=$s(e);if(n.archived_at)return Fs(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = ?, updated_at = ? WHERE id = ?").run(s,s,e),Re(e,"archive",{name:n.name},null,s)})(),pt(),Ye(e)}function du(e){let t=h(),n=$s(e);if(!n.archived_at)return Fs(n);let s=new Date().toISOString();return t.transaction(()=>{t.prepare("UPDATE collections SET archived_at = NULL, updated_at = ? WHERE id = ?").run(s,e),Re(e,"restore",{name:n.name},null,s)})(),pt(),Ye(e)}function Tn(e,t,n=null,s={}){let r=h();if($s(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let c=s.source??"manual",u=s.rule_id??null;if(c==="auto"&&!u)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
1239
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,n,c,u),Re(e,"add",{note:n,source:c,rule_id:u},t,d)})(),pt(),{added:!0}}function pu(e,t){let n=h();if(!n.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return n.transaction(()=>{n.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),Re(e,"remove",null,t,r)})(),pt(),{removed:!0}}function Us(e){let t=h(),n=t.prepare(`SELECT collection_id, session_id FROM collection_sessions
1240
+ WHERE rule_id = ?`).all(e);if(n.length===0)return{removed:0};let s=new Date().toISOString();return t.transaction(()=>{t.prepare("DELETE FROM collection_sessions WHERE rule_id = ?").run(e);for(let o of n)Re(o.collection_id,"remove",{rule_id:e},o.session_id,s)})(),pt(),{removed:n.length}}function TS(){return h().prepare(`SELECT id, collection_id, session_id, action, payload, at
1174
1241
  FROM collection_events
1175
- ORDER BY at ASC, id ASC`).all()}function rt(){try{z();let e=f(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
1242
+ ORDER BY at ASC, id ASC`).all()}function pt(){try{V();let e=h(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
1176
1243
  created_at, updated_at, archived_at
1177
1244
  FROM collections
1178
1245
  ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),n=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
1179
1246
  FROM collection_sessions
1180
- ORDER BY collection_id, added_at`).all(),s=PE(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:n,events:s};jE(DE,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var fs=go(W,"auto-rules"),WE=go(fs,"rules.json"),qE=go(fs,"suggestions.json"),uo="Repositories",XE="Topics",Sl=3;var JE=5,GE=2,YE=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function _o(e){return{id:e.id,name:e.name,type:e.type,pattern:e.pattern,collection_id:e.collection_id,priority:e.priority,enabled:e.enabled!==0,created_at:e.created_at,created_by:e.created_by}}function zE(e){return{id:e.id,type:e.type,pattern:e.pattern,suggested_name:e.suggested_name,suggested_parent_collection_id:e.suggested_parent_collection_id,session_count:e.session_count,detected_at:e.detected_at,dismissed:e.dismissed!==0}}function KE(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return uo;case"tag":return XE;case"plan-file":return null}}function VE(e){let n=f().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(n)return n.id;let o=sn({name:e,icon:e===uo?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===uo?"0000-repos":"0001-topics"});return f().prepare(`INSERT INTO auto_collection_rules
1247
+ ORDER BY collection_id, added_at`).all(),s=TS(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:n,events:s};ES(SS,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Hs=Vo(W,"auto-rules"),xS=Vo(Hs,"rules.json"),NS=Vo(Hs,"suggestions.json"),Yo="Repositories",OS="Topics",gu=3;var CS=5,LS=2,vS=[/\bROADMAP\.md\b/g,/\bPROJECT\.md\b/g,/\bdocs\/[A-Za-z0-9._-]+\.md\b/g,/\.planning\/[A-Za-z0-9._\-/]+/g];function Qo(e){return{id:e.id,name:e.name,type:e.type,pattern:e.pattern,collection_id:e.collection_id,priority:e.priority,enabled:e.enabled!==0,created_at:e.created_at,created_by:e.created_by}}function IS(e){return{id:e.id,type:e.type,pattern:e.pattern,suggested_name:e.suggested_name,suggested_parent_collection_id:e.suggested_parent_collection_id,session_count:e.session_count,detected_at:e.detected_at,dismissed:e.dismissed!==0}}function MS(e){switch(e){case"cwd-prefix":case"project-id":case"git-branch-prefix":return Yo;case"tag":return OS;case"plan-file":return null}}function jS(e){let n=h().prepare("SELECT id FROM collections WHERE name = ? AND parent_id IS NULL AND archived_at IS NULL").get(e);if(n)return n.id;let o=yn({name:e,icon:e===Yo?"\u{1F4E6}":"\u{1F3F7}",sort_key:e===Yo?"0000-repos":"0001-topics"});return h().prepare(`INSERT INTO auto_collection_rules
1181
1248
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
1182
- VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(mo(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function ZE(e,t,n){let s;if(n!==void 0)s=n;else{let o=KE(t);s=o?VE(o):null}return sn({name:e,parent_id:s}).id}function hs(e){let t=f().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?_o(t):null}function QE(e){let t=f();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(Es(e.pattern)+"%").map(s=>s.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(Es(e.pattern)+"%").map(s=>s.id);case"project-id":{let n=Number(e.pattern);return Number.isFinite(n)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(n).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(s=>s.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1183
- WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(s=>s.id)}}function Tl(e,t,n=3){let s=f(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1249
+ VALUES (?, ?, 'cwd-prefix', '__seed__', ?, 1000, 0, ?, 'seed')`).run(zo(),`seed:${e}`,o.id,new Date().toISOString()),o.id}function DS(e,t,n){let s;if(n!==void 0)s=n;else{let o=MS(t);s=o?jS(o):null}return yn({name:e,parent_id:s}).id}function Bs(e){let t=h().prepare("SELECT * FROM auto_collection_rules WHERE id = ?").get(e);return t?Qo(t):null}function PS(e){let t=h();switch(e.type){case"cwd-prefix":return t.prepare("SELECT id FROM sessions WHERE cwd IS NOT NULL AND cwd LIKE ? ESCAPE '\\'").all(Ws(e.pattern)+"%").map(s=>s.id);case"git-branch-prefix":return t.prepare("SELECT id FROM sessions WHERE git_branch IS NOT NULL AND git_branch LIKE ? ESCAPE '\\'").all(Ws(e.pattern)+"%").map(s=>s.id);case"project-id":{let n=Number(e.pattern);return Number.isFinite(n)?t.prepare("SELECT id FROM sessions WHERE project_id = ?").all(n).map(r=>r.id):[]}case"tag":return t.prepare("SELECT session_id FROM session_tags WHERE tag = ?").all(e.pattern).map(s=>s.session_id);case"plan-file":return t.prepare(`SELECT id, first_user_message FROM sessions
1250
+ WHERE first_user_message IS NOT NULL AND first_user_message LIKE ?`).all("%"+e.pattern+"%").map(s=>s.id)}}function fu(e,t,n=3){let s=h(),r=`s.id AS id, s.cwd AS cwd, s.started_at AS started_at,
1184
1251
  sa.alias AS alias, s.auto_title AS auto_title, s.first_user_message AS first_user_message`,o="LEFT JOIN session_aliases sa ON sa.session_id = s.id",a=[];switch(e){case"cwd-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1185
1252
  WHERE s.cwd IS NOT NULL AND s.cwd LIKE ? ESCAPE '\\'
1186
- ORDER BY s.started_at DESC LIMIT ?`).all(Es(t)+"%",n);break;case"git-branch-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1253
+ ORDER BY s.started_at DESC LIMIT ?`).all(Ws(t)+"%",n);break;case"git-branch-prefix":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1187
1254
  WHERE s.git_branch IS NOT NULL AND s.git_branch LIKE ? ESCAPE '\\'
1188
- ORDER BY s.started_at DESC LIMIT ?`).all(Es(t)+"%",n);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1255
+ ORDER BY s.started_at DESC LIMIT ?`).all(Ws(t)+"%",n);break;case"project-id":{let c=Number(t);Number.isFinite(c)&&(a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1189
1256
  WHERE s.project_id = ?
1190
1257
  ORDER BY s.started_at DESC LIMIT ?`).all(c,n));break}case"tag":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1191
1258
  JOIN session_tags st ON st.session_id = s.id
@@ -1193,22 +1260,39 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1193
1260
  ORDER BY s.started_at DESC LIMIT ?`).all(t,n);break;case"plan-file":a=s.prepare(`SELECT ${r} FROM sessions s ${o}
1194
1261
  WHERE s.first_user_message IS NOT NULL
1195
1262
  AND s.first_user_message LIKE ?
1196
- ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",n);break}return a.map(c=>({id:c.id,title:eb(c),cwd:c.cwd,started_at:c.started_at}))}function eb(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return`session ${e.id.slice(0,8)}`;let n=t.split(`
1197
- `)[0].trim();return n.length>80?n.slice(0,80)+"\u2026":n}function tb(e,t,n=f()){switch(e.type){case"cwd-prefix":return!!t.cwd&&t.cwd.startsWith(e.pattern);case"git-branch-prefix":return!!t.git_branch&&t.git_branch.startsWith(e.pattern);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)&&t.project_id===s}case"tag":return!!n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(t.id,e.pattern);case"plan-file":return!!t.first_user_message&&t.first_user_message.includes(e.pattern)}}function yl(e){let t=f(),n=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!n)return{added:0};let s=t.prepare(`SELECT * FROM auto_collection_rules
1263
+ ORDER BY s.started_at DESC LIMIT ?`).all("%"+t+"%",n);break}return a.map(c=>({id:c.id,title:FS(c),cwd:c.cwd,started_at:c.started_at}))}function FS(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return`session ${e.id.slice(0,8)}`;let n=t.split(`
1264
+ `)[0].trim();return n.length>80?n.slice(0,80)+"\u2026":n}function $S(e,t,n=h()){switch(e.type){case"cwd-prefix":return!!t.cwd&&t.cwd.startsWith(e.pattern);case"git-branch-prefix":return!!t.git_branch&&t.git_branch.startsWith(e.pattern);case"project-id":{let s=Number(e.pattern);return Number.isFinite(s)&&t.project_id===s}case"tag":return!!n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(t.id,e.pattern);case"plan-file":return!!t.first_user_message&&t.first_user_message.includes(e.pattern)}}function _u(e){let t=h(),n=t.prepare("SELECT id, project_id, cwd, git_branch, first_user_message FROM sessions WHERE id = ?").get(e);if(!n)return{added:0};let s=t.prepare(`SELECT * FROM auto_collection_rules
1198
1265
  WHERE enabled = 1 AND created_by != 'seed'
1199
- ORDER BY priority, created_at`).all(),r=0;for(let o of s){let a=_o(o);if(tb(a,n,t))try{rn(a.collection_id,e,null,{source:"auto",rule_id:a.id}).added&&(r+=1)}catch(c){console.error(`[auto-collections] failed to apply rule ${a.id} to session ${e}:`,c)}}return{added:r}}function po(e){if(!e.enabled)return{added:0};let t=0;for(let n of QE(e))try{rn(e.collection_id,n,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(s){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${n}:`,s)}return{added:t}}function fo(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let n=(e.pattern??"").trim();if(!n)throw new Error("pattern required");let s=f(),r=new Date().toISOString(),o=mo(),a=e.collection_id;a||(a=ZE(t,e.type,e.parent_collection_id)),s.prepare(`INSERT INTO auto_collection_rules
1266
+ ORDER BY priority, created_at`).all(),r=0;for(let o of s){let a=Qo(o);if($S(a,n,t))try{Tn(a.collection_id,e,null,{source:"auto",rule_id:a.id}).added&&(r+=1)}catch(c){console.error(`[auto-collections] failed to apply rule ${a.id} to session ${e}:`,c)}}return{added:r}}function Ko(e){if(!e.enabled)return{added:0};let t=0;for(let n of PS(e))try{Tn(e.collection_id,n,null,{source:"auto",rule_id:e.id}).added&&(t+=1)}catch(s){console.error(`[auto-collections] backfill failed for rule ${e.id} / session ${n}:`,s)}return{added:t}}function Zo(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");let n=(e.pattern??"").trim();if(!n)throw new Error("pattern required");let s=h(),r=new Date().toISOString(),o=zo(),a=e.collection_id;a||(a=DS(t,e.type,e.parent_collection_id)),s.prepare(`INSERT INTO auto_collection_rules
1200
1267
  (id, name, type, pattern, collection_id, priority, enabled, created_at, created_by)
1201
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,n,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),s.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,n);let c=hs(o);return po(c),wt(),c}function wl(e={}){let t=e.includeSeed?"SELECT * FROM auto_collection_rules ORDER BY priority, created_at":"SELECT * FROM auto_collection_rules WHERE created_by != 'seed' ORDER BY priority, created_at";return f().prepare(t).all().map(_o)}function Rl(e,t){let n=f(),s=hs(e);if(!s)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():s.name,pattern:t.pattern!==void 0?t.pattern.trim():s.pattern,enabled:t.enabled!==void 0?t.enabled:s.enabled,priority:t.priority!==void 0?t.priority:s.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");n.prepare(`UPDATE auto_collection_rules
1268
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(o,t,e.type,n,a,e.priority??100,e.enabled===!1?0:1,r,e.created_by??"user"),s.prepare("DELETE FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").run(e.type,n);let c=Bs(o);return Ko(c),Mt(),c}function hu(e={}){let t=e.includeSeed?"SELECT * FROM auto_collection_rules ORDER BY priority, created_at":"SELECT * FROM auto_collection_rules WHERE created_by != 'seed' ORDER BY priority, created_at";return h().prepare(t).all().map(Qo)}function Eu(e,t){let n=h(),s=Bs(e);if(!s)throw new Error(`rule not found: ${e}`);let r={name:t.name!==void 0?t.name.trim():s.name,pattern:t.pattern!==void 0?t.pattern.trim():s.pattern,enabled:t.enabled!==void 0?t.enabled:s.enabled,priority:t.priority!==void 0?t.priority:s.priority};if(!r.name)throw new Error("name required");if(!r.pattern)throw new Error("pattern required");n.prepare(`UPDATE auto_collection_rules
1202
1269
  SET name = ?, pattern = ?, enabled = ?, priority = ?
1203
- WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=hs(e);return t.pattern!==void 0&&t.pattern!==s.pattern?(_s(e),o.enabled&&po(o)):t.enabled!==void 0&&t.enabled!==s.enabled&&(o.enabled?po(o):_s(e)),wt(),o}function kl(e){let t=f();if(!hs(e))return{removed:0};let s=_s(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),wt(),s}function bs(e={}){let t=e.includeDismissed?"SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC":"SELECT * FROM auto_collection_suggestions WHERE dismissed = 0 ORDER BY detected_at DESC";return f().prepare(t).all().map(zE)}function Al(e){f().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),wt()}function xl(e){let t=f(),n=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!n)throw new Error(`suggestion not found: ${e}`);if(n.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let s=fo({name:n.suggested_name,type:n.type,pattern:n.pattern,parent_collection_id:n.suggested_parent_collection_id===null?void 0:n.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),wt(),s}function Ss(){let e=f(),t=new Date().toISOString(),n=BE(),s=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...nb(n,t).filter(c=>!s.has(c.pattern)),...sb(t),...rb(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),s.size>0){let c=Array.from(s).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(s))}let o=e.prepare("SELECT type, pattern FROM auto_collection_rules").all(),a=new Set(o.map(c=>`${c.type}:${c.pattern}`));for(let c of r){let u=`${c.type}:${c.pattern}`;if(a.has(u))continue;let d=e.prepare("SELECT id FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").get(c.type,c.pattern);d?e.prepare(`UPDATE auto_collection_suggestions
1270
+ WHERE id = ?`).run(r.name,r.pattern,r.enabled?1:0,r.priority,e);let o=Bs(e);return t.pattern!==void 0&&t.pattern!==s.pattern?(Us(e),o.enabled&&Ko(o)):t.enabled!==void 0&&t.enabled!==s.enabled&&(o.enabled?Ko(o):Us(e)),Mt(),o}function bu(e){let t=h();if(!Bs(e))return{removed:0};let s=Us(e);return t.prepare("DELETE FROM auto_collection_rules WHERE id = ?").run(e),Mt(),s}function qs(e={}){let t=e.includeDismissed?"SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC":"SELECT * FROM auto_collection_suggestions WHERE dismissed = 0 ORDER BY detected_at DESC";return h().prepare(t).all().map(IS)}function Su(e){h().prepare("UPDATE auto_collection_suggestions SET dismissed = 1 WHERE id = ?").run(e),Mt()}function yu(e){let t=h(),n=t.prepare("SELECT * FROM auto_collection_suggestions WHERE id = ?").get(e);if(!n)throw new Error(`suggestion not found: ${e}`);if(n.dismissed)throw new Error(`suggestion already dismissed: ${e}`);let s=Zo({name:n.suggested_name,type:n.type,pattern:n.pattern,parent_collection_id:n.suggested_parent_collection_id===null?void 0:n.suggested_parent_collection_id,created_by:"suggestion-accepted"});return t.prepare("DELETE FROM auto_collection_suggestions WHERE id = ?").run(e),Mt(),s}function Xs(){let e=h(),t=new Date().toISOString(),n=kS(),s=new Set(e.prepare("SELECT decoded_path FROM projects").all().map(c=>c.decoded_path)),r=[...US(n,t).filter(c=>!s.has(c.pattern)),...HS(t),...BS(t)];if(e.prepare("DELETE FROM auto_collection_suggestions WHERE type = 'project-id'").run(),s.size>0){let c=Array.from(s).map(()=>"?").join(",");e.prepare(`DELETE FROM auto_collection_suggestions WHERE type = 'cwd-prefix' AND pattern IN (${c})`).run(...Array.from(s))}let o=e.prepare("SELECT type, pattern FROM auto_collection_rules").all(),a=new Set(o.map(c=>`${c.type}:${c.pattern}`));for(let c of r){let u=`${c.type}:${c.pattern}`;if(a.has(u))continue;let d=e.prepare("SELECT id FROM auto_collection_suggestions WHERE type = ? AND pattern = ?").get(c.type,c.pattern);d?e.prepare(`UPDATE auto_collection_suggestions
1204
1271
  SET session_count = ?, detected_at = ?, suggested_name = ?, suggested_parent_collection_id = ?
1205
1272
  WHERE id = ?`).run(c.session_count,t,c.suggested_name,c.suggested_parent_collection_id,d.id):e.prepare(`INSERT INTO auto_collection_suggestions
1206
1273
  (id, type, pattern, suggested_name, suggested_parent_collection_id, session_count, detected_at, dismissed)
1207
- VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(mo(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return wt(),bs()}function nb(e,t){let s=f().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of s){let c=a.cwd.split("/").filter(Boolean),u="";for(let d of c){if(u=`${u}/${d}`,u===e||u==="/")continue;let m=r.get(u);m||(m=new Set,r.set(u,m)),m.add(a.id)}}let o=[];for(let[a,c]of r.entries()){if(c.size<Sl)continue;let u=!1;for(let[d,m]of r.entries())if(d!==a&&d.startsWith(a+"/")&&m.size>=Sl){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:HE(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function sb(e){return f().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(JE).map(n=>({type:"tag",pattern:n.tag,suggested_name:n.tag,suggested_parent_collection_id:null,session_count:n.n,detected_at:e,dismissed:!1}))}function rb(e){let t=f().prepare(`SELECT id, first_user_message FROM sessions
1208
- WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),n=new Map;for(let r of t)for(let o of YE){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=n.get(c);u||(u=new Set,n.set(c,u)),u.add(r.id)}}let s=[];for(let[r,o]of n.entries())o.size<GE||s.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return s}function Es(e){return e.replace(/[\\%_]/g,"\\$&")}function wt(){try{z(),$E(fs)||UE(fs,{recursive:!0});let e=f(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),n=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();bl(WE,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),bl(qE,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:n},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function Nl(){let e=f().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}var ob=/^[ \t]*<!--\s*claude-recall-alias\s*:\s*([^\n]+?)\s*-->[ \t]*\n?/;function ho(e){if(e==null)return{alias:null,stripped:""};let t=e.match(ob);if(!t)return{alias:null,stripped:e};let n=t[1].trim();return n?{alias:n.length>200?n.slice(0,200):n,stripped:e.slice(t[0].length)}:{alias:null,stripped:e.slice(t[0].length)}}var Ts=new Map;function Ol(e,t){let s=(Ts.get(e)??Promise.resolve()).catch(()=>{}).then(t);return Ts.set(e,s),s.catch(()=>{}).finally(()=>{Ts.get(e)===s&&Ts.delete(e)}),s}var lb=1500,Eo=new Map;function vl(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}function Il(e){return e.replace(/\\/g,"/").includes("/subagents/")}function ub(e){let s=f().prepare("SELECT COUNT(*) AS n FROM sessions WHERE file_path = ?").get(e).n;console.log(`[watcher] indexed ${Cl(e)} (${s} session${s===1?"":"s"})`)}async function db(e){let t=0;try{t=bo(e).mtimeMs}catch{return}let n=vl(e);if(!n)return;let s=f(),r=s.prepare("SELECT id, file_mtime FROM sessions WHERE file_path = ? LIMIT 1").get(e);if(r&&r.file_mtime>=t)return;let o=new Map,a=null;for await(let L of Ac(e)){let x=o.get(L.sessionId);if(x||(x={sessionId:L.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},o.set(L.sessionId,x)),x.entries.push(L),L.timestamp&&((!x.earliest||L.timestamp<x.earliest)&&(x.earliest=L.timestamp),(!x.latest||L.timestamp>x.latest)&&(x.latest=L.timestamp)),L.role==="user"&&!L.isSidechain){if(x.users+=1,!x.firstUser&&L.contentText){let F=L.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();F&&!/^<local-command-caveat>/.test(F)&&(x.firstUser=Se(F).redacted.slice(0,2e3))}}else L.role==="assistant"&&!L.isSidechain&&(x.assistants+=1);!x.cwd&&L.cwd&&(x.cwd=L.cwd),!x.branch&&L.gitBranch&&(x.branch=L.gitBranch),!x.version&&L.version&&(x.version=L.version),!a&&L.cwd&&(a=L.cwd)}let c=a?Cl(a)||a:n,u=a??n.replace(/^-/,"/").replace(/-/g,"/"),d=s.prepare(`INSERT INTO projects (encoded_path, decoded_path, name)
1209
- VALUES (?, ?, ?)
1210
- ON CONFLICT(encoded_path) DO UPDATE SET decoded_path = excluded.decoded_path, name = excluded.name
1211
- RETURNING id`),m=s.prepare(`
1274
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0)`).run(zo(),c.type,c.pattern,c.suggested_name,c.suggested_parent_collection_id,c.session_count,t)}return Mt(),qs()}function US(e,t){let s=h().prepare("SELECT id, cwd FROM sessions WHERE cwd IS NOT NULL AND cwd != ''").all(),r=new Map;for(let a of s){let c=a.cwd.split("/").filter(Boolean),u="";for(let d of c){if(u=`${u}/${d}`,u===e||u==="/")continue;let p=r.get(u);p||(p=new Set,r.set(u,p)),p.add(a.id)}}let o=[];for(let[a,c]of r.entries()){if(c.size<gu)continue;let u=!1;for(let[d,p]of r.entries())if(d!==a&&d.startsWith(a+"/")&&p.size>=gu){u=!0;break}u||o.push({type:"cwd-prefix",pattern:a,suggested_name:AS(a)||a,suggested_parent_collection_id:null,session_count:c.size,detected_at:t,dismissed:!1})}return o}function HS(e){return h().prepare("SELECT tag, COUNT(*) AS n FROM session_tags GROUP BY tag HAVING n >= ?").all(CS).map(n=>({type:"tag",pattern:n.tag,suggested_name:n.tag,suggested_parent_collection_id:null,session_count:n.n,detected_at:e,dismissed:!1}))}function BS(e){let t=h().prepare(`SELECT id, first_user_message FROM sessions
1275
+ WHERE first_user_message IS NOT NULL AND first_user_message != ''`).all(),n=new Map;for(let r of t)for(let o of vS){o.lastIndex=0;let a=r.first_user_message.match(o);if(a)for(let c of a){let u=n.get(c);u||(u=new Set,n.set(c,u)),u.add(r.id)}}let s=[];for(let[r,o]of n.entries())o.size<LS||s.push({type:"plan-file",pattern:r,suggested_name:r,suggested_parent_collection_id:null,session_count:o.size,detected_at:e,dismissed:!1});return s}function Ws(e){return e.replace(/[\\%_]/g,"\\$&")}function Mt(){try{V(),wS(Hs)||RS(Hs,{recursive:!0});let e=h(),t=e.prepare("SELECT * FROM auto_collection_rules ORDER BY created_at, id").all(),n=e.prepare("SELECT * FROM auto_collection_suggestions ORDER BY detected_at DESC, id").all();mu(xS,JSON.stringify({schema:"claude-recall.auto-rules.v1",backed_up_at:new Date().toISOString(),rules:t},null,2)),mu(NS,JSON.stringify({schema:"claude-recall.auto-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:n},null,2))}catch(e){console.error("[auto-collections] backup failed:",e)}}function Tu(){let e=h().prepare("SELECT DISTINCT collection_id FROM auto_collection_rules").all();return new Set(e.map(t=>t.collection_id))}var WS=/^[ \t]*<!--\s*claude-recall-alias\s*:\s*([^\n]+?)\s*-->[ \t]*\n?/;function ei(e){if(e==null)return{alias:null,stripped:""};let t=e.match(WS);if(!t)return{alias:null,stripped:e};let n=t[1].trim();return n?{alias:n.length>200?n.slice(0,200):n,stripped:e.slice(t[0].length)}:{alias:null,stripped:e.slice(t[0].length)}}var Js=new Map;function wu(e,t){let s=(Js.get(e)??Promise.resolve()).catch(()=>{}).then(t);return Js.set(e,s),s.catch(()=>{}).finally(()=>{Js.get(e)===s&&Js.delete(e)}),s}import{realpathSync as qS,statSync as XS,readFileSync as JS}from"node:fs";import{dirname as Ru,join as GS,parse as YS}from"node:path";var jt="(temporary sessions)";function Rn(e){return e?e.startsWith("/private/var/folders/")||e.startsWith("/var/folders/")||e.startsWith("/tmp/")||e.startsWith("/private/tmp/")||e==="/tmp"||e==="/private/tmp":!1}function ku(e,t){let n=e;for(;n!=="/"&&n!=="."&&n!=="";){if(t.has(n))return n;let s=Ru(n);if(s===n)break;n=s}return null}var wn=new Map;function Gs(e,t={}){let n=t.foldWorktrees===!0,s=`${n?"1":"0"}:${e}`,r=wn.get(s);if(r)return r;let o={root:e,isRepo:!1,isWorktree:!1,mainRepo:null},a;try{a=qS(e)}catch{return wn.set(s,o),o}let{root:c}=YS(a),u=a;for(;;){let d=GS(u,".git"),p=null;try{p=XS(d)}catch{p=null}if(p&&p.isDirectory()){let f={root:u,isRepo:!0,isWorktree:!1,mainRepo:null};return wn.set(s,f),f}if(p&&p.isFile()){let f=KS(d),b={root:n&&f?f:u,isRepo:!0,isWorktree:f!==null,mainRepo:f};return wn.set(s,b),b}if(u===c)break;u=Ru(u)}return wn.set(s,o),o}function KS(e){let t;try{t=JS(e,"utf8")}catch{return null}let n=t.match(/^gitdir:\s*(.+?)\s*$/m);if(!n)return null;let s=n[1].trim(),o=s.indexOf("/.git/worktrees/");return o<0?null:s.slice(0,o)}q();bt();q();function Au(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function zS(e){let t=[],o=0;for(;o<e.length;){let a=[],c=0;for(;a.length<5&&o<e.length;){let d=e[o],p=Au(d.content_text??"");if(c+p.length>2e3&&a.length>=3)break;a.push(d),c+=p.length,o++}if(a.length===0)break;let u=a.map(d=>{let p=d.role??"system",f=Au(d.content_text??"");return`[${p}] ${f}`}).join(`
1276
+
1277
+ `);t.push({messageUuids:a.map(d=>d.uuid),text:u}),o<e.length&&a.length>=3&&(o=Math.max(o-1,o-1))}return t}function xu(e){let n=h().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return zS(n)}q();function ti(e){let t=h();t.transaction(()=>{t.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()&&t.prepare("DELETE FROM vec_chunks_v1_backup WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(e)})()}function ri(){try{return!!h().prepare("SELECT 1 AS held FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: migration_state"))return!1;throw e}}function VS(){return h().prepare("UPDATE chunk_queue SET action = 'embed' WHERE action = 'pending_post_migration'").run().changes}function QS(){try{return!!h().prepare("SELECT 1 AS present FROM chunk_queue WHERE action = 'pending_post_migration' LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: chunk_queue"))return!1;throw e}}var Mu=2e3,ZS=1e4,mt=null,Ys=!1,oi=null,ni=0;function ju(e){ni=Math.max(0,Math.floor(e))}var Ie=new Set,Du=-1;function Pu(e){Ie.add(e)}function Fu(){Ie.add(Du)}function Nu(e){if(Ie.has(Du))return!0;if(Ie.size===0)return!1;let t=h().prepare("SELECT project_id FROM sessions WHERE id = ?").get(e);return t?Ie.has(t.project_id):!1}var Ou=10,ey=200,ty=2e3;function ny(){let e=Number(process.env.RECALL_VECTOR_HARD_CAP??"");return Number.isFinite(e)&&e>0?Math.min(ty,Math.floor(e)):ey}var sy=3,Cu=1e3,kn=new Map,si=new Set;function Lu(e,t,n,s){if(e.size>=Cu&&(e instanceof Map,!e.has(t))){let r=e.keys().next();r.done||(e.delete(r.value),console.warn(`[vector-worker] ${s??"tracking-map"} hit ${Cu}-entry cap; evicted oldest entry`))}e instanceof Map?e.set(t,n):e.add(t)}var An=[],ry=50,vu=.3,Dt=[],oy=50,Iu=!1,xn=null,iy=0;function ay(e){if(!Iu){Iu=!0;return}e<=0||(An.push({ts:Date.now(),durationMs:e}),An.length>ry&&An.shift(),xn=xn===null?e:vu*e+(1-vu)*xn,iy+=1)}function cy(e){e<=0||(Dt.push({ts:Date.now(),chunks:e}),Dt.length>oy&&Dt.shift())}function ii(){if(An.length<5||xn===null)return null;let e=1e3/xn,t=Dt.length>0?Dt.reduce((n,s)=>n+s.chunks,0)/Dt.length:30;return{samples:An.length,chunksPerSec:e,sessionsPerSec:e/Math.max(1,t),avgChunksPerSession:t,lastProcessedAt:oi}}function ly(){return h().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function uy(){if(ri())return!1;let e=h(),t=e.prepare("SELECT DISTINCT session_id FROM chunk_queue ORDER BY id LIMIT 1").get();if(!t)return Ie.size>0&&Ie.clear(),!1;if(Nu(t.session_id)||si.has(t.session_id))return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(t.session_id),!0;let n=t.session_id;try{ti(n);let s=xu(n),r=ni>0?ni:1/0,o=Math.min(r,ny()),a=o<s.length?s.slice(0,o):s;if(a.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),!0;let c=pe().modelId,u=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, ?, 768, 0, datetime('now'))"),d=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)"),p=0,f=!1;for(let S=0;S<a.length;S+=Ou){if(Nu(n)){f=!0;break}let y=Math.min(a.length,S+Ou),b=a.slice(S,y),R=b.map(v=>v.text),k=Date.now(),M=await Et(R),P=Date.now()-k;e.transaction(()=>{for(let v=0;v<b.length;v++){let F=u.run(n,JSON.stringify(b[v].messageUuids),b[v].text,c),U=Buffer.from(M[v].buffer,M[v].byteOffset,M[v].byteLength);d.run(BigInt(F.lastInsertRowid),U)}})();let D=b.length>0?P/b.length:0;for(let v=0;v<b.length;v++)ay(D);p+=b.length,await new Promise(v=>setImmediate(v))}f&&ti(n),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),oi=new Date().toISOString(),cy(p),kn.has(n)&&kn.delete(n)}catch(s){console.error("[vector-worker] failed for session",n,s);let r=(kn.get(n)??0)+1;Lu(kn,n,r,"failureCounts"),r>=sy&&(Lu(si,n,void 0,"blacklist"),kn.delete(n),console.error(`[vector-worker] blacklisted session ${n} after ${r} failures \u2014 will not retry until daemon restart`)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n)}finally{if(Ie.size>0)try{let s=e.prepare("SELECT project_id FROM sessions WHERE id = ?").get(n);s!==void 0&&Ie.has(s.project_id)&&(e.prepare("SELECT 1 FROM chunk_queue cq JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ? LIMIT 1").get(s.project_id)||Ie.delete(s.project_id))}catch(s){console.error("[vector-worker] cancelledProjects cleanup failed:",s)}}return!0}async function $u(){if(!pe().loaded)return;QS()&&VS();let e=await uy();mt!==null&&clearTimeout(mt),mt=setTimeout(()=>{$u()},e?Mu:ZS)}function Nn(){if(!Ys){if(!pe().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}Ys=!0,mt=setTimeout(()=>{$u()},Mu)}}function Uu(){Ys=!1,mt!==null&&(clearTimeout(mt),mt=null)}function Me(){return{running:Ys,queueDepth:ly(),lastProcessedAt:oi,blacklistedCount:si.size,pausedForMigration:ri()}}var je=new Map;function Hu(e,t=Date.now()){let n=je.get(e);if(n?n.count+=1:je.set(e,{count:1,firstSeenAt:t}),je.size>1e4&&(Bu(t),je.size>1e4)){let s=[...je.entries()].sort((o,a)=>o[1].firstSeenAt-a[1].firstSeenAt),r=je.size-1e4;for(let o=0;o<r;o+=1){let a=s[o];a&&je.delete(a[0])}}}function Bu(e){let t=e-36e5;for(let[n,s]of je)s.firstSeenAt<t&&je.delete(n)}function Ks(e=10,t=Date.now()){Bu(t);let n=[];for(let[s,r]of je)n.push({path:s,count:r.count,firstSeenAt:r.firstSeenAt});return n.sort((s,r)=>r.count-s.count),n.slice(0,e)}var gy=1500,ai=new Map;function ci(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}function qu(e){return e.replace(/\\/g,"/").includes("/subagents/")}function fy(e){let s=h().prepare("SELECT COUNT(*) AS n FROM sessions WHERE file_path = ?").get(e).n;console.log(`[watcher] indexed ${zs(e)} (${s} session${s===1?"":"s"})`)}function _y(e,t){let n=zs(e).replace(/\.jsonl$/,"");if(!n)return;let s=ci(e);if(!s)return;let o=`parse_failed:${t instanceof Error&&t.name||"Error"}`,a=h(),c=a.prepare("SELECT id, skipped_reason FROM sessions WHERE id = ? LIMIT 1").get(n);if(c?.skipped_reason)return;let u=0;try{u=Vs(e).mtimeMs}catch{}if(c){a.prepare("UPDATE sessions SET skipped_reason = ? WHERE id = ?").run(o,n);return}if(u===0)return;let d=s.replace(/^-/,"/").replace(/-/g,"/"),p=zs(d)||s,{id:f}=a.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1278
+ VALUES (?, ?, ?, NULL, NULL, 0, 0)
1279
+ ON CONFLICT(encoded_path) DO UPDATE SET decoded_path = excluded.decoded_path
1280
+ RETURNING id`).get(s,d,p);a.prepare(`INSERT INTO sessions (
1281
+ id, project_id, file_path, file_mtime,
1282
+ started_at, ended_at, message_count,
1283
+ user_message_count, assistant_message_count,
1284
+ first_user_message, cwd, git_branch, version, indexed_at,
1285
+ skipped_reason
1286
+ ) VALUES (?, ?, ?, ?, NULL, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, ?, ?)`).run(n,f,e,u,new Date().toISOString(),o)}async function hy(e){let t=0;try{t=Vs(e).mtimeMs}catch{return}let n=ci(e);if(!n)return;let s=h(),r=s.prepare("SELECT id, file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1").get(e);if(r?.skipped_reason||r&&r.file_mtime>=t)return;let o=new Map,a=null;for await(let v of bl(e)){let F=o.get(v.sessionId);if(F||(F={sessionId:v.sessionId,entries:[],earliest:null,latest:null,firstUser:null,users:0,assistants:0,cwd:null,branch:null,version:null},o.set(v.sessionId,F)),F.entries.push(v),v.timestamp&&((!F.earliest||v.timestamp<F.earliest)&&(F.earliest=v.timestamp),(!F.latest||v.timestamp>F.latest)&&(F.latest=v.timestamp)),v.role==="user"&&!v.isSidechain){if(F.users+=1,!F.firstUser&&v.contentText){let U=v.contentText.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim();U&&!/^<local-command-caveat>/.test(U)&&(F.firstUser=Se(U).redacted.slice(0,2e3))}}else v.role==="assistant"&&!v.isSidechain&&(F.assistants+=1);!F.cwd&&v.cwd&&(F.cwd=v.cwd),!F.branch&&v.gitBranch&&(F.branch=v.gitBranch),!F.version&&v.version&&(F.version=v.version),!a&&v.cwd&&(a=v.cwd)}let c=a?zs(a)||a:n,u=a??n.replace(/^-/,"/").replace(/-/g,"/"),d=null,p=null,f=0,S=0;if(Rn(u))d=jt;else{let v=Gs(u);v.isRepo&&(d=v.root,p=v.mainRepo,f=1,S=v.isWorktree?1:0)}let y=s.prepare(`INSERT INTO projects (encoded_path, decoded_path, name, repo_root, main_repo, is_repo, is_worktree)
1287
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1288
+ ON CONFLICT(encoded_path) DO UPDATE SET
1289
+ decoded_path = excluded.decoded_path,
1290
+ name = excluded.name,
1291
+ repo_root = excluded.repo_root,
1292
+ main_repo = excluded.main_repo,
1293
+ is_repo = excluded.is_repo,
1294
+ is_worktree = excluded.is_worktree
1295
+ RETURNING id`),b=s.prepare(`
1212
1296
  INSERT INTO sessions (
1213
1297
  id, project_id, file_path, file_mtime,
1214
1298
  started_at, ended_at, message_count,
@@ -1230,13 +1314,13 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1230
1314
  git_branch = excluded.git_branch,
1231
1315
  version = excluded.version,
1232
1316
  indexed_at = excluded.indexed_at
1233
- `),h=s.prepare(`
1317
+ `),R=s.prepare(`
1234
1318
  INSERT INTO messages (uuid, session_id, parent_uuid, type, role, timestamp,
1235
1319
  is_sidechain, content_text, tool_names, raw_json)
1236
1320
  VALUES (@uuid, @session_id, @parent_uuid, @type, @role, @timestamp,
1237
1321
  @is_sidechain, @content_text, @tool_names, @raw_json)
1238
1322
  ON CONFLICT(uuid) DO NOTHING
1239
- `),b=s.prepare("DELETE FROM messages WHERE session_id = ?"),S=s.prepare(`
1323
+ `),k=s.prepare("DELETE FROM messages WHERE session_id = ?"),M=s.prepare(`
1240
1324
  INSERT INTO sessions (
1241
1325
  id, project_id, file_path, file_mtime,
1242
1326
  started_at, ended_at, message_count,
@@ -1251,10 +1335,16 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1251
1335
  file_mtime = excluded.file_mtime,
1252
1336
  indexed_at = excluded.indexed_at,
1253
1337
  skipped_reason = 'daemon_spawn_phantom'
1254
- `),T=new Date().toISOString(),R=new Set;if(s.transaction(()=>{let{id:L}=d.get(n,u,c);for(let x of o.values()){if(Gc(x.firstUser)){console.log(`[watcher] skipping daemon-spawn phantom ${x.sessionId} (first message matches autonomous-spawn pattern)`),b.run(x.sessionId),S.run({id:x.sessionId,project_id:L,file_path:e,file_mtime:t,started_at:x.earliest,ended_at:x.latest,cwd:x.cwd,indexed_at:T}),R.add(x.sessionId);continue}let F=ho(x.firstUser),A=F.alias?F.stripped:x.firstUser;if(m.run({id:x.sessionId,project_id:L,file_path:e,file_mtime:t,started_at:x.earliest,ended_at:x.latest,message_count:x.entries.length,user_message_count:x.users,assistant_message_count:x.assistants,first_user_message:A,cwd:x.cwd,git_branch:x.branch,version:x.version,indexed_at:T}),F.alias&&!Te(x.sessionId))try{he(x.sessionId,F.alias)}catch($){console.error(`[watcher] header-alias setAlias failed for ${x.sessionId}:`,$)}b.run(x.sessionId);for(let $ of x.entries)h.run(pb($));$c(s,x.sessionId,x.entries),os(s,x.sessionId)}})(),st().heuristicEnabled)for(let L of o.values()){if(R.has(L.sessionId))continue;let x=ho(L.firstUser).stripped,F=yt(x);F&&Ee(L.sessionId,F,"heuristic")}}function jl(e){return Ol(e,()=>db(e))}function pb(e){let t=Se(e.contentText).redacted,n=Se(e.raw).redacted;return{uuid:e.uuid,session_id:e.sessionId,parent_uuid:e.parentUuid,type:e.type,role:e.role,timestamp:e.timestamp,is_sidechain:e.isSidechain?1:0,content_text:t,tool_names:e.toolNames.join(","),raw_json:n}}function Ll(e){let t=Eo.get(e);t?.timer&&clearTimeout(t.timer);let n={timer:null};n.timer=setTimeout(()=>{Eo.delete(e),jl(e).then(async()=>{ub(e),ss(e);try{let r=f().prepare("SELECT id FROM sessions WHERE file_path = ? AND skipped_reason IS NULL").all(e);for(let o of r){ec(o.id),Hc(o.id);try{yl(o.id)}catch(a){console.error("[watcher] auto-collections apply failed:",a)}}}catch(s){console.error("[watcher] semantic dispatch failed:",s)}}).catch(s=>{console.error(`[watcher] reindex failed for ${e}:`,s)})},lb),Eo.set(e,n)}function Ml(){let e=ib(Ut,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:t=>{if(t.endsWith(".jsonl"))return!1;try{if(bo(t).isDirectory())return!1}catch{}return!0}});return e.on("add",t=>t.endsWith(".jsonl")&&Ll(t)),e.on("change",t=>t.endsWith(".jsonl")&&Ll(t)),e.on("ready",()=>{console.log(`[watcher] ready, watching ${Ut}`)}),e.on("error",t=>{console.error("[watcher] chokidar error:",t)}),process.env.RECALL_WATCHER_DEBUG==="1"&&(e.on("add",t=>console.log(`[watcher.debug] add: ${t}`)),e.on("change",t=>console.log(`[watcher.debug] change: ${t}`)),e.on("unlink",t=>console.log(`[watcher.debug] unlink: ${t}`))),e}var mb=4;function*So(e){let t;try{t=ab(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=cb(e,n.name);n.isDirectory()?yield*So(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}async function To(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},n=f(),s=[],r=n.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1");for(let d of So(Ut)){if(t.scanned+=1,Il(d)){t.skipped+=1;continue}let m;try{m=bo(d).mtimeMs}catch{t.errors+=1;continue}let h=r.get(d);if(h&&h.file_mtime>=m){t.upToDate+=1;continue}s.push(d)}if(s.length===0)return t.durationMs=Date.now()-e,t;let o=s.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await jl(d),t.reindexed+=1}catch(m){t.errors+=1;let h=m instanceof Error?m.message:String(m);console.error(`[ingestion-sweep] failed for ${d}: ${h}`)}}},c=Math.min(mb,s.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}async function Dl(){try{let e=await To();e.reindexed>0&&console.log(`[safety-sweep] reindexed ${e.reindexed} file(s) the watcher missed (scanned=${e.scanned}, upToDate=${e.upToDate})`)}catch(e){console.error("[safety-sweep] failed:",e)}}import{execFileSync as Pl}from"node:child_process";function yo(e={}){let t=e.psOutput??gb(),n=e.isProcessAlive??_b,s=e.getParentCommand??fb,r=[];for(let o of t.split(`
1255
- `)){let a=o.trim();if(!a||!a.includes("dist/mcp/server.js")||hb(a))continue;let c=a.split(/\s+/);if(c.length<5)continue;let u=Number(c[0]),d=Number(c[1]),m=c[2],h=Number(c[3]);if(!Number.isFinite(u)||!Number.isFinite(d))continue;let b=d>1&&n(d);r.push({pid:u,ppid:d,parentAlive:b,etimeSeconds:Eb(m),pcpu:Number.isFinite(h)?h:0,orphan:!b,parentCommand:b?s(d):null})}return r}function gb(){try{return Pl("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}
1256
- `),""}}function _b(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function fb(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=Pl("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 hb(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=t[4]??"";return n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"}function Eb(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Fl(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Fl),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Fl(e){let t=Number(e);return Number.isFinite(t)?t:0}var bb=50,Sb=60;function wo(e){return e.pcpu>=bb&&e.etimeSeconds>=Sb}import{statSync as Tb}from"node:fs";function Ro(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var yb=6e4,wb=5*6e4,$l=1073741824,Rb=5368709120,kb=6e4,Ab=100;function ko(e,t="PASSIVE",n){let s=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=Nb(r),a={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-s};return n&&(t==="PASSIVE"&&a.log>=Ab&&a.moved===0?n(`[wal-maintenance] PASSIVE checkpoint blocked: log=${a.log} frames pending, moved=0 (readers holding snapshots)`):a.moved>0&&n(`[wal-maintenance] ${t} checkpoint: log=${a.log} moved=${a.moved} busy=${a.busy} (${a.durationMs}ms)`)),a}function Ul(e){let t=e.db,n=e.walPath,s=e.checkpointEveryMs??yb,r=e.sizeCheckEveryMs??wb,o=e.warnBytes??$l,a=e.errorBytes??Rb,c=e.forceRestartCooldownMs??kb,u=e.logger??(L=>{process.stderr.write(L+`
1257
- `)}),d=e.statFn??(L=>Tb(L)),m=e.describeSuspectsFn??xb,h=0,b=null,S=()=>{try{ko(t,"PASSIVE",u).moved>0&&(b=Date.now())}catch(L){u(`[wal-maintenance] PASSIVE checkpoint threw: ${Ro(L)}`)}},T=()=>{let L;try{L=d(n).size}catch(x){let F=x.code;if(F!=="ENOENT"){let A=x instanceof Error?x.message:String(x);u(`[wal-maintenance] WAL stat failed (${F??"unknown"}): ${A}`)}return}if(L>=a){u(`[wal-maintenance] ERROR: WAL ${ys(L)} exceeds error threshold ${ys(a)}`);let x=m();x&&u(`[wal-maintenance] ${x}`);let F=Date.now();if(F-h>=c){h=F;try{let A=ko(t,"RESTART",u);u(`[wal-maintenance] forced RESTART: moved=${A.moved} busy=${A.busy} log=${A.log} (${A.durationMs}ms)`),A.moved>0&&(b=Date.now())}catch(A){u(`[wal-maintenance] forced RESTART threw: ${Ro(A)}`)}}}else L>=o&&u(`[wal-maintenance] WARN: WAL ${ys(L)} exceeds warn threshold ${ys(o)}`)},R=setInterval(S,s),O=setInterval(T,r);return typeof R.unref=="function"&&R.unref(),typeof O.unref=="function"&&O.unref(),{stop:()=>{clearInterval(R),clearInterval(O)},checkpointNow:(L="PASSIVE")=>{let x=ko(t,L,u);return x.moved>0&&(b=Date.now()),x},walSizeBytes:()=>{try{return d(n).size}catch{return 0}},lastCheckpointAt:()=>b}}function xb(e={}){let n=(e.list??yo)();if(n.length===0)return null;let s=n.find(wo);if(s)return`pin suspect: pid ${s.pid} at ${s.pcpu.toFixed(0)}%cpu, elapsed ${s.etimeSeconds}s (parent ${s.parentCommand??s.ppid})`;let r=n.slice().sort((o,a)=>a.etimeSeconds-o.etimeSeconds)[0];return`pin suspect: pid ${r.pid}, elapsed ${r.etimeSeconds}s (parent ${r.parentCommand??r.ppid})`}function Nb(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:Ao(t.busy),log:Ao(t.log),checkpointed:Ao(t.checkpointed)}}function Ao(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}function ys(e){return e>=1e9?`${(e/1e9).toFixed(2)} GB`:e>=1e6?`${(e/1e6).toFixed(0)} MB`:e>=1e3?`${(e/1e3).toFixed(0)} KB`:`${e} B`}var Lb=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],hN=new RegExp(`^(${Lb.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var EN=5*6e4;function xo(){try{let e=Ob(W);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function Bl(e){let{projects:t,sessions:n,messages:s,port:r,version:o}=e;return`<!DOCTYPE html>
1338
+ `),P=new Date().toISOString(),G=new Set;if(s.transaction(()=>{let{id:v}=y.get(n,u,c,d,p,f,S);for(let F of o.values()){if(Hl(F.firstUser)){console.log(`[watcher] skipping daemon-spawn phantom ${F.sessionId} (first message matches autonomous-spawn pattern)`),k.run(F.sessionId),M.run({id:F.sessionId,project_id:v,file_path:e,file_mtime:t,started_at:F.earliest,ended_at:F.latest,cwd:F.cwd,indexed_at:P}),G.add(F.sessionId);continue}let U=ei(F.firstUser),L=U.alias?U.stripped:F.firstUser;if(b.run({id:F.sessionId,project_id:v,file_path:e,file_mtime:t,started_at:F.earliest,ended_at:F.latest,message_count:F.entries.length,user_message_count:F.users,assistant_message_count:F.assistants,first_user_message:L,cwd:F.cwd,git_branch:F.branch,version:F.version,indexed_at:P}),U.alias&&!ye(F.sessionId))try{he(F.sessionId,U.alias)}catch(i){console.error(`[watcher] header-alias setAlias failed for ${F.sessionId}:`,i)}k.run(F.sessionId);for(let i of F.entries)R.run(Ey(i));Il(s,F.sessionId,F.entries),Cs(s,F.sessionId)}})(),dt().heuristicEnabled)for(let v of o.values()){if(G.has(v.sessionId))continue;let F=ei(v.firstUser).stripped,U=It(F);U&&Ee(v.sessionId,U,"heuristic")}}function Xu(e){return wu(e,()=>hy(e)).finally(()=>{Hu(e)})}function Ey(e){let t=Se(e.contentText).redacted,n=Se(e.raw).redacted;return{uuid:e.uuid,session_id:e.sessionId,parent_uuid:e.parentUuid,type:e.type,role:e.role,timestamp:e.timestamp,is_sidechain:e.isSidechain?1:0,content_text:t,tool_names:e.toolNames.join(","),raw_json:n}}function Wu(e){let t=ai.get(e);t?.timer&&clearTimeout(t.timer);let n={timer:null};n.timer=setTimeout(()=>{ai.delete(e),Xu(e).then(async()=>{fy(e),Ns(e);try{let r=h().prepare("SELECT id FROM sessions WHERE file_path = ? AND skipped_reason IS NULL").all(e);for(let o of r){Gc(o.id),Dl(o.id);try{_u(o.id)}catch(a){console.error("[watcher] auto-collections apply failed:",a)}}}catch(s){console.error("[watcher] semantic dispatch failed:",s)}}).catch(s=>{console.error(`[watcher] reindex failed for ${e}:`,s)})},gy),ai.set(e,n)}function Ju(){let e=dy(nn,{depth:4,ignoreInitial:!0,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:200},ignored:t=>{if(t.endsWith(".jsonl"))return!1;try{if(Vs(t).isDirectory())return!1}catch{}return!0}});return e.on("add",t=>t.endsWith(".jsonl")&&Wu(t)),e.on("change",t=>t.endsWith(".jsonl")&&Wu(t)),e.on("ready",()=>{console.log(`[watcher] ready, watching ${nn}`)}),e.on("error",t=>{console.error("[watcher] chokidar error:",t)}),process.env.RECALL_WATCHER_DEBUG==="1"&&(e.on("add",t=>console.log(`[watcher.debug] add: ${t}`)),e.on("change",t=>console.log(`[watcher.debug] change: ${t}`)),e.on("unlink",t=>console.log(`[watcher.debug] unlink: ${t}`))),e}var by=4;function*li(e){let t;try{t=py(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=my(e,n.name);n.isDirectory()?yield*li(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}async function ui(){let e=Date.now(),t={scanned:0,reindexed:0,upToDate:0,skipped:0,errors:0,durationMs:0},n=h(),s=[],r=n.prepare("SELECT file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1");for(let d of li(nn)){if(t.scanned+=1,qu(d)){t.skipped+=1;continue}let p;try{p=Vs(d).mtimeMs}catch{t.errors+=1;continue}let f=r.get(d);if(f?.skipped_reason){t.skipped+=1;continue}if(f&&f.file_mtime>=p){t.upToDate+=1;continue}s.push(d)}if(s.length===0)return t.durationMs=Date.now()-e,t;let o=s.slice(),a=async()=>{for(;o.length>0;){let d=o.shift();if(!d)break;try{await Xu(d),t.reindexed+=1}catch(p){t.errors+=1;let f=p instanceof Error?p.message:String(p);try{_y(d,p)}catch(S){console.error(`[ingestion-sweep] marker write failed for ${d}:`,S)}console.error(`[ingestion-sweep] failed for ${d}: ${f}`)}}},c=Math.min(by,s.length),u=[];for(let d=0;d<c;d+=1)u.push(a());return await Promise.all(u),t.durationMs=Date.now()-e,t}async function Gu(){try{let e=await ui();e.reindexed>0&&console.log(`[safety-sweep] reindexed ${e.reindexed} file(s) the watcher missed (scanned=${e.scanned}, upToDate=${e.upToDate})`)}catch(e){console.error("[safety-sweep] failed:",e)}}import{execFileSync as zu}from"node:child_process";var Sy=["dist/mcp-server.js","dist/mcp/server.js"];function yy(e){for(let t of Sy)if(e.includes(t))return!0;return!1}function Pt(e={}){let t=e.psOutput??Ty(),n=e.isProcessAlive??wy,s=e.getParentCommand??Ry,r=[];for(let o of t.split(`
1339
+ `)){let a=o.trim();if(!a||!yy(a)||ky(a))continue;let c=a.split(/\s+/);if(c.length<5)continue;let u=Number(c[0]),d=Number(c[1]),p=c[2],f=c[3],S=c[4],y=0,b=0;if(/^\d+$/.test(f)&&(S.includes(".")||/^\d+$/.test(S))?(y=Number(f),b=Number(S)):b=Number(f),!Number.isFinite(u)||!Number.isFinite(d))continue;let k=d>1&&n(d);r.push({pid:u,ppid:d,parentAlive:k,etimeSeconds:Ay(p),pcpu:Number.isFinite(b)?b:0,rssKb:Number.isFinite(y)?y:0,orphan:!k,parentCommand:k?s(d):null})}return r}function Ty(){try{return zu("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -axo failed: ${t}
1340
+ `),""}}function wy(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function Ry(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=zu("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return n.length?n.slice(0,200):null}catch{return null}}function ky(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=[t[5]??"",t[4]??""];for(let s of n)if(s&&(s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"))return!0;return!1}function Ay(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Yu(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Yu),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Yu(e){let t=Number(e);return Number.isFinite(t)?t:0}var xy=50,Ny=60;function di(e){return e.pcpu>=xy&&e.etimeSeconds>=Ny}var Ku=4,Oy=1024*1024;function pi(e){let t=e??Pt(),n=t.length,s=t.reduce((d,p)=>d+(Number.isFinite(p.rssKb)&&p.rssKb>0?p.rssKb:0),0),r=n>Ku,o=s>Oy;if(!(r||o))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,message:null};let c=[];r&&c.push(`${n} MCP children (threshold ${Ku})`),o&&c.push(`${Cy(s)} aggregate RSS (threshold 1 GiB)`);let u=`Zombie MCP threshold breached: ${c.join(" + ")}. Each MCP child holds a SQLite read connection and can pin WAL checkpoints. Run \`recall mcp-prune --all\` to reap (note: pre-2026-05-23 builds may miss processes from stale install paths -- \`pkill -f mcp-server.js\` is the nuclear option).`;return{flagged:!0,severity:"high",count:n,aggregateRssKb:s,message:u}}function Cy(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}import{execFileSync as Ly}from"node:child_process";var vy=["dist/daemon/entrypoint.js"];function Iy(e){for(let t of vy)if(e.includes(t))return!0;return!1}function My(e){if(e.length<5)return!1;let t=[e[5]??"",e[4]??""];for(let n of t)if(n&&(n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"))return!0;return!1}function mi(e={}){let t=e.psOutput??jy(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
1341
+ `)){let o=r.trim();if(!o||!Iy(o))continue;let a=o.split(/\s+/);if(My(a)||a.length<5)continue;let c=Number(a[0]),u=Number(a[1]),d=a[2];!Number.isFinite(c)||!Number.isFinite(u)||n.has(c)||s.push({pid:c,ppid:u,etimeSeconds:Dy(d),etime:d,command:o})}return s.sort((r,o)=>r.etimeSeconds-o.etimeSeconds),s}function jy(){try{return Ly("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] ps -axo failed: ${t}
1342
+ `),""}}function Dy(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Vu(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Vu),o=0,a=0,c=0;return r.length===3?[o,a,c]=r:r.length===2?[a,c]=r:r.length===1&&(c=r[0]),t*86400+o*3600+a*60+c}function Vu(e){let t=Number(e);return Number.isFinite(t)?t:0}ne();import{existsSync as Qs,readFileSync as $L,writeFileSync as $t,unlinkSync as Wy}from"node:fs";import{basename as Zs,join as gi}from"node:path";import{randomBytes as Hy}from"node:crypto";ne();import{join as By}from"node:path";ne();import{appendFileSync as Py}from"node:fs";import{join as Fy}from"node:path";var $y=Fy(W,"daemon.log"),Uy="[state-files-audit]";function Ft(e,t){let s=(new Error().stack??"").split(`
1343
+ `).slice(2,4).map(a=>a.trim().replace(/file:\/\/.*?(\bdist\b|\bsrc\b)/g,"$1")).join(" << "),o=`${new Date().toISOString()} ${Uy} ${e}: ${t} | caller: ${s}
1344
+ `;try{Py($y,o)}catch{}}var Qu=By(W,"daemon.token");function Zu(){let e=Hy(32).toString("hex");return Ft("mint-token",`length=${e.length}`),e}var qy=gi(W,"daemon.pid"),Xy=gi(W,"daemon.port"),XL=gi(W,"daemon.log");function On(){return{pid:qy,port:Xy,token:Qu}}function ed(e,t={}){let n=t.paths??On();V(),$t(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),$t(n.port,String(e.port),{encoding:"utf8",mode:384}),$t(n.token,e.token,{encoding:"utf8",mode:384}),Ft("write-info-atomic",`pid=${e.pid} port=${e.port}`)}function fi(e={}){let t=e.paths??On(),n=[];for(let s of[t.pid,t.port,t.token])if(Qs(s))try{Wy(s),n.push(Zs(s))}catch{}return Ft("clear-force",`cleared: ${n.join(",")||"(none -- files already absent)"}`),{deleted:!0,reason:`cleared ${n.length} file(s)`,cleared:n}}function td(e){return Jy(e)}function Jy(e){try{return process.kill(e,0),!0}catch{return!1}}function nd(e,t={}){let n=t.paths??On(),s=[];return V(),Qs(n.pid)||($t(n.pid,JSON.stringify({pid:e.pid,port:e.port,startedAt:e.startedAt}),{encoding:"utf8",mode:384}),s.push(Zs(n.pid))),Qs(n.port)||($t(n.port,String(e.port),{encoding:"utf8",mode:384}),s.push(Zs(n.port))),Qs(n.token)||($t(n.token,e.token,{encoding:"utf8",mode:384}),s.push(Zs(n.token))),s.length>0&&Ft("heal",`restored: ${s.join(",")}`),{restored:s}}import{statSync as Gy}from"node:fs";function _i(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var Yy=6e4,Ky=5*6e4,sd=1073741824,zy=5368709120,Vy=6e4,Qy=100;function hi(e,t="PASSIVE",n){let s=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=eT(r),a={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-s};return n&&(t==="PASSIVE"&&a.log>=Qy&&a.moved===0?n(`[wal-maintenance] PASSIVE checkpoint blocked: log=${a.log} frames pending, moved=0 (readers holding snapshots)`):a.moved>0&&n(`[wal-maintenance] ${t} checkpoint: log=${a.log} moved=${a.moved} busy=${a.busy} (${a.durationMs}ms)`)),a}function rd(e){let t=e.db,n=e.walPath,s=e.checkpointEveryMs??Yy,r=e.sizeCheckEveryMs??Ky,o=e.warnBytes??sd,a=e.errorBytes??zy,c=e.forceRestartCooldownMs??Vy,u=e.logger??(M=>{process.stderr.write(M+`
1345
+ `)}),d=e.statFn??(M=>Gy(M)),p=e.describeSuspectsFn??Zy,f=0,S=null,y=()=>{try{hi(t,"PASSIVE",u).moved>0&&(S=Date.now())}catch(M){u(`[wal-maintenance] PASSIVE checkpoint threw: ${_i(M)}`)}},b=()=>{let M;try{M=d(n).size}catch(P){let G=P.code;if(G!=="ENOENT"){let D=P instanceof Error?P.message:String(P);u(`[wal-maintenance] WAL stat failed (${G??"unknown"}): ${D}`)}return}if(M>=a){u(`[wal-maintenance] ERROR: WAL ${er(M)} exceeds error threshold ${er(a)}`);let P=p();P&&u(`[wal-maintenance] ${P}`);let G=Date.now();if(G-f>=c){f=G;try{let D=hi(t,"RESTART",u);u(`[wal-maintenance] forced RESTART: moved=${D.moved} busy=${D.busy} log=${D.log} (${D.durationMs}ms)`),D.moved>0&&(S=Date.now())}catch(D){u(`[wal-maintenance] forced RESTART threw: ${_i(D)}`)}}}else M>=o&&u(`[wal-maintenance] WARN: WAL ${er(M)} exceeds warn threshold ${er(o)}`)},R=setInterval(y,s),k=setInterval(b,r);return typeof R.unref=="function"&&R.unref(),typeof k.unref=="function"&&k.unref(),{stop:()=>{clearInterval(R),clearInterval(k)},checkpointNow:(M="PASSIVE")=>{let P=hi(t,M,u);return P.moved>0&&(S=Date.now()),P},walSizeBytes:()=>{try{return d(n).size}catch{return 0}},lastCheckpointAt:()=>S}}function Zy(e={}){let n=(e.list??Pt)();if(n.length===0)return null;let s=n.find(di);if(s)return`pin suspect: pid ${s.pid} at ${s.pcpu.toFixed(0)}%cpu, elapsed ${s.etimeSeconds}s (parent ${s.parentCommand??s.ppid})`;let r=n.slice().sort((o,a)=>a.etimeSeconds-o.etimeSeconds)[0];return`pin suspect: pid ${r.pid}, elapsed ${r.etimeSeconds}s (parent ${r.parentCommand??r.ppid})`}function eT(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:Ei(t.busy),log:Ei(t.log),checkpointed:Ei(t.checkpointed)}}function Ei(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}function er(e){return e>=1e9?`${(e/1e9).toFixed(2)} GB`:e>=1e6?`${(e/1e6).toFixed(0)} MB`:e>=1e3?`${(e/1e3).toFixed(0)} KB`:`${e} B`}ne();q();import{existsSync as tT,readFileSync as nT,renameSync as id,writeFileSync as sT}from"node:fs";import{join as rT}from"node:path";var oT=24,iT=3600*1e3,aT=1e3,cT=1e3,lT=1e4,uT=5e3,dT=1440*60*1e3,od=1e4;function ad(){return rT(W,"doctor-state.json")}function cd(){let e=ad();if(!tT(e))return{chunkQueue:{samples:[]}};let t;try{t=nT(e,"utf8")}catch{return{chunkQueue:{samples:[]}}}try{let n=JSON.parse(t),s=n.chunkQueue?.samples,r=Array.isArray(s)?s.filter(u=>typeof u=="object"&&u!==null&&typeof u.ts=="string"&&typeof u.size=="number"&&typeof u.semanticEnabled=="boolean"):[],o=n.autoPruneCounters?.events,a=Array.isArray(o)?o.filter(u=>{if(!u||typeof u!="object")return!1;let d=u;return!(typeof d.ts!="string"||typeof d.pid!="number"||!Number.isFinite(d.pid)||d.action!=="would_kill"&&d.action!=="killed"&&d.action!=="failed"||d.reason!=="orphan_10min"&&d.reason!=="runaway_cpu_5min")}):[],c={chunkQueue:{samples:r}};return(a.length>0||o!==void 0)&&(c.autoPruneCounters={events:a}),c}catch{try{id(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function ld(e){let t=ad(),n=`${t}.tmp`;try{sT(n,JSON.stringify(e,null,2),{mode:384}),id(n,t)}catch{}}function bi(){let e=h(),t=0;try{t=e.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let n=!1;try{n=e.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}let s=cd(),r=Date.now(),o=s.chunkQueue.samples,a=o.map(b=>({s:b,ms:Date.parse(b.ts)})).filter(b=>Number.isFinite(b.ms)&&r-b.ms<=iT).sort((b,R)=>b.ms-R.ms),c=null;a.length>0&&(c=t-a[0].s.size);let u=a.length,p={chunkQueue:{samples:[...o,{ts:new Date(r).toISOString(),size:t,semanticEnabled:n}].slice(-oT)}};s.autoPruneCounters&&(p.autoPruneCounters=s.autoPruneCounters),ld(p);let f="ok",S=`chunk_queue growth: ok (current ${t.toLocaleString()} row${t===1?"":"s"}, semantic ${n?"on":"off"}, ${u} prior sample${u===1?"":"s"} in last hour).`,y=null;return t>aT&&!n&&c!==null&&c>cT?(f="critical",S=`chunk_queue growth: CRITICAL \u2014 ${t.toLocaleString()} rows with semantic disabled, grew by ${c.toLocaleString()} in the last hour. Schema-sync race shape.`,y="Schema-sync race likely \u2014 Phase 1.1 fix shipped on `feat/daemon-state-integrity`; if rows persist after a clean restart, re-investigate. Inspect with `sqlite3 ~/.recall/db.sqlite 'SELECT action, COUNT(*) FROM chunk_queue GROUP BY action;'`."):t>lT?(f="high",S=`chunk_queue growth: HIGH \u2014 ${t.toLocaleString()} rows pending (semantic ${n?"on":"off"}`+(c!==null?`, last-hour \u0394 ${c>=0?"+":""}${c.toLocaleString()}`:"")+").",y="Queue is large but stable \u2014 operator-authorized `recall semantic backfill` (or the daemon's embed worker, if intentionally on) would drain."):c!==null&&c>uT&&(f="medium",S=`chunk_queue growth: MEDIUM \u2014 grew by ${c.toLocaleString()} in the last hour (current ${t.toLocaleString()}, semantic ${n?"on":"off"}).`,y="Growth is fast even though the absolute size is small. Re-run `recall doctor` in 10\u201330 min; if growth continues without semantic on, investigate the schema-sync gate."),{status:f,currentSize:t,semanticEnabled:n,lastHourGrowth:c,priorSampleCount:u,message:S,remediation:y}}function ud(e,t=Date.now()){if(e.length===0)return;let n=cd(),r=[...n.autoPruneCounters?.events??[],...e],o=pT(r,t),a={chunkQueue:n.chunkQueue,autoPruneCounters:{events:o}};ld(a)}function pT(e,t){let n=e.filter(s=>{let r=Date.parse(s.ts);return Number.isFinite(r)&&t-r<=dT});return n.length>od?n.slice(-od):n}var dd="dry-run",mT=600,gT=95,fT=300,_T=5e3;function tr(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return dd;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:dd}async function gd(e={}){let t=e.mode??tr(),n=e.log??ST,s=e.now??Date.now,r={mode:t,candidates:[],wouldHaveKilled:[],killed:[],failed:[]};if(t==="off")return r;let o=e.list??Pt,a;try{a=o()}catch(p){return n(`[auto-prune] inventory scan failed: ${Ke(p)}`),r}for(let p of a){let f=hT(p);f!==null&&r.candidates.push({pid:p.pid,reason:f,proc:p})}if(r.candidates.length===0)return r;if(t==="dry-run"){for(let p of r.candidates)n(Si("WOULD kill",p.pid,p.reason,p.proc)),r.wouldHaveKilled.push({pid:p.pid,reason:p.reason});return md(r,s()),r}let c=e.kill??ET,u=e.sleep??bT,d=[];for(let p of r.candidates)try{c(p.pid,"SIGTERM"),d.push({pid:p.pid,reason:p.reason})}catch(f){f?.code==="ESRCH"?r.killed.push({pid:p.pid,reason:p.reason}):(n(`[auto-prune] kill failed pid=${p.pid} reason=${p.reason} error=${Ke(f)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ke(f)}))}await u(_T);for(let p of d){let f=!1;try{c(p.pid,0),f=!0}catch(S){S?.code==="ESRCH"?r.killed.push({pid:p.pid,reason:p.reason}):(n(`[auto-prune] liveness probe failed pid=${p.pid} reason=${p.reason} error=${Ke(S)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ke(S)}))}if(f)try{c(p.pid,"SIGKILL"),r.killed.push({pid:p.pid,reason:p.reason}),n(Si("killed",p.pid,p.reason,pd(r.candidates,p.pid)))}catch(S){n(`[auto-prune] SIGKILL failed pid=${p.pid} reason=${p.reason} error=${Ke(S)}`),r.failed.push({pid:p.pid,reason:p.reason,error:Ke(S)})}else n(Si("killed",p.pid,p.reason,pd(r.candidates,p.pid)))}return md(r,s()),r}function hT(e){return e.orphan&&e.etimeSeconds>mT?"orphan_10min":e.pcpu>gT&&e.etimeSeconds>fT?"runaway_cpu_5min":null}function pd(e,t){return e.find(s=>s.pid===t).proc}function Si(e,t,n,s){return`[auto-prune] ${e} pid=${t} reason=${n} etime=${s.etimeSeconds}s pcpu=${s.pcpu}% rssKb=${s.rssKb} ppid=${s.ppid} parentAlive=${s.parentAlive}`}function md(e,t){let n=new Date(t).toISOString(),s=[];for(let r of e.wouldHaveKilled)s.push({ts:n,pid:r.pid,action:"would_kill",reason:r.reason});for(let r of e.killed)s.push({ts:n,pid:r.pid,action:"killed",reason:r.reason});for(let r of e.failed)s.push({ts:n,pid:r.pid,action:"failed",reason:r.reason});if(s.length!==0)try{ud(s,t)}catch(r){process.stderr.write(`[auto-prune] counter persist failed: ${Ke(r)}
1346
+ `)}}function ET(e,t){process.kill(e,t)}function bT(e){return new Promise(t=>{setTimeout(t,e)})}function ST(e){process.stderr.write(e+`
1347
+ `)}function Ke(e){if(e instanceof Error){let t=e.code;return t?`${t} ${e.message}`:e.message}return String(e)}ne();import{existsSync as yT,readFileSync as TT,renameSync as _d,writeFileSync as wT}from"node:fs";import{createHash as RT}from"node:crypto";import{join as kT}from"node:path";function hd(){return kT(W,"doctor-alerts.json")}function AT(e,t){let n=Object.keys(t).sort(),s={};for(let o of n)s[o]=t[o];let r=RT("sha256");return r.update(e),r.update("\0"),r.update(JSON.stringify(s)),r.digest("hex")}function yi(){let e=hd();if(!yT(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=TT(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return fd(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return fd(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter(xT):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function fd(e){try{_d(e,`${e}.corrupt.${Date.now()}`)}catch{}}function xT(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&typeof t.check=="string"&&(t.severity==="critical"||t.severity==="high"||t.severity==="medium")&&typeof t.message=="string"&&typeof t.remediation=="string"&&typeof t.firstSeenAt=="string"&&typeof t.lastSeenAt=="string"&&typeof t.seenCount=="number"&&typeof t.acknowledged=="boolean"}function Ti(e){let t=hd(),n=`${t}.tmp`;try{wT(n,JSON.stringify(e,null,2),{mode:384}),_d(n,t)}catch{}}function Ed(e,t,n=new Date){let s=n.toISOString(),r=new Map;for(let o of e.alerts)r.set(o.id,o);for(let o of t){let a=AT(o.check,o.keyFacts),c=r.get(a);c?r.set(a,{...c,severity:o.severity,message:o.message,remediation:o.remediation,lastSeenAt:s,seenCount:c.seenCount+1}):r.set(a,{id:a,check:o.check,severity:o.severity,message:o.message,remediation:o.remediation,firstSeenAt:s,lastSeenAt:s,seenCount:1,acknowledged:!1})}return{version:1,lastTickAt:s,alerts:[...r.values()]}}var LT=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],Iv=new RegExp(`^(${LT.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);var Td=50;var Mv=5*6e4;function vT(e){return e==="db.sqlite"||e==="db.sqlite-wal"||e==="db.sqlite-shm"?!1:!!(e.startsWith("db.sqlite.")||e.endsWith(".bak")||e.includes(".bak."))}function wd(e=W){let t=0,n=0;try{let y=Sd(e);t=Number(y.bavail)*Number(y.bsize),n=Number(y.blocks)*Number(y.bsize)}catch{}let s=n>0?t/n*100:100,r=[];try{r=NT(e)}catch{r=[]}let o=0,a=0,c=[],u=Date.now(),d=720*60*60*1e3;for(let y of r){if(!vT(y))continue;let b;try{b=OT(yd(e,y))}catch{continue}if(!b.isFile())continue;a+=1,o+=b.size;let R=Math.max(0,u-b.mtimeMs),k=Math.floor(R/(1440*60*1e3));R>=d&&c.push({name:y,sizeBytes:b.size,ageDays:k})}c.sort((y,b)=>b.sizeBytes!==y.sizeBytes?b.sizeBytes-y.sizeBytes:b.ageDays-y.ageDays);let p=2*1024**3,f=5*1024**3,S="ok";return n>0&&s<10||o>f?S="high":n>0&&s<20||o>p?S="medium":a>0&&(S="low"),{freeBytes:t,totalBytes:n,freePercent:s,backupTotalBytes:o,backupFileCount:a,oldFiles:c,severity:S}}function Rd(e={}){if((e.liveDaemons??mi({excludePids:[process.pid]})).length===0)return{flagged:!1,severity:"ok",daemonAlive:!1,missing:[],message:null,remediation:null};let n=e.paths??On(),s=e.existsSync??wi,r=e.isProcessAlive??td,o=!1;if(s(n.pid))try{let u=JSON.parse(bd(n.pid,"utf8"));typeof u.pid=="number"&&r(u.pid)&&(o=!0)}catch{}if(!o)return{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null};let a=[];return s(n.port)||a.push("daemon.port"),s(n.token)||a.push("daemon.token"),a.length===0?{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null}:{flagged:!0,severity:"critical",daemonAlive:!0,missing:a,message:`Daemon process is alive (per pidfile) but companion state file${a.length===1?"":"s"} ${a.join(", ")} missing. Web UI and authenticated CLI calls will return 401 until the 30s heal tick restores them.`,remediation:"The 30-second heal tick re-creates these files automatically. To force immediate recovery: recall stop && recall start. Inspect ~/.recall/daemon.log for [state-files-audit] entries to identify the deletion source."}}function kd(e=yd(CT(),".claude.json")){let t={status:"ok",configPath:e,configExists:wi(e),findings:[]};if(!t.configExists)return t;let n;try{n=bd(e,"utf8")}catch{return t}let s;try{s=JSON.parse(n)}catch{return t}if(!s||typeof s!="object"||Array.isArray(s))return t;let r=s.mcpServers;if(!r||typeof r!="object"||Array.isArray(r))return t;for(let[o,a]of Object.entries(r)){if(!a||typeof a!="object")continue;let c=a.args;if(!Array.isArray(c)||c.length===0)continue;let u=c[0];if(typeof u!="string"||u.length===0||!u.startsWith("/")&&!u.startsWith("~")||wi(u))continue;let d=o==="recall";t.findings.push({name:o,stalePath:u,severity:d?"HIGH":"MEDIUM",remediation:d?"restart daemon (`recall stop && recall start`) \u2014 auto-repoint runs on boot now":"this is a third-party MCP \u2014 the vendor's package may have moved; reinstall their package"})}return t.findings.some(o=>o.severity==="HIGH")?t.status="fail":t.findings.length>0&&(t.status="warn"),t}function Ri(){try{let e=Sd(W);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}function Ad(e){let{projects:t,sessions:n,messages:s,port:r,version:o}=e;return`<!DOCTYPE html>
1258
1348
  <html lang="en">
1259
1349
  <head>
1260
1350
  <meta charset="utf-8" />
@@ -1309,32 +1399,32 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1309
1399
  </footer>
1310
1400
  </main>
1311
1401
  </body>
1312
- </html>`}var Cb=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,vb=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,Ib=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function jb(e){return e.replace(Cb,"").trim()}function Mb(e){let t=e.replace(vb,"[tool call]");return t=t.replace(Ib,"[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,`
1402
+ </html>`}var IT=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,MT=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,jT=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function DT(e){return e.replace(IT,"").trim()}function PT(e){let t=e.replace(MT,"[tool call]");return t=t.replace(jT,"[tool result]"),t=t.replace(/_\(unknown block: thinking\)_/g,""),t=t.replace(/(?:\[tool call\]|\[tool result\])(?:\s*(?:\[tool call\]|\[tool result\]))+/g,"[tool activity]"),t=t.replace(/\n{3,}/g,`
1313
1403
 
1314
- `),t.trim()}function Db(e){return e.role??e.type??"message"}function Hl(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,o=n.since?Date.parse(n.since):0,a=t.filter(m=>!(!r&&m.is_sidechain===1||o&&m.timestamp&&Date.parse(m.timestamp)<o)),c=[];n.prelude&&(c.push(n.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${s})`),c.push(""),c.push(`- **Project**: ${e.project_name}`),e.decoded_path&&c.push(`- **Path**: \`${e.decoded_path}\``),c.push(`- **Session ID**: \`${e.id}\``),e.started_at&&c.push(`- **Started**: ${e.started_at}`),e.ended_at&&c.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&c.push(`- **Branch**: \`${e.git_branch}\``),c.push(`- **Messages**: ${a.length}`),c.push(""),c.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),c.push(""),c.push("---"),c.push("");let u=0,d=0;for(let m of a){let h=m.content_text??"",b=jb(h);s==="condensed"&&(b=Mb(b));let S=b.length>0,T=!!m.tool_names&&m.tool_names.length>0;if(!S&&!T){d+=1;continue}let R=Db(m),O=m.timestamp?` \`${m.timestamp}\``:"";c.push(`## ${R}${O}`),c.push(""),T&&s==="condensed"&&(c.push(`_tools used: ${m.tool_names}_`),c.push("")),S&&(c.push(b),c.push("")),u+=1}return c.push("---"),c.push(""),c.push(`_${u} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),c.join(`
1315
- `)}function on(e){if(!e.sessionStartedAt)return{allowed:!1,reason:"missing-session-started-at"};if(!e.terminalOpenedAt)return{allowed:!1,reason:"missing-terminal-opened-at"};let t=Date.parse(e.sessionStartedAt),n=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(n)?n-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}B();ee();import{writeFileSync as Fb,mkdirSync as Pb,existsSync as $b}from"node:fs";import{join as ql}from"node:path";var No=ql(W,"notes"),Wl=200,Ub=12e3,Bb=800,Hb=8e3;function Xl(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Jl(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 Wb(){z(),$b(No)||Pb(No,{recursive:!0})}function qb(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:Xl(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:Jl(e.auto_synopsis_history)}}var Xb="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function ws(e){let t=f().prepare(`SELECT ${Xb} FROM session_notes WHERE session_id = ?`).get(e);return t?qb(t):null}function Gl(e,t){let n=f(),s=new Date().toISOString(),r=n.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=Xl(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:s})),n.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1404
+ `),t.trim()}function FT(e){return e.role??e.type??"message"}function xd(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,o=n.since?Date.parse(n.since):0,a=t.filter(p=>!(!r&&p.is_sidechain===1||o&&p.timestamp&&Date.parse(p.timestamp)<o)),c=[];n.prelude&&(c.push(n.prelude.trim()),c.push("")),c.push(`# Claude Recall, past session context (${s})`),c.push(""),c.push(`- **Project**: ${e.project_name}`),e.decoded_path&&c.push(`- **Path**: \`${e.decoded_path}\``),c.push(`- **Session ID**: \`${e.id}\``),e.started_at&&c.push(`- **Started**: ${e.started_at}`),e.ended_at&&c.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&c.push(`- **Branch**: \`${e.git_branch}\``),c.push(`- **Messages**: ${a.length}`),c.push(""),c.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),c.push(""),c.push("---"),c.push("");let u=0,d=0;for(let p of a){let f=p.content_text??"",S=DT(f);s==="condensed"&&(S=PT(S));let y=S.length>0,b=!!p.tool_names&&p.tool_names.length>0;if(!y&&!b){d+=1;continue}let R=FT(p),k=p.timestamp?` \`${p.timestamp}\``:"";c.push(`## ${R}${k}`),c.push(""),b&&s==="condensed"&&(c.push(`_tools used: ${p.tool_names}_`),c.push("")),y&&(c.push(S),c.push("")),u+=1}return c.push("---"),c.push(""),c.push(`_${u} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),c.join(`
1405
+ `)}function Cn(e){if(!e.sessionStartedAt)return{allowed:!1,reason:"missing-session-started-at"};if(!e.terminalOpenedAt)return{allowed:!1,reason:"missing-terminal-opened-at"};let t=Date.parse(e.sessionStartedAt),n=Date.parse(e.terminalOpenedAt);return Number.isFinite(t)?Number.isFinite(n)?n-t>6e4?{allowed:!1,reason:"terminal-postdates-session"}:{allowed:!0}:{allowed:!1,reason:"missing-terminal-opened-at"}:{allowed:!1,reason:"missing-session-started-at"}}q();ne();import{writeFileSync as $T,mkdirSync as UT,existsSync as HT}from"node:fs";import{join as Od}from"node:path";var ki=Od(W,"notes"),Nd=200,BT=12e3,WT=800,qT=8e3;function Cd(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Ld(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&typeof n.synopsis=="string"&&typeof n.replaced_at=="string"):[]}catch{return[]}}function XT(){V(),HT(ki)||UT(ki,{recursive:!0})}function JT(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:Cd(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:Ld(e.auto_synopsis_history)}}var GT="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function nr(e){let t=h().prepare(`SELECT ${GT} FROM session_notes WHERE session_id = ?`).get(e);return t?JT(t):null}function vd(e,t){let n=h(),s=new Date().toISOString(),r=n.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),o=[];return r&&(o=Cd(r.previous_versions),r.content!==t&&r.content.length>0&&o.push({content:r.content,replaced_at:s})),n.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
1316
1406
  VALUES (?, ?, ?, ?)
1317
1407
  ON CONFLICT(session_id) DO UPDATE SET
1318
1408
  content = excluded.content,
1319
1409
  updated_at = excluded.updated_at,
1320
- previous_versions = excluded.previous_versions`).run(e,t,s,JSON.stringify(o)),Gb(e,t,s),ws(e)??{session_id:e,content:t,updated_at:s,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function Yl(e){let n=f().prepare(`SELECT rowid AS rid, role, content_text
1410
+ previous_versions = excluded.previous_versions`).run(e,t,s,JSON.stringify(o)),KT(e,t,s),nr(e)??{session_id:e,content:t,updated_at:s,previous_versions:o,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}async function Id(e){let n=h().prepare(`SELECT rowid AS rid, role, content_text
1321
1411
  FROM messages
1322
1412
  WHERE session_id = ? AND is_sidechain = 0
1323
1413
  AND content_text IS NOT NULL AND content_text != ''
1324
1414
  AND role IN ('user', 'assistant')
1325
1415
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
1326
- LIMIT ?`).all(e,Wl);if(n.length===0)throw new Error("no messages available to summarise");let s=Ub,r=[];for(let b of n){if(s<=0)break;let S=(b.content_text??"").slice(0,Bb);r.push({rid:b.rid,role:b.role,content:S}),s-=S.length}r.reverse();let o=n.length===Wl||s<=0,a=r.map(b=>`**${b.role}**: ${b.content}`).join(`
1416
+ LIMIT ?`).all(e,Nd);if(n.length===0)throw new Error("no messages available to summarise");let s=BT,r=[];for(let S of n){if(s<=0)break;let y=(S.content_text??"").slice(0,WT);r.push({rid:S.rid,role:S.role,content:y}),s-=y.length}r.reverse();let o=n.length===Nd||s<=0,a=r.map(S=>`**${S.role}**: ${S.content}`).join(`
1327
1417
 
1328
1418
  `),c=["You will receive a sampled chronological transcript from a Claude Code session.",o?"The sample is the most recent slice that fits in the context budget; older messages were dropped.":"The full session is included.","","Write a clean markdown synopsis of the session. Use these sections \u2014 omit any that genuinely have nothing to say:","","## Goal","One sentence \u2014 what the user was trying to accomplish.","","## What was done","Bullet list \u2014 concrete actions, code changes, decisions. Skip pleasantries.","","## Key decisions","Bullet list \u2014 non-obvious choices and the reason behind them.","","## Files touched","Bullet list \u2014 file paths mentioned in the conversation. Omit the section if none.","","## Open follow-ups","Bullet list \u2014 anything left undone or flagged for later. Omit the section if none.","","Output ONLY the markdown \u2014 no surrounding prose, no code fences around the whole thing, no closing summary.","","---","",a].join(`
1329
- `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(ye(),et));if(!d())throw new Error("claude CLI not found on PATH");let m=await u(c,[],{});if(!m.success)throw new Error(`claude CLI exited ${m.exitCode}: ${m.stderr.slice(-500)}`);let h=Jb(m.stdout);if(!h)throw new Error("claude CLI returned an empty synopsis");return h.slice(0,Hb)}function Jb(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return s.trim()}}catch{}return t}function zl(e,t){let n=f(),s=new Date().toISOString(),r=Date.now(),o=n.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=Jl(o?.auto_synopsis_history??null);return o?.auto_synopsis&&o.auto_synopsis!==t&&o.auto_synopsis.length>0&&a.push({synopsis:o.auto_synopsis,replaced_at:s}),o?n.prepare(`UPDATE session_notes
1419
+ `),{spawnClaudePrompt:u,isClaudeCliAvailable:d}=await Promise.resolve().then(()=>(Te(),ct));if(!d())throw new Error("claude CLI not found on PATH");let p=await u(c,[],{});if(!p.success)throw new Error(`claude CLI exited ${p.exitCode}: ${p.stderr.slice(-500)}`);let f=YT(p.stdout);if(!f)throw new Error("claude CLI returned an empty synopsis");return f.slice(0,qT)}function YT(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return s.trim()}}catch{}return t}function Md(e,t){let n=h(),s=new Date().toISOString(),r=Date.now(),o=n.prepare("SELECT auto_synopsis, auto_synopsis_history, content, updated_at FROM session_notes WHERE session_id = ?").get(e),a=Ld(o?.auto_synopsis_history??null);return o?.auto_synopsis&&o.auto_synopsis!==t&&o.auto_synopsis.length>0&&a.push({synopsis:o.auto_synopsis,replaced_at:s}),o?n.prepare(`UPDATE session_notes
1330
1420
  SET auto_synopsis = ?,
1331
1421
  auto_synopsis_generated_at = ?,
1332
1422
  auto_synopsis_history = ?
1333
1423
  WHERE session_id = ?`).run(t,r,JSON.stringify(a),e):n.prepare(`INSERT INTO session_notes
1334
1424
  (session_id, content, updated_at, previous_versions, auto_synopsis,
1335
1425
  auto_synopsis_generated_at, auto_synopsis_history)
1336
- VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,s,t,r,JSON.stringify(a)),ws(e)}function Gb(e,t,n){try{Wb();let s=ql(No,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${n} -->
1337
- `;Fb(s,r+t)}catch(s){console.error("[notes] mirror write failed:",s)}}pt();B();ee();import{randomUUID as Kl}from"node:crypto";import{writeFileSync as Vl,readFileSync as LN,existsSync as Yb,mkdirSync as zb}from"node:fs";import{join as Oo}from"node:path";var Rs=Oo(W,"threads"),Kb=Oo(Rs,"index.json");function Zl(){z(),Yb(Rs)||zb(Rs,{recursive:!0})}function Lo(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 Ql(e){let t=new Map;if(e.length===0)return t;let n=f(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
1426
+ VALUES (?, '', ?, '[]', ?, ?, ?)`).run(e,s,t,r,JSON.stringify(a)),nr(e)}function KT(e,t,n){try{XT();let s=Od(ki,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${n} -->
1427
+ `;$T(s,r+t)}catch(s){console.error("[notes] mirror write failed:",s)}}wt();q();ne();import{randomUUID as jd}from"node:crypto";import{writeFileSync as Dd,readFileSync as Jv,existsSync as zT,mkdirSync as VT}from"node:fs";import{join as Ai}from"node:path";var sr=Ai(W,"threads"),QT=Ai(sr,"index.json");function Pd(){V(),zT(sr)||VT(sr,{recursive:!0})}function xi(e,t,n){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:n?.project??null,project_count:n?.project_count??0,folder_id:e.folder_id??null}}function Fd(e){let t=new Map;if(e.length===0)return t;let n=h(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
1338
1428
  p.name AS project,
1339
1429
  COUNT(*) AS n,
1340
1430
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
@@ -1342,7 +1432,7 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1342
1432
  LEFT JOIN sessions s ON s.id = te.session_id
1343
1433
  LEFT JOIN projects p ON p.id = s.project_id
1344
1434
  WHERE te.thread_id IN (${s})
1345
- GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let a of r){let c=o.get(a.thread_id);c||(c=[],o.set(a.thread_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(h=>h.project!==null),d=u.length,m=null;u.length>0&&(m=[...u].sort((b,S)=>S.n-b.n||S.origin_n-b.origin_n||(b.project??"").localeCompare(S.project??""))[0].project),t.set(a,{project:m,project_count:d})}return t}function eu(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 tu(e){let n=f().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1435
+ GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let a of r){let c=o.get(a.thread_id);c||(c=[],o.set(a.thread_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(f=>f.project!==null),d=u.length,p=null;u.length>0&&(p=[...u].sort((S,y)=>y.n-S.n||y.origin_n-S.origin_n||(S.project??"").localeCompare(y.project??""))[0].project),t.set(a,{project:p,project_count:d})}return t}function $d(e){let t=e.auto_title_source;return{thread_id:e.thread_id,session_id:e.session_id,parent_session_id:e.parent_session_id,role:e.role,confidence:e.confidence,source:e.source,added_at:e.added_at,alias:e.alias,auto_title:e.auto_title,auto_title_source:t==="agent"||t==="heuristic"?t:null,alias_source:e.alias?"manual":null,first_user_message:e.first_user_message,project:e.project}}function Ud(e){let n=h().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1346
1436
  s.auto_title AS auto_title,
1347
1437
  s.auto_title_source AS auto_title_source,
1348
1438
  s.first_user_message AS first_user_message,
@@ -1350,11 +1440,11 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1350
1440
  FROM (SELECT ? AS sid) q
1351
1441
  LEFT JOIN sessions s ON s.id = q.sid
1352
1442
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
1353
- 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 Co(e){let n=f().prepare(`SELECT
1443
+ LEFT JOIN projects p ON p.id = s.project_id`).get(e),s=n?.auto_title_source??null;return{alias:n?.alias??null,auto_title:n?.auto_title??null,auto_title_source:s==="agent"||s==="heuristic"?s:null,first_user_message:n?.first_user_message??null,project:n?.project??null}}function Ni(e){let n=h().prepare(`SELECT
1354
1444
  COUNT(*) AS session_count,
1355
1445
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
1356
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function ke(e){let t=ie(e);t&&(Zl(),Vl(Oo(Rs,`${e}.json`),JSON.stringify(t,null,2)),nu())}function nu(){Zl();let e=vo({includeArchived:!0});Vl(Kb,JSON.stringify({threads:e},null,2))}function ks(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=f(),s=Kl(),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)
1357
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),ke(s);let o=ie(s);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function vo(e={}){let t=f(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=Ql(s.map(o=>o.id));return s.map(o=>Lo(o,Co(o.id),r.get(o.id)))}function ie(e){let t=f(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
1446
+ FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function ke(e){let t=ce(e);t&&(Pd(),Dd(Ai(sr,`${e}.json`),JSON.stringify(t,null,2)),Hd())}function Hd(){Pd();let e=Oi({includeArchived:!0});Dd(QT,JSON.stringify({threads:e},null,2))}function rr(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=h(),s=jd(),r=new Date().toISOString();n.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(s,t,e.summary?.trim()||null,r),e.originSessionId&&n.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1447
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),ke(s);let o=ce(s);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function Oi(e={}){let t=h(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=Fd(s.map(o=>o.id));return s.map(o=>xi(o,Ni(o.id),r.get(o.id)))}function ce(e){let t=h(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
1358
1448
  NULLIF(sa.alias, '') AS alias,
1359
1449
  s.auto_title AS auto_title,
1360
1450
  s.auto_title_source AS auto_title_source,
@@ -1365,10 +1455,10 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1365
1455
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
1366
1456
  LEFT JOIN projects p ON p.id = s.project_id
1367
1457
  WHERE e.thread_id = ?
1368
- ORDER BY e.added_at ASC`).all(e).map(eu),r=Ql([e]).get(e);return{...Lo(n,Co(n.id),r),edges:s}}function su(e){return f().prepare(`SELECT t.* FROM threads t
1458
+ ORDER BY e.added_at ASC`).all(e).map($d),r=Fd([e]).get(e);return{...xi(n,Ni(n.id),r),edges:s}}function Bd(e){return h().prepare(`SELECT t.* FROM threads t
1369
1459
  JOIN thread_edges e ON e.thread_id = t.id
1370
1460
  WHERE e.session_id = ? AND t.archived = 0
1371
- ORDER BY t.created_at DESC`).all(e).map(s=>Lo(s,Co(s.id)))}function As(e){let t=f();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),a=e.confidence??1,c=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
1461
+ ORDER BY t.created_at DESC`).all(e).map(s=>xi(s,Ni(s.id)))}function or(e){let t=h();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),a=e.confidence??1,c=e.source??"manual";if(a<0||a>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
1372
1462
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1373
1463
  VALUES (?, ?, ?, ?, ?, ?, ?)
1374
1464
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -1376,33 +1466,33 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1376
1466
  role = excluded.role,
1377
1467
  confidence = excluded.confidence,
1378
1468
  source = excluded.source,
1379
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,s),ke(e.threadId);let u=tu(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:s,alias:u.alias,auto_title:u.auto_title,auto_title_source:u.auto_title_source,alias_source:u.alias?"manual":null,first_user_message:u.first_user_message,project:u.project}}function ru(e,t){let s=f().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&ke(e),{removed:s.changes}}function an(e,t,n){let s=f(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let c=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=n,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=n?"child":"origin";s.prepare(`UPDATE thread_edges
1469
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,a,c,s),ke(e.threadId);let u=Ud(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:a,source:c,added_at:s,alias:u.alias,auto_title:u.auto_title,auto_title_source:u.auto_title_source,alias_source:u.alias?"manual":null,first_user_message:u.first_user_message,project:u.project}}function Wd(e,t){let s=h().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&ke(e),{removed:s.changes}}function Ln(e,t,n){let s=h(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let c=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),u=n,d=new Set;for(;u!==null;){if(u===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(d.has(u))break;d.add(u),u=c.get(e,u)?.parent_session_id??null}}let o=n?"child":"origin";s.prepare(`UPDATE thread_edges
1380
1470
  SET parent_session_id = ?, role = ?, added_at = ?
1381
- WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),ke(e);let a=tu(t);return eu({...r,parent_session_id:n,role:o,added_at:new Date().toISOString(),alias:a.alias,auto_title:a.auto_title,auto_title_source:a.auto_title_source,first_user_message:a.first_user_message,project:a.project})}function ou(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");f().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),ke(e);let r=ie(e);if(!r)throw new Error(`thread ${e} not found`);return r}function iu(e){f().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),ke(e);let n=ie(e);if(!n)throw new Error(`thread ${e} not found`);return n}function au(e){f().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),ke(e);let n=ie(e);if(!n)throw new Error(`thread ${e} not found`);return n}function cu(e){f().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),ke(e);let n=ie(e);if(!n)throw new Error(`thread ${e} not found`);return n}function lu(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=f(),s=new Date().toISOString();n.transaction(()=>{let o=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)n.prepare(`INSERT INTO thread_edges
1471
+ WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),ke(e);let a=Ud(t);return $d({...r,parent_session_id:n,role:o,added_at:new Date().toISOString(),alias:a.alias,auto_title:a.auto_title,auto_title_source:a.auto_title_source,first_user_message:a.first_user_message,project:a.project})}function qd(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");h().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),ke(e);let r=ce(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Xd(e){h().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),ke(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Jd(e){h().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),ke(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Gd(e){h().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),ke(e);let n=ce(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Yd(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=h(),s=new Date().toISOString();n.transaction(()=>{let o=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let a of o)n.prepare(`INSERT INTO thread_edges
1382
1472
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1383
1473
  VALUES (?, ?, ?, ?, ?, ?, ?)
1384
1474
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
1385
1475
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
1386
1476
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
1387
1477
  confidence = MAX(thread_edges.confidence, excluded.confidence),
1388
- source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),ke(t),nu();let r=ie(t);if(!r)throw new Error("merge destination disappeared");return r}function uu(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=f(),n=new Date().toISOString(),s=Kl();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let o of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);a&&(t.prepare(`INSERT INTO thread_edges
1478
+ source = thread_edges.source`).run(t,a.session_id,a.parent_session_id,a.role,a.confidence,a.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),ke(t),Hd();let r=ce(t);if(!r)throw new Error("merge destination disappeared");return r}function Kd(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=h(),n=new Date().toISOString(),s=jd();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let o of e.sessionIds){let a=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);a&&(t.prepare(`INSERT INTO thread_edges
1389
1479
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1390
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,o,a.parent_session_id,a.role,a.confidence,a.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),ke(e.threadId),ke(s);let r=ie(s);if(!r)throw new Error("split destination disappeared");return r}B();import{execFile as dS}from"node:child_process";import{promisify as pS}from"node:util";import{readlink as mS,readFile as _u}from"node:fs/promises";import{platform as Cs}from"node:os";import{readFileSync as Vb,statSync as Zb}from"node:fs";var Qb=200*1024*1024,Ns=.7,Os=.5,mu=Os,eS=[{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"}],tS=["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 du(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 nS(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of eS)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function sS(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let o=r.toLowerCase();for(let a of tS)if(o.includes(a))return{weight:t[s],matched:a,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function Io(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 rS(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let o=Io(s,r);o>n&&(n=o)}return n}function oS(e,t){let n=Io(e.mean_embedding,t.mean_embedding),s=Io(e.tail_pool,t.head_pool),r=rS(e.sample_chunks,t.sample_chunks),o=0,a=null;if(n>o&&(o=n,a="mean"),s>o&&(o=s,a="asymmetric"),r>o&&(o=r,a="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:a};let c=(o-.65)/.2*.3;return{weight:Math.round(c*100)/100,cosine:o,mode:a}}function iS(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 aS(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 cS(e,t){let n=du(e),s=du(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function pu(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function lS(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1391
- `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){n+=.5,s=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=pu(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=pu(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function uS(e,t,n=mu){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=nS(s,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=sS(o),c=aS(e.touched_files,t.touched_files),u=cS(e.auto_title,t.auto_title),d=oS(e,t),m=iS(e,t),h=lS(e,t),b=r.weight+a.weight+c.weight+u.weight+d.weight+m.weight+h.weight;if(b<n)return null;let S=[];if(r.label&&S.push(`temporal ${r.label} (+${r.weight})`),a.matched){let T=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;S.push(`continuation phrase "${a.matched}" in ${T} (+${a.weight})`)}if(c.count>0&&S.push(`${c.count} file${c.count===1?"":"s"} overlap (+${c.weight.toFixed(1)})`),u.brand&&S.push(`shared brand "${u.brand}" (+${u.weight})`),d.weight>0&&d.mode&&S.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),m.same&&S.push(`same cluster (+${m.weight})`),h.weight>0){let T=[];h.pathMatch&&T.push(`opened authored path "${h.pathMatch.split("/").pop()}"`),h.contentMatch&&T.push("verbatim-paste of authored content"),S.push(`doc-authorship: ${T.join(" + ")} (+${h.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,b),signals:{temporal:r.weight,continuation:a.weight,file_overlap:c.weight,same_brand:u.weight,semantic:d.weight,cluster:m.weight,doc_authorship:h.weight},reasons:S}}function Rt(e,t=mu){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=null;for(let a=0;a<s;a++){let c=e[a],u=uS(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&n.push(o)}return n}function gu(e,t){let n=new Map,s=c=>{let u=c;for(;n.get(u)!==u;)u=n.get(u);let d=c;for(;n.get(d)!==u;){let m=n.get(d);n.set(d,u),d=m}return u},r=(c,u)=>{let d=s(c),m=s(u);d!==m&&n.set(d,m)};for(let c of e)n.has(c.parent_id)||n.set(c.parent_id,c.parent_id),n.has(c.child_id)||n.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of n.keys()){let u=s(c),d=o.get(u);d||(d=[],o.set(u,d)),d.push(c)}let a=new Map;for(let c of t)a.set(c.id,c.started_at_ms);return Array.from(o.values()).map(c=>(c.sort((u,d)=>(a.get(u)??0)-(a.get(d)??0)),{rootId:c[0],sessionIds:c}))}function Ls(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if(Zb(e).size>Qb)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=Vb(e,"utf8")}catch{return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}let d=0;for(;d<u.length;){let m=u.indexOf(`
1392
- `,d),h=m===-1?u.length:m,b=u.slice(d,h);if(d=m===-1?u.length:m+1,!b.trim())continue;let S;try{S=JSON.parse(b)}catch{continue}let T=S;if(T.type==="user"&&T.message?.role==="user"&&typeof T.message.content=="string"&&o.length<n){let O=T.message.content.trim();O&&o.push(O.length>s?O.slice(0,s):O)}let R=T.message?.content;if(Array.isArray(R))for(let O of R){if(!O||typeof O!="object")continue;let L=O;if(L.type!=="tool_use")continue;let x=L.input??{},F=typeof x.file_path=="string"?x.file_path:null;if(F){let A=xs(F);A&&r.add(A)}if((L.name==="Write"||L.name==="Edit"||L.name==="MultiEdit")&&F){let A=xs(F);A&&a.add(A);let $=typeof x.content=="string"?x.content:typeof x.new_string=="string"?x.new_string:null;$&&$.length>=200&&c.push($.length>4096?$.slice(0,4096):$)}if(L.name==="Bash"&&typeof x.command=="string")for(let A of x.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let $=xs(A[1]);$&&r.add($)}if((L.name==="Glob"||L.name==="Grep")&&typeof x.pattern=="string"){let A=xs(x.pattern);A&&!A.includes("*")&&r.add(A)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function xs(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Mo=pS(dS),gS=6,fu="Active ",hu=" sessions \u2014 ",_S=6e4;async function fS(){if(Cs()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Mo(t,["-eo","pid=,comm="],{timeout:2e3}),s=[];for(let r of n.split(`
1393
- `)){let o=r.trim().match(/^(\d+)\s+(.+)$/);if(!o)continue;let a=Number(o[1]),c=o[2].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&Number.isFinite(a)&&s.push(a)}return s}catch{continue}return[]}async function hS(e){let t=Cs();if(t==="linux")try{return(await mS(`/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 Mo(n,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of s.split(`
1394
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function ES(e){let t=Cs();if(t==="linux")try{let n=await _u(`/proc/${e}/stat`,"utf8"),s=n.lastIndexOf(")");if(s===-1)return null;let r=n.slice(s+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await _u("/proc/uptime","utf8"),c=Number(a.split(/\s+/)[0]);return Number.isFinite(c)?Date.now()-c*1e3+o/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Mo(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 bS(e,t){let n=await fS();if(n.length===0)return null;let s=e.replace(/\/+$/,""),r=[];for(let a of n){let c=await hS(a);if(c&&(c===s||c.startsWith(s+"/"))){let u=await ES(a);r.push({pid:a,startMs:u})}}if(r.length===0)return new Set;let o=new Set;for(let{startMs:a}of r){if(a==null)continue;let c=null,u=_S;for(let d of t){if(o.has(d.session_id)||!d.started_at)continue;let m=Date.parse(d.started_at);if(!Number.isFinite(m))continue;let h=Math.abs(m-a);h<u&&(u=h,c=d)}c&&o.add(c.session_id)}return o}function SS(e){let n=f().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 TS(e,t){let n=f(),s=`${fu}${t}${hu}%`,r=n.prepare(`SELECT t.id
1480
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,o,a.parent_session_id,a.role,a.confidence,a.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),ke(e.threadId),ke(s);let r=ce(s);if(!r)throw new Error("split destination disappeared");return r}q();import{execFile as mw}from"node:child_process";import{promisify as gw}from"node:util";import{readlink as fw,readFile as ep}from"node:fs/promises";import{platform as ur}from"node:os";import{readFileSync as ZT,statSync as ew}from"node:fs";var tw=200*1024*1024,ar=.7,cr=.5,Qd=cr,nw=[{maxGapMs:3600*1e3,weight:.7,label:"<1h gap"},{maxGapMs:14400*1e3,weight:.4,label:"<4h gap"},{maxGapMs:1440*60*1e3,weight:.2,label:"<24h gap"}],sw=["let's commit","lets commit","now commit","inspect the diff","inspect this diff","review the diff","review this diff","diff of all changes","diff of changes","based on what we just did","based on the things done","based on all the things","continue from","continuing from","pick up where","next step","now fix","now lets","now let's","from the previous session","from our last session","from the last session"];function zd(e){if(!e)return null;if(e.startsWith("/")){let n=e.split(" \xB7 ");if(n.length>1)return n[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function rw(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of nw)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function ow(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let o=r.toLowerCase();for(let a of sw)if(o.includes(a))return{weight:t[s],matched:a,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function Ci(e,t){if(!e||!t||e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function iw(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let o=Ci(s,r);o>n&&(n=o)}return n}function aw(e,t){let n=Ci(e.mean_embedding,t.mean_embedding),s=Ci(e.tail_pool,t.head_pool),r=iw(e.sample_chunks,t.sample_chunks),o=0,a=null;if(n>o&&(o=n,a="mean"),s>o&&(o=s,a="asymmetric"),r>o&&(o=r,a="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:a};let c=(o-.65)/.2*.3;return{weight:Math.round(c*100)/100,cosine:o,mode:a}}function cw(e,t){return e.cluster_id===null||t.cluster_id===null?{weight:0,same:!1}:e.cluster_id!==t.cluster_id?{weight:0,same:!1}:{weight:.05,same:!0}}function lw(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let n=0;for(let r of t)e.has(r)&&n++;return n===0?{weight:0,count:0}:{weight:Math.min(.4,n*.1),count:n}}function uw(e,t){let n=zd(e),s=zd(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function Vd(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function dw(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1481
+ `).toLowerCase();for(let a of e.authored_paths){let c=a.toLowerCase();if(t.touched_files.has(a)||o.includes(c)){n+=.5,s=a;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=Vd(t.recent_user_messages[0]);if(o.length>=200)for(let a of e.authored_content){let c=Vd(a);if(c.length<200)continue;let u=Math.min(c.length,240),d=c.slice(0,u);if(o.includes(d)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function pw(e,t,n=Qd){if(t.started_at_ms<=e.started_at_ms)return null;let s=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<s)return null;let r=rw(s,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],a=ow(o),c=lw(e.touched_files,t.touched_files),u=uw(e.auto_title,t.auto_title),d=aw(e,t),p=cw(e,t),f=dw(e,t),S=r.weight+a.weight+c.weight+u.weight+d.weight+p.weight+f.weight;if(S<n)return null;let y=[];if(r.label&&y.push(`temporal ${r.label} (+${r.weight})`),a.matched){let b=a.matchedIndex===0?"opening message":`message #${a.matchedIndex+1}`;y.push(`continuation phrase "${a.matched}" in ${b} (+${a.weight})`)}if(c.count>0&&y.push(`${c.count} file${c.count===1?"":"s"} overlap (+${c.weight.toFixed(1)})`),u.brand&&y.push(`shared brand "${u.brand}" (+${u.weight})`),d.weight>0&&d.mode&&y.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),p.same&&y.push(`same cluster (+${p.weight})`),f.weight>0){let b=[];f.pathMatch&&b.push(`opened authored path "${f.pathMatch.split("/").pop()}"`),f.contentMatch&&b.push("verbatim-paste of authored content"),y.push(`doc-authorship: ${b.join(" + ")} (+${f.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,S),signals:{temporal:r.weight,continuation:a.weight,file_overlap:c.weight,same_brand:u.weight,semantic:d.weight,cluster:p.weight,doc_authorship:f.weight},reasons:y}}function Ut(e,t=Qd){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=null;for(let a=0;a<s;a++){let c=e[a],u=pw(c,r,t);u&&(!o||u.confidence>o.confidence)&&(o=u)}o&&n.push(o)}return n}function Zd(e,t){let n=new Map,s=c=>{let u=c;for(;n.get(u)!==u;)u=n.get(u);let d=c;for(;n.get(d)!==u;){let p=n.get(d);n.set(d,u),d=p}return u},r=(c,u)=>{let d=s(c),p=s(u);d!==p&&n.set(d,p)};for(let c of e)n.has(c.parent_id)||n.set(c.parent_id,c.parent_id),n.has(c.child_id)||n.set(c.child_id,c.child_id),r(c.parent_id,c.child_id);let o=new Map;for(let c of n.keys()){let u=s(c),d=o.get(u);d||(d=[],o.set(u,d)),d.push(c)}let a=new Map;for(let c of t)a.set(c.id,c.started_at_ms);return Array.from(o.values()).map(c=>(c.sort((u,d)=>(a.get(u)??0)-(a.get(d)??0)),{rootId:c[0],sessionIds:c}))}function lr(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,o=[],a=new Set,c=[],u;try{if(ew(e).size>tw)return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c};u=ZT(e,"utf8")}catch{return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}let d=0;for(;d<u.length;){let p=u.indexOf(`
1482
+ `,d),f=p===-1?u.length:p,S=u.slice(d,f);if(d=p===-1?u.length:p+1,!S.trim())continue;let y;try{y=JSON.parse(S)}catch{continue}let b=y;if(b.type==="user"&&b.message?.role==="user"&&typeof b.message.content=="string"&&o.length<n){let k=b.message.content.trim();k&&o.push(k.length>s?k.slice(0,s):k)}let R=b.message?.content;if(Array.isArray(R))for(let k of R){if(!k||typeof k!="object")continue;let M=k;if(M.type!=="tool_use")continue;let P=M.input??{},G=typeof P.file_path=="string"?P.file_path:null;if(G){let D=ir(G);D&&r.add(D)}if((M.name==="Write"||M.name==="Edit"||M.name==="MultiEdit")&&G){let D=ir(G);D&&a.add(D);let v=typeof P.content=="string"?P.content:typeof P.new_string=="string"?P.new_string:null;v&&v.length>=200&&c.push(v.length>4096?v.slice(0,4096):v)}if(M.name==="Bash"&&typeof P.command=="string")for(let D of P.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let v=ir(D[1]);v&&r.add(v)}if((M.name==="Glob"||M.name==="Grep")&&typeof P.pattern=="string"){let D=ir(P.pattern);D&&!D.includes("*")&&r.add(D)}}}return{touched_files:r,recent_user_messages:o,authored_paths:a,authored_content:c}}function ir(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var vi=gw(mw),_w=6,tp="Active ",np=" sessions \u2014 ",hw=6e4;async function Ew(){if(ur()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await vi(t,["-eo","pid=,comm="],{timeout:2e3}),s=[];for(let r of n.split(`
1483
+ `)){let o=r.trim().match(/^(\d+)\s+(.+)$/);if(!o)continue;let a=Number(o[1]),c=o[2].trim();(c==="claude"||c.endsWith("/claude")||c.endsWith("/bin/claude"))&&Number.isFinite(a)&&s.push(a)}return s}catch{continue}return[]}async function bw(e){let t=ur();if(t==="linux")try{return(await fw(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:s}=await vi(n,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of s.split(`
1484
+ `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Sw(e){let t=ur();if(t==="linux")try{let n=await ep(`/proc/${e}/stat`,"utf8"),s=n.lastIndexOf(")");if(s===-1)return null;let r=n.slice(s+1).trim().split(/\s+/),o=Number(r[19]);if(!Number.isFinite(o))return null;let a=await ep("/proc/uptime","utf8"),c=Number(a.split(/\s+/)[0]);return Number.isFinite(c)?Date.now()-c*1e3+o/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let n of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await vi(n,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(s.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function yw(e,t){let n=await Ew();if(n.length===0)return null;let s=e.replace(/\/+$/,""),r=[];for(let a of n){let c=await bw(a);if(c&&(c===s||c.startsWith(s+"/"))){let u=await Sw(a);r.push({pid:a,startMs:u})}}if(r.length===0)return new Set;let o=new Set;for(let{startMs:a}of r){if(a==null)continue;let c=null,u=hw;for(let d of t){if(o.has(d.session_id)||!d.started_at)continue;let p=Date.parse(d.started_at);if(!Number.isFinite(p))continue;let f=Math.abs(p-a);f<u&&(u=f,c=d)}c&&o.add(c.session_id)}return o}function Tw(e){let n=h().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!n)throw new Error(`project ${e} not found`);return n}function ww(e,t){let n=h(),s=`${tp}${t}${np}%`,r=n.prepare(`SELECT t.id
1395
1485
  FROM threads t
1396
1486
  WHERE t.archived = 0
1397
1487
  AND t.name LIKE ?
1398
- ORDER BY t.created_at DESC`).all(s);for(let a of r){let c=ie(a.id);if(c&&c.project===t)return c}let o=n.prepare(`SELECT DISTINCT te.thread_id AS id
1488
+ ORDER BY t.created_at DESC`).all(s);for(let a of r){let c=ce(a.id);if(c&&c.project===t)return c}let o=n.prepare(`SELECT DISTINCT te.thread_id AS id
1399
1489
  FROM thread_edges te
1400
1490
  JOIN sessions s ON s.id = te.session_id
1401
1491
  JOIN threads t ON t.id = te.thread_id
1402
1492
  WHERE s.project_id = ?
1403
1493
  AND t.archived = 0
1404
1494
  AND t.name LIKE ?
1405
- LIMIT 1`).get(e,s);return o?ie(o.id):null}function Eu(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 jo(e,t){let n=f(),s=t>0,r=s?Date.now()-t*60*60*1e3:0;return s?n.prepare(`SELECT s.id AS session_id,
1495
+ LIMIT 1`).get(e,s);return o?ce(o.id):null}function sp(e){let t=e?new Date(e):new Date,n=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${s}-${r}`}function Li(e,t){let n=h(),s=t>0,r=s?Date.now()-t*60*60*1e3:0;return s?n.prepare(`SELECT s.id AS session_id,
1406
1496
  sa.alias AS alias,
1407
1497
  s.auto_title AS auto_title,
1408
1498
  s.first_user_message AS first_user_message,
@@ -1422,19 +1512,19 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1422
1512
  FROM sessions s
1423
1513
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1424
1514
  WHERE s.project_id = ?
1425
- ORDER BY s.started_at ASC`).all(e)}function yS(e){let t=f(),n=[];for(let s of e){if(!s.started_at)continue;let r=Date.parse(s.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(s.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=Ls(o.file_path,{maxUserMessages:7});n.push({id:s.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:s.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:s.auto_title,touched_files:c.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:c.authored_paths,authored_content:c.authored_content})}return n}function wS(e){let n=f().prepare(`SELECT session_id, parent_session_id, source
1515
+ ORDER BY s.started_at ASC`).all(e)}function Rw(e){let t=h(),n=[];for(let s of e){if(!s.started_at)continue;let r=Date.parse(s.started_at);if(!Number.isFinite(r))continue;let o=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(s.session_id);if(!o?.file_path)continue;let a=o.ended_at?Date.parse(o.ended_at):null,c=lr(o.file_path,{maxUserMessages:7});n.push({id:s.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(a)?a:null,first_user_message:s.first_user_message,recent_user_messages:c.recent_user_messages,auto_title:s.auto_title,touched_files:c.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:c.authored_paths,authored_content:c.authored_content})}return n}function kw(e){let n=h().prepare(`SELECT session_id, parent_session_id, source
1426
1516
  FROM thread_edges
1427
- 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 bu(e,t={}){let n=SS(e),s=t.windowHours??gS,r=t.scoreThreshold??Os,o=t.useLivePids??!0,a=[],c=[];if(o&&n.decoded_path){let R=jo(e,0),O=await bS(n.decoded_path,R);if(O===null){let x=Cs()==="win32"?"Windows live-PID detection is not yet supported \u2014 falling back to the rolling mtime window.":"No live `claude` processes detected \u2014 falling back to the rolling mtime window. Output may include sessions that are no longer open.";a.push(x),c=jo(e,s)}else O.size===0?(a.push(`No active terminals open in ${n.name} (cwd=${n.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=R.filter(L=>O.has(L.session_id))}else c=jo(e,s);c.length===0&&!a.length&&a.push(`No active sessions in ${n.name} within the last ${s}h.`);let u=TS(e,n.name),d=new Set(u?u.edges.map(R=>R.session_id):[]),m=c.filter(R=>!d.has(R.session_id)),h=yS(c);h.sort((R,O)=>R.started_at_ms-O.started_at_ms);let b=Rt(h,r),S=u?u.edges.filter(R=>R.source!=="auto-active"&&(R.parent_session_id||R.role==="origin")).map(R=>({session_id:R.session_id,parent_session_id:R.parent_session_id})):[],T=u?u.name:`${fu}${n.name}${hu}${Eu(t.todayIso)}`;return{project:n,thread:{id:u?.id??null,name:T,exists:!!u,existing_session_count:u?.edges.length??0},candidates:c,proposed_additions:m,proposed_edges:b,preserved_manual_edges:S,warnings:a}}function Su(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=ks({name:e.thread.name,summary:`Auto-captured by sync-active on ${Eu()}. 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=wS(n);for(let a of e.candidates)s.has(a.session_id)||(As({threadId:n,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,s.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=s.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&s.has(a.parent_id))try{an(n,a.child_id,a.parent_id),t.edges_set++,s.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=f().prepare(`SELECT session_id FROM thread_edges
1517
+ WHERE thread_id = ?`).all(e),s=new Map;for(let r of n)s.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return s}async function rp(e,t={}){let n=Tw(e),s=t.windowHours??_w,r=t.scoreThreshold??cr,o=t.useLivePids??!0,a=[],c=[];if(o&&n.decoded_path){let R=Li(e,0),k=await yw(n.decoded_path,R);if(k===null){let P=ur()==="win32"?"Windows live-PID detection is not yet supported \u2014 falling back to the rolling mtime window.":"No live `claude` processes detected \u2014 falling back to the rolling mtime window. Output may include sessions that are no longer open.";a.push(P),c=Li(e,s)}else k.size===0?(a.push(`No active terminals open in ${n.name} (cwd=${n.decoded_path}). Open a Claude terminal in this repo and re-run.`),c=[]):c=R.filter(M=>k.has(M.session_id))}else c=Li(e,s);c.length===0&&!a.length&&a.push(`No active sessions in ${n.name} within the last ${s}h.`);let u=ww(e,n.name),d=new Set(u?u.edges.map(R=>R.session_id):[]),p=c.filter(R=>!d.has(R.session_id)),f=Rw(c);f.sort((R,k)=>R.started_at_ms-k.started_at_ms);let S=Ut(f,r),y=u?u.edges.filter(R=>R.source!=="auto-active"&&(R.parent_session_id||R.role==="origin")).map(R=>({session_id:R.session_id,parent_session_id:R.parent_session_id})):[],b=u?u.name:`${tp}${n.name}${np}${sp(t.todayIso)}`;return{project:n,thread:{id:u?.id??null,name:b,exists:!!u,existing_session_count:u?.edges.length??0},candidates:c,proposed_additions:p,proposed_edges:S,preserved_manual_edges:y,warnings:a}}function op(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},n;e.thread.exists&&e.thread.id?n=e.thread.id:n=rr({name:e.thread.name,summary:`Auto-captured by sync-active on ${sp()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=n;let s=kw(n);for(let a of e.candidates)s.has(a.session_id)||(or({threadId:n,sessionId:a.session_id,source:"auto-active",confidence:.5}),t.added++,s.set(a.session_id,{parent_session_id:null,source:"auto-active"}));for(let a of e.proposed_edges){let c=s.get(a.child_id);if(c&&c.source==="auto-active"&&c.parent_session_id!==a.parent_id&&s.has(a.parent_id))try{Ln(n,a.child_id,a.parent_id),t.edges_set++,s.set(a.child_id,{parent_session_id:a.parent_id,source:c.source})}catch{}}let o=h().prepare(`SELECT session_id FROM thread_edges
1428
1518
  WHERE thread_id = ?
1429
1519
  AND source = 'auto-active'
1430
1520
  AND parent_session_id IS NULL
1431
- AND role = 'child'`).all(n);for(let a of o)try{an(n,a.session_id,null)}catch{}return t}B();ee();import{randomUUID as RS}from"node:crypto";import{writeFileSync as kS}from"node:fs";import{join as AS}from"node:path";var xS=AS(W,"thread-folders.json");function Tu(e){return{id:e.id,name:e.name,parent_folder_id:e.parent_folder_id,project_scope:e.project_scope,created_at:e.created_at,archived:e.archived===1,sort_order:e.sort_order}}function cn(){try{z();let e=Do({includeArchived:!0});kS(xS,JSON.stringify({folders:e},null,2))}catch{}}function Do(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return f().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(Tu)}function ot(e){let t=f().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?Tu(t):null}function yu(e){let t=e.name.trim();if(!t)throw new Error("folder name cannot be empty");if(t.length>200)throw new Error("folder name too long (200 char max)");let n=e.parentFolderId??null,s=e.projectScope??null;if(n){let c=ot(n);if(!c)throw new Error(`parent folder ${n} not found`);s=c.project_scope}let r=RS(),o=new Date().toISOString(),a=wu(n,s);return f().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1432
- VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,n,s,o,a),cn(),{id:r,name:t,parent_folder_id:n,project_scope:s,created_at:o,archived:!1,sort_order:a}}function wu(e,t){return f().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1521
+ AND role = 'child'`).all(n);for(let a of o)try{Ln(n,a.session_id,null)}catch{}return t}q();ne();import{randomUUID as Aw}from"node:crypto";import{writeFileSync as xw}from"node:fs";import{join as Nw}from"node:path";var Ow=Nw(W,"thread-folders.json");function ip(e){return{id:e.id,name:e.name,parent_folder_id:e.parent_folder_id,project_scope:e.project_scope,created_at:e.created_at,archived:e.archived===1,sort_order:e.sort_order}}function vn(){try{V();let e=Ii({includeArchived:!0});xw(Ow,JSON.stringify({folders:e},null,2))}catch{}}function Ii(e={}){let t=e.includeArchived?"":"WHERE archived = 0";return h().prepare(`SELECT * FROM thread_folders ${t} ORDER BY sort_order, name`).all().map(ip)}function gt(e){let t=h().prepare("SELECT * FROM thread_folders WHERE id = ?").get(e);return t?ip(t):null}function ap(e){let t=e.name.trim();if(!t)throw new Error("folder name cannot be empty");if(t.length>200)throw new Error("folder name too long (200 char max)");let n=e.parentFolderId??null,s=e.projectScope??null;if(n){let c=gt(n);if(!c)throw new Error(`parent folder ${n} not found`);s=c.project_scope}let r=Aw(),o=new Date().toISOString(),a=cp(n,s);return h().prepare(`INSERT INTO thread_folders (id, name, parent_folder_id, project_scope, created_at, archived, sort_order)
1522
+ VALUES (?, ?, ?, ?, ?, 0, ?)`).run(r,t,n,s,o,a),vn(),{id:r,name:t,parent_folder_id:n,project_scope:s,created_at:o,archived:!1,sort_order:a}}function cp(e,t){return h().prepare(`SELECT COALESCE(MAX(sort_order), -100) + 100 AS next
1433
1523
  FROM thread_folders
1434
1524
  WHERE parent_folder_id IS ?
1435
- AND project_scope IS ?`).get(e,t)?.next??0}function Ru(e,t){let n=t.trim();if(!n)throw new Error("folder name cannot be empty");if(n.length>200)throw new Error("folder name too long (200 char max)");let s=ot(e);if(!s)throw new Error(`folder ${e} not found`);return f().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(n,e),cn(),{...s,name:n}}function ku(e,t){let n=ot(e);if(!n)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let s=n.project_scope;if(t!==null){let o=ot(t);if(!o)throw new Error(`parent folder ${t} not found`);let a=o.parent_folder_id,c=0;for(;a!==null&&c<1024;){if(a===e)throw new Error("cannot move a folder into one of its own descendants (cycle)");let u=ot(a);if(!u)break;a=u.parent_folder_id,c++}s=o.project_scope}let r=wu(t,s);return f().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,s,r,e),cn(),{...n,parent_folder_id:t,project_scope:s,sort_order:r}}function Au(e,t,n){if(!Array.isArray(n)||n.length===0)throw new Error("ordered_ids must be a non-empty array");let s=new Set;for(let d of n){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(s.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);s.add(d)}let r=f(),o=r.prepare(`SELECT id FROM thread_folders
1525
+ AND project_scope IS ?`).get(e,t)?.next??0}function lp(e,t){let n=t.trim();if(!n)throw new Error("folder name cannot be empty");if(n.length>200)throw new Error("folder name too long (200 char max)");let s=gt(e);if(!s)throw new Error(`folder ${e} not found`);return h().prepare("UPDATE thread_folders SET name = ? WHERE id = ?").run(n,e),vn(),{...s,name:n}}function up(e,t){let n=gt(e);if(!n)throw new Error(`folder ${e} not found`);if(t===e)throw new Error("cannot move a folder under itself");let s=n.project_scope;if(t!==null){let o=gt(t);if(!o)throw new Error(`parent folder ${t} not found`);let a=o.parent_folder_id,c=0;for(;a!==null&&c<1024;){if(a===e)throw new Error("cannot move a folder into one of its own descendants (cycle)");let u=gt(a);if(!u)break;a=u.parent_folder_id,c++}s=o.project_scope}let r=cp(t,s);return h().prepare("UPDATE thread_folders SET parent_folder_id = ?, project_scope = ?, sort_order = ? WHERE id = ?").run(t,s,r,e),vn(),{...n,parent_folder_id:t,project_scope:s,sort_order:r}}function dp(e,t,n){if(!Array.isArray(n)||n.length===0)throw new Error("ordered_ids must be a non-empty array");let s=new Set;for(let d of n){if(typeof d!="string"||!d)throw new Error("ordered_ids must contain non-empty strings");if(s.has(d))throw new Error(`duplicate id in ordered_ids: ${d}`);s.add(d)}let r=h(),o=r.prepare(`SELECT id FROM thread_folders
1436
1526
  WHERE parent_folder_id IS ?
1437
- AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==n.length)throw new Error(`ordered_ids length ${n.length} does not match sibling count ${a.size}`);for(let d of n)if(!a.has(d))throw new Error(`folder ${d} is not in the named sibling bucket`);let c=r.prepare("UPDATE thread_folders SET sort_order = ? WHERE id = ?");r.transaction(d=>{d.forEach((m,h)=>c.run(h*100,m))})(n),cn()}function xu(e){if(!ot(e))throw new Error(`folder ${e} not found`);f().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),cn()}function Nu(e,t){if(t!==null&&!ot(t))throw new Error(`folder ${t} not found`);if(f().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function Ou(e,t){let n=new Set,s=[];for(let r of t){if(n.has(r.id))continue;let o=null,a="";if(r.source_session_id===e)o="outbound",a=r.target_session_id;else if(r.target_session_id===e)o="inbound",a=r.source_session_id;else continue;n.add(r.id),s.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return s.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),s}B();var NS=4e3,OS=2,LS=30,CS=.2,vS={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function vs(e){return e?Math.ceil(e.length/4):0}function Lu(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/LS);return Math.max(CS,t)}function Cu(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 vu(e){return f().prepare(`SELECT s.id,
1527
+ AND project_scope IS ?`).all(e,t),a=new Set(o.map(d=>d.id));if(a.size!==n.length)throw new Error(`ordered_ids length ${n.length} does not match sibling count ${a.size}`);for(let d of n)if(!a.has(d))throw new Error(`folder ${d} is not in the named sibling bucket`);let c=r.prepare("UPDATE thread_folders SET sort_order = ? WHERE id = ?");r.transaction(d=>{d.forEach((p,f)=>c.run(f*100,p))})(n),vn()}function pp(e){if(!gt(e))throw new Error(`folder ${e} not found`);h().prepare("DELETE FROM thread_folders WHERE id = ?").run(e),vn()}function mp(e,t){if(t!==null&&!gt(t))throw new Error(`folder ${t} not found`);if(h().prepare("UPDATE threads SET folder_id = ? WHERE id = ?").run(t,e).changes===0)throw new Error(`thread ${e} not found`)}function gp(e,t){let n=new Set,s=[];for(let r of t){if(n.has(r.id))continue;let o=null,a="";if(r.source_session_id===e)o="outbound",a=r.target_session_id;else if(r.target_session_id===e)o="inbound",a=r.source_session_id;else continue;n.add(r.id),s.push({linkId:r.id,otherSessionId:a,direction:o,updatedAt:r.updated_at,link:r})}return s.sort((r,o)=>r.updatedAt<o.updatedAt?1:r.updatedAt>o.updatedAt?-1:0),s}q();var Cw=4e3,Lw=2,vw=30,Iw=.2,Mw={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function dr(e){return e?Math.ceil(e.length/4):0}function fp(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/vw);return Math.max(Iw,t)}function _p(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function hp(e){return h().prepare(`SELECT s.id,
1438
1528
  NULLIF(sa.alias, '') AS alias,
1439
1529
  s.auto_title,
1440
1530
  s.auto_title_source,
@@ -1445,72 +1535,72 @@ ${h+1}. ${b}`:`${h+1}. ${b}`}).join(`
1445
1535
  FROM sessions s
1446
1536
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1447
1537
  LEFT JOIN projects p ON p.id = s.project_id
1448
- WHERE s.id = ?`).get(e)??null}function Iu(e){let n=f().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 ju(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 IS(e){let n=f().prepare(`SELECT id, auto_title, started_at
1538
+ WHERE s.id = ?`).get(e)??null}function Ep(e){let n=h().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!n||!n.summary)return null;let s=n.summary.trim();return s.length>0?s:null}function bp(e){let t=e.alias?.trim(),n=e.auto_title?.trim(),s=e.first_user_message?.trim();return n&&e.auto_title_source==="agent"?n:t||n||(s?s.slice(0,80):e.id.slice(0,8))}function jw(e){let n=h().prepare(`SELECT id, auto_title, started_at
1449
1539
  FROM sessions
1450
1540
  WHERE project_id = ?
1451
- ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,o=[];for(let b of n){if(!b.auto_title||!b.auto_title.startsWith("/")){o.push({id:b.id,brand:null,skill:null});continue}let S=b.auto_title.split(" \xB7 "),T=S[0].trim(),R=S.length>1?S.slice(1).join(" \xB7 ").trim():null;o.push({id:b.id,brand:R||null,skill:T||null}),R&&s.add(R),T&&r.add(T)}let a=[...s].sort(),c=new Map;a.forEach((b,S)=>c.set(b,S));let u=[...r].sort(),d=new Map;u.forEach((b,S)=>d.set(b,S));let m=new Map,h=new Map;for(let b of o){if(!b.brand||!b.skill)continue;let S=c.get(b.brand),T=d.get(b.skill);if(S===void 0||T===void 0)continue;let R=`${S}.${T}`,O=(m.get(R)??0)+1;m.set(R,O),h.set(b.id,`${S}.${T}.${O}`)}return{byId:h}}function jS(e){return{table:e!==null?IS(e):null,originProjectId:e,cache:new Map}}function Is(e,t){let n=e.cache.get(t);if(n)return n;let s=vu(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:s.id,title:ju(s),decimal:r,summary:Iu(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,o),o}function MS(e,t){let s=f().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1541
+ ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,o=[];for(let S of n){if(!S.auto_title||!S.auto_title.startsWith("/")){o.push({id:S.id,brand:null,skill:null});continue}let y=S.auto_title.split(" \xB7 "),b=y[0].trim(),R=y.length>1?y.slice(1).join(" \xB7 ").trim():null;o.push({id:S.id,brand:R||null,skill:b||null}),R&&s.add(R),b&&r.add(b)}let a=[...s].sort(),c=new Map;a.forEach((S,y)=>c.set(S,y));let u=[...r].sort(),d=new Map;u.forEach((S,y)=>d.set(S,y));let p=new Map,f=new Map;for(let S of o){if(!S.brand||!S.skill)continue;let y=c.get(S.brand),b=d.get(S.skill);if(y===void 0||b===void 0)continue;let R=`${y}.${b}`,k=(p.get(R)??0)+1;p.set(R,k),f.set(S.id,`${y}.${b}.${k}`)}return{byId:f}}function Dw(e){return{table:e!==null?jw(e):null,originProjectId:e,cache:new Map}}function pr(e,t){let n=e.cache.get(t);if(n)return n;let s=hp(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:s.id,title:bp(s),decimal:r,summary:Ep(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,o),o}function Pw(e,t){let s=h().prepare(`SELECT DISTINCT te.parent_session_id AS pid
1452
1542
  FROM thread_edges te
1453
1543
  WHERE te.session_id = ?
1454
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let a=Is(e,o.pid);a&&r.push(a)}return r}function DS(e,t){let s=f().prepare(`SELECT DISTINCT te.session_id AS sid
1544
+ AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let a=pr(e,o.pid);a&&r.push(a)}return r}function Fw(e,t){let s=h().prepare(`SELECT DISTINCT te.session_id AS sid
1455
1545
  FROM thread_edges te
1456
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let a=Is(e,o.sid);a&&r.push(a)}return r}function Mu(e){let t=vS[e.linkType]??.5,n=kt(e.confidence),s=t*n,r=Lu(e.daysApart),o=e.embeddingCosine??.5,a=kt(e.pagerank);if(e.scoring==="pagerank")return kt(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?kt(s):kt(o);let c=.35*s+.2*r+.2*o+.25*a;return kt(c)}function kt(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function FS(e,t,n,s,r){let o=new Map;function a(c,u){if(c===u)return;let d=o.get(c);d||(d=new Set,o.set(c,d)),d.add(u)}for(let c of t)a(c.source_session_id,c.target_session_id),a(c.target_session_id,c.source_session_id);for(let c of n)a(e,c.session_id);for(let c of n)a(c.session_id,e);for(let c of s)a(e,c.session_id);for(let c of s)a(c.session_id,e);if(r>1){let c=new Set([e]),u=new Set([e]);for(let d=1;d<r;d++){let m=new Set;for(let h of c){let b=o.get(h);if(b)for(let S of b){if(u.has(S))continue;let T=Jt(S).filter(R=>R.approved);for(let R of T)a(R.source_session_id,R.target_session_id),a(R.target_session_id,R.source_session_id);u.add(S),m.add(S)}}if(m.size===0)break;for(let h of m)c.add(h)}}return{edges:o}}function PS(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,a=new Map(r.map(d=>[d,o]));for(let d=0;d<n;d++){let m=new Map(r.map(h=>[h,(1-s)/r.length]));for(let h of r){let b=e.edges.get(h);if(!b||b.size===0)continue;let S=(a.get(h)??0)/b.size;for(let T of b)m.set(T,(m.get(T)??0)+s*S)}a=m}let c=0;for(let d of a.values())d>c&&(c=d);if(c<=0)return a;let u=new Map;for(let[d,m]of a)u.set(d,m/c);return u}var Du=240;function Fu(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function $S(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let o=Fu(e.summary,Du);return`${r}
1457
- ${o}`}return r}function US(e,t,n){let s=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(s.push(c),o+=vs(c),e.summary){let u=Fu(e.summary,Du*4);s.push(u),o+=vs(u)}s.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,m=vs(d),h=[],b=0;for(let S of u.refs){let T=$S(S),R=vs(T);if(o+m+b+R>n){r.push({session_id:S.session_id,title:S.title,decimal:S.decimal,summary:S.summary,project:S.project,started_at:S.started_at});continue}h.push(T),b+=R}if(h.length>0){s.push(d);for(let S of h)s.push(S);s.push(""),o+=m+b}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
1546
+ WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let a=pr(e,o.sid);a&&r.push(a)}return r}function Sp(e){let t=Mw[e.linkType]??.5,n=Ht(e.confidence),s=t*n,r=fp(e.daysApart),o=e.embeddingCosine??.5,a=Ht(e.pagerank);if(e.scoring==="pagerank")return Ht(a);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Ht(s):Ht(o);let c=.35*s+.2*r+.2*o+.25*a;return Ht(c)}function Ht(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function $w(e,t,n,s,r){let o=new Map;function a(c,u){if(c===u)return;let d=o.get(c);d||(d=new Set,o.set(c,d)),d.add(u)}for(let c of t)a(c.source_session_id,c.target_session_id),a(c.target_session_id,c.source_session_id);for(let c of n)a(e,c.session_id);for(let c of n)a(c.session_id,e);for(let c of s)a(e,c.session_id);for(let c of s)a(c.session_id,e);if(r>1){let c=new Set([e]),u=new Set([e]);for(let d=1;d<r;d++){let p=new Set;for(let f of c){let S=o.get(f);if(S)for(let y of S){if(u.has(y))continue;let b=un(y).filter(R=>R.approved);for(let R of b)a(R.source_session_id,R.target_session_id),a(R.target_session_id,R.source_session_id);u.add(y),p.add(y)}}if(p.size===0)break;for(let f of p)c.add(f)}}return{edges:o}}function Uw(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,a=new Map(r.map(d=>[d,o]));for(let d=0;d<n;d++){let p=new Map(r.map(f=>[f,(1-s)/r.length]));for(let f of r){let S=e.edges.get(f);if(!S||S.size===0)continue;let y=(a.get(f)??0)/S.size;for(let b of S)p.set(b,(p.get(b)??0)+s*y)}a=p}let c=0;for(let d of a.values())d>c&&(c=d);if(c<=0)return a;let u=new Map;for(let[d,p]of a)u.set(d,p/c);return u}var yp=240;function Tp(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function Hw(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let o=Tp(e.summary,yp);return`${r}
1547
+ ${o}`}return r}function Bw(e,t,n){let s=[],r=[],o=0,a=e.decimal?`${e.decimal}: `:"",c=`# Neighborhood for ${e.session_id} (${a}${e.title})`;if(s.push(c),o+=dr(c),e.summary){let u=Tp(e.summary,yp*4);s.push(u),o+=dr(u)}s.push("");for(let u of t){if(u.refs.length===0)continue;let d=`## ${u.heading}`,p=dr(d),f=[],S=0;for(let y of u.refs){let b=Hw(y),R=dr(b);if(o+p+S+R>n){r.push({session_id:y.session_id,title:y.title,decimal:y.decimal,summary:y.summary,project:y.project,started_at:y.started_at});continue}f.push(b),S+=R}if(f.length>0){s.push(d);for(let y of f)s.push(y);s.push(""),o+=p+S}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
1458
1548
  `)+`
1459
- `,budgetUsed:o,truncated:r}}function BS(e,t,n,s,r,o){let a=[];for(let c of n){if(s&&!s.has(c.link_type))continue;let u=null;if(c.source_session_id===t.session_id?u=c.target_session_id:c.target_session_id===t.session_id&&(u=c.source_session_id),!u)continue;let d=Is(e,u);if(!d)continue;let m=Cu(t.started_at,d.started_at),h=Mu({confidence:c.confidence,linkType:c.link_type,daysApart:m,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:h,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(m)}d apart`,link_type:c.link_type})}return a}function js(e,t={}){let n=Math.max(100,Math.floor(t.budget??NS)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??OS)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=vu(e);if(!u)throw new Error(`session not found: ${e}`);let d=jS(u.project_id),m={session_id:u.id,title:ju(u),decimal:d.table?.byId.get(u.id)??null,summary:Iu(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,m);let h=MS(d,e),b=DS(d,e),S=Jt(e).filter(H=>H.approved).filter(H=>!c||c.has(H.link_type)).filter(H=>o||H.link_type!=="wiki_link"),T=FS(e,S,h,b,r),R=PS(T),O=[],L=[],x=[],F=[];for(let H of S){let se=H.source_session_id===e?H.target_session_id:H.source_session_id,i=Is(d,se);if(!i)continue;let l=Cu(m.started_at,i.started_at),p=Mu({confidence:H.confidence,linkType:H.link_type,daysApart:l,embeddingCosine:null,pagerank:R.get(se)??0,scoring:s}),g=Lu(l),_=`${H.link_type} confidence=${H.confidence.toFixed(2)} recency=${g.toFixed(2)} (${Math.round(l)}d apart)`,E={...i,score:p,evidence:_,link_type:H.link_type};H.link_type==="citation"?O.push(E):H.link_type==="similar"?L.push(E):H.link_type==="wiki_link"?F.push(E):x.push(E)}if(a){let H=lt({sourceSessionId:e,status:"pending",limit:100}),se=lt({targetSessionId:e,status:"pending",limit:100}),i=[...H,...se],l=new Set,p=i.filter(_=>l.has(_.id)?!1:(l.add(_.id),!0)),g=BS(d,m,p,c,s,R);for(let _ of g)_.link_type==="citation"?O.push(_):_.link_type==="similar"?L.push(_):_.link_type==="wiki_link"?F.push(_):x.push(_)}let A=(H,se)=>se.score-H.score;O.sort(A),L.sort(A),x.sort(A),F.sort(A);let te=US(m,[{heading:"Parents",refs:h},{heading:"Children",refs:b},{heading:"Citations (approved)",refs:O},{heading:"Similar sessions",refs:L},{heading:"Cousins (skill track + temporal)",refs:x},{heading:"Wiki links (manual)",refs:F}],n);return{origin:m,parents:h,children:b,citations:O,similar:L,cousins:x,wikiLinks:F,bundle:te.bundle,budgetUsed:te.budgetUsed,budgetRemaining:Math.max(0,n-te.budgetUsed),truncated:te.truncated}}import{randomUUID as YS}from"node:crypto";var HS=50;function WS(e){if(e.length===0)return[];let t=[...e].sort((u,d)=>u.added_at<d.added_at?-1:u.added_at>d.added_at?1:0),n=new Map,s=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=n.get(u.parent_session_id)??[];d.push(u),n.set(u.parent_session_id,d)}let o=t.filter(u=>u.role==="origin"),c=[...o.length>0?o:t.filter(u=>!u.parent_session_id)];for(;c.length>0;){let u=c.shift();if(s.has(u.session_id))continue;s.add(u.session_id),r.push(u.session_id);let d=n.get(u.session_id)??[];for(let m of d)s.has(m.session_id)||c.push(m)}for(let u of t)s.has(u.session_id)||(s.add(u.session_id),r.push(u.session_id));return r}function qS(e){let t=ao(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(`
1549
+ `,budgetUsed:o,truncated:r}}function Ww(e,t,n,s,r,o){let a=[];for(let c of n){if(s&&!s.has(c.link_type))continue;let u=null;if(c.source_session_id===t.session_id?u=c.target_session_id:c.target_session_id===t.session_id&&(u=c.source_session_id),!u)continue;let d=pr(e,u);if(!d)continue;let p=_p(t.started_at,d.started_at),f=Sp({confidence:c.confidence,linkType:c.link_type,daysApart:p,embeddingCosine:null,pagerank:o.get(u)??0,scoring:r});a.push({...d,score:f,evidence:`(suggestion, ${c.inferred_by}) confidence=${c.confidence.toFixed(2)} ${Math.round(p)}d apart`,link_type:c.link_type})}return a}function mr(e,t={}){let n=Math.max(100,Math.floor(t.budget??Cw)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Lw)),o=t.includeWikiLinks??!0,a=t.includeSuggestions??!1,c=t.edgeTypes?new Set(t.edgeTypes):null,u=hp(e);if(!u)throw new Error(`session not found: ${e}`);let d=Dw(u.project_id),p={session_id:u.id,title:bp(u),decimal:d.table?.byId.get(u.id)??null,summary:Ep(u.id),project:u.project,started_at:u.started_at};d.cache.set(u.id,p);let f=Pw(d,e),S=Fw(d,e),y=un(e).filter(U=>U.approved).filter(U=>!c||c.has(U.link_type)).filter(U=>o||U.link_type!=="wiki_link"),b=$w(e,y,f,S,r),R=Uw(b),k=[],M=[],P=[],G=[];for(let U of y){let L=U.source_session_id===e?U.target_session_id:U.source_session_id,i=pr(d,L);if(!i)continue;let l=_p(p.started_at,i.started_at),m=Sp({confidence:U.confidence,linkType:U.link_type,daysApart:l,embeddingCosine:null,pagerank:R.get(L)??0,scoring:s}),g=fp(l),_=`${U.link_type} confidence=${U.confidence.toFixed(2)} recency=${g.toFixed(2)} (${Math.round(l)}d apart)`,E={...i,score:m,evidence:_,link_type:U.link_type};U.link_type==="citation"?k.push(E):U.link_type==="similar"?M.push(E):U.link_type==="wiki_link"?G.push(E):P.push(E)}if(a){let U=St({sourceSessionId:e,status:"pending",limit:100}),L=St({targetSessionId:e,status:"pending",limit:100}),i=[...U,...L],l=new Set,m=i.filter(_=>l.has(_.id)?!1:(l.add(_.id),!0)),g=Ww(d,p,m,c,s,R);for(let _ of g)_.link_type==="citation"?k.push(_):_.link_type==="similar"?M.push(_):_.link_type==="wiki_link"?G.push(_):P.push(_)}let D=(U,L)=>L.score-U.score;k.sort(D),M.sort(D),P.sort(D),G.sort(D);let F=Bw(p,[{heading:"Parents",refs:f},{heading:"Children",refs:S},{heading:"Citations (approved)",refs:k},{heading:"Similar sessions",refs:M},{heading:"Cousins (skill track + temporal)",refs:P},{heading:"Wiki links (manual)",refs:G}],n);return{origin:p,parents:f,children:S,citations:k,similar:M,cousins:P,wikiLinks:G,bundle:F.bundle,budgetUsed:F.budgetUsed,budgetRemaining:Math.max(0,n-F.budgetUsed),truncated:F.truncated}}import{randomUUID as zw}from"node:crypto";var qw=50;function Xw(e){if(e.length===0)return[];let t=[...e].sort((u,d)=>u.added_at<d.added_at?-1:u.added_at>d.added_at?1:0),n=new Map,s=new Set,r=[];for(let u of t)if(u.parent_session_id){let d=n.get(u.parent_session_id)??[];d.push(u),n.set(u.parent_session_id,d)}let o=t.filter(u=>u.role==="origin"),c=[...o.length>0?o:t.filter(u=>!u.parent_session_id)];for(;c.length>0;){let u=c.shift();if(s.has(u.session_id))continue;s.add(u.session_id),r.push(u.session_id);let d=n.get(u.session_id)??[];for(let p of d)s.has(p.session_id)||c.push(p)}for(let u of t)s.has(u.session_id)||(s.add(u.session_id),r.push(u.session_id));return r}function Jw(e){let t=Xo(e.sessionId),n=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
1460
1550
  `);return`${t}
1461
- ${n}`}function XS(e){return Le(e)?.auto_title_source??null}async function JS(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(ye(),et));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=GS(s.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,HS)}function GS(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 Pu(s)}}catch{}return Pu(t)}function Pu(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function Uu(e,t={}){let n=ie(e);if(!n)throw new Error(`thread not found: ${e}`);let s=WS(n.edges),r=s.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let m=0;m<s.length;m++){let h=s[m],b=m+1;if(a?.aborted){let T={sessionId:h,reason:"cancelled"};u.push(T),t.onSkipped?.(T);continue}if(!o&&XS(h)==="agent"){let T={sessionId:h,reason:"already-titled"};u.push(T),t.onSkipped?.(T);continue}let S;try{if($u)S=await $u({sessionId:h,current:b,total:r});else{let T=qS({sessionId:h,current:b,total:r});S=await JS({prompt:T,model:t.model})}}catch(T){let R=T instanceof Error?T.message:String(T??"unknown error"),O={sessionId:h,error:R};d.push(O),t.onFailed?.(O);continue}try{Ee(h,S,"agent")}catch(T){let R=T instanceof Error?T.message:String(T??"unknown error"),O={sessionId:h,error:`setAutoTitle failed: ${R}`};d.push(O),t.onFailed?.(O);continue}c.push(h),t.onProgress?.({current:b,total:r,sessionId:h,title:S})}return{generated:c,skipped:u,failed:d}}var $u=null;var un=new Map,zS=300*1e3;function ln(e,t,n){let s=e.events.length+1;e.events.push({id:s,kind:t,data:n});for(let r of e.waiters)r.resolve();e.waiters.clear()}function KS(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{un.delete(e.jobId)},zS)}function VS(e){let t=0,n=0,s=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(n+=1),r.kind==="error"&&(s+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:n,failed:s,result:e.result}}function Bu(e){let t=YS(),n=new AbortController,s={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:n,result:null,cleanupTimer:null};return un.set(t,s),(async()=>{await Promise.resolve();try{let r=await Uu(e.threadId,{force:e.force??!1,signal:n.signal,model:e.model,onProgress:o=>{s.total=o.total,ln(s,"progress",o)},onSkipped:o=>{ln(s,"skipped",o)},onFailed:o=>{ln(s,"error",o)}});s.result=r,s.status=n.signal.aborted?"cancelled":"done",s.endedAt=new Date().toISOString(),ln(s,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");s.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},s.status="failed",s.endedAt=new Date().toISOString(),ln(s,"done",s.result)}finally{KS(s)}})(),t}async function*Hu(e,t=0){let n=un.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(s+=1,yield r,r.kind==="done")return}if(n.endedAt)return;await new Promise(r=>{n.waiters.add({resolve:r})})}}function Wu(e){let t=un.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Fo(e){let t=un.get(e);return t?VS(t):null}import{existsSync as qu,mkdirSync as ZS,readFileSync as QS,writeFileSync as eT,chmodSync as tT}from"node:fs";import{homedir as nT}from"node:os";import{join as Xu}from"node:path";import{z as He}from"zod";function Ju(){return process.env.RECALL_HOME??Xu(nT(),".recall")}function sT(){let e=Ju();qu(e)||ZS(e,{recursive:!0})}function Gu(){return Xu(Ju(),"config.json")}var Ds=He.object({enabled:He.boolean().default(!1),backend:He.enum(["api","mcp"]).default("api"),apiKey:He.string().optional(),model:He.string().default("claude-opus-4-7"),maxTagsPerSession:He.number().int().min(1).max(10).default(4),minTagsPerSession:He.number().int().min(1).max(10).default(2),autopilot:He.boolean().default(!1)}),Ms={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function Yu(){let e=Gu();if(!qu(e))return{};try{return JSON.parse(QS(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Fe(){let e=Yu().autoTag;if(!e)return{...Ms};let t=Ds.safeParse({...Ms,...e});return t.success?t.data:{...Ms}}function zu(e){sT();let t=Yu(),n=Ds.parse({...Ms,...t.autoTag??{},...e}),s={...t,autoTag:n},r=Gu();eT(r,JSON.stringify(s,null,2));try{tT(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return n}function Po(e){let{apiKey:t,...n}=e;return{...n,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}B();var Fs="claude-haiku-4-5-20251001",Ku=80,rT=2e3,At=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},Vu=null;async function oT(e,t){if(Vu)return Vu(e,t);let{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(ye(),et));return s()?n(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function iT(e){let n=f().prepare(`SELECT s.id,
1551
+ ${n}`}function Gw(e){return ve(e)?.auto_title_source??null}async function Yw(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(Te(),ct));if(!n())throw new Error("claude CLI not found on PATH");let s=await t(e.prompt,[],{model:e.model});if(!s.success)throw new Error(`claude CLI exited ${s.exitCode}: ${s.stderr.slice(-500)}`);let r=Kw(s.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,qw)}function Kw(e){let t=e.trim();if(!t)return"";try{let n=JSON.parse(t);if(n&&typeof n=="object"){let s=n.result;if(typeof s=="string")return wp(s)}}catch{}return wp(t)}function wp(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function kp(e,t={}){let n=ce(e);if(!n)throw new Error(`thread not found: ${e}`);let s=Xw(n.edges),r=s.length,o=t.force??!1,a=t.signal,c=[],u=[],d=[];for(let p=0;p<s.length;p++){let f=s[p],S=p+1;if(a?.aborted){let b={sessionId:f,reason:"cancelled"};u.push(b),t.onSkipped?.(b);continue}if(!o&&Gw(f)==="agent"){let b={sessionId:f,reason:"already-titled"};u.push(b),t.onSkipped?.(b);continue}let y;try{if(Rp)y=await Rp({sessionId:f,current:S,total:r});else{let b=Jw({sessionId:f,current:S,total:r});y=await Yw({prompt:b,model:t.model})}}catch(b){let R=b instanceof Error?b.message:String(b??"unknown error"),k={sessionId:f,error:R};d.push(k),t.onFailed?.(k);continue}try{Ee(f,y,"agent")}catch(b){let R=b instanceof Error?b.message:String(b??"unknown error"),k={sessionId:f,error:`setAutoTitle failed: ${R}`};d.push(k),t.onFailed?.(k);continue}c.push(f),t.onProgress?.({current:S,total:r,sessionId:f,title:y})}return{generated:c,skipped:u,failed:d}}var Rp=null;var Mn=new Map,Vw=300*1e3;function In(e,t,n){let s=e.events.length+1;e.events.push({id:s,kind:t,data:n});for(let r of e.waiters)r.resolve();e.waiters.clear()}function Qw(e){e.cleanupTimer&&clearTimeout(e.cleanupTimer),e.cleanupTimer=setTimeout(()=>{Mn.delete(e.jobId)},Vw)}function Zw(e){let t=0,n=0,s=0;for(let r of e.events)r.kind==="progress"&&(t+=1),r.kind==="skipped"&&(n+=1),r.kind==="error"&&(s+=1);return{jobId:e.jobId,threadId:e.threadId,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.total,done:t,skipped:n,failed:s,result:e.result}}function Ap(e){let t=zw(),n=new AbortController,s={jobId:t,threadId:e.threadId,status:"running",startedAt:new Date().toISOString(),endedAt:null,total:0,events:[],waiters:new Set,controller:n,result:null,cleanupTimer:null};return Mn.set(t,s),(async()=>{await Promise.resolve();try{let r=await kp(e.threadId,{force:e.force??!1,signal:n.signal,model:e.model,onProgress:o=>{s.total=o.total,In(s,"progress",o)},onSkipped:o=>{In(s,"skipped",o)},onFailed:o=>{In(s,"error",o)}});s.result=r,s.status=n.signal.aborted?"cancelled":"done",s.endedAt=new Date().toISOString(),In(s,"done",r)}catch(r){let o=r instanceof Error?r.message:String(r??"unknown error");s.result={generated:[],skipped:[],failed:[{sessionId:e.threadId,error:o}]},s.status="failed",s.endedAt=new Date().toISOString(),In(s,"done",s.result)}finally{Qw(s)}})(),t}async function*xp(e,t=0){let n=Mn.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(s+=1,yield r,r.kind==="done")return}if(n.endedAt)return;await new Promise(r=>{n.waiters.add({resolve:r})})}}function Np(e){let t=Mn.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Mi(e){let t=Mn.get(e);return t?Zw(t):null}import{existsSync as Op,mkdirSync as e0,readFileSync as t0,writeFileSync as n0,chmodSync as s0}from"node:fs";import{homedir as r0}from"node:os";import{join as Cp}from"node:path";import{z as ze}from"zod";function Lp(){return process.env.RECALL_HOME??Cp(r0(),".recall")}function o0(){let e=Lp();Op(e)||e0(e,{recursive:!0})}function vp(){return Cp(Lp(),"config.json")}var fr=ze.object({enabled:ze.boolean().default(!1),backend:ze.enum(["api","mcp"]).default("api"),apiKey:ze.string().optional(),model:ze.string().default("claude-opus-4-7"),maxTagsPerSession:ze.number().int().min(1).max(10).default(4),minTagsPerSession:ze.number().int().min(1).max(10).default(2),autopilot:ze.boolean().default(!1)}),gr={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function Ip(){let e=vp();if(!Op(e))return{};try{return JSON.parse(t0(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Be(){let e=Ip().autoTag;if(!e)return{...gr};let t=fr.safeParse({...gr,...e});return t.success?t.data:{...gr}}function Mp(e){o0();let t=Ip(),n=fr.parse({...gr,...t.autoTag??{},...e}),s={...t,autoTag:n},r=vp();n0(r,JSON.stringify(s,null,2));try{s0(r,384)}catch(o){console.error("[auto-tag-config] chmod 0600 failed (continuing):",o)}return n}function ji(e){let{apiKey:t,...n}=e;return{...n,apiKey:t?"sk-ant-\u2026":null,hasApiKey:!!t}}q();var _r="claude-haiku-4-5-20251001",jp=80,i0=2e3,Bt=class extends Error{sessionId;constructor(t){super(`no neighborhood context available for session ${t}`),this.name="NoContextAvailableError",this.sessionId=t}},Dp=null;async function a0(e,t){if(Dp)return Dp(e,t);let{spawnClaudePrompt:n,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(Te(),ct));return s()?n(e,[],{model:t}):{success:!1,stdout:"",stderr:"claude CLI not found on PATH",exitCode:null}}function c0(e){let n=h().prepare(`SELECT s.id,
1462
1552
  s.auto_title,
1463
1553
  s.auto_title_source,
1464
1554
  CASE WHEN sa.alias IS NOT NULL AND sa.alias != ''
1465
1555
  THEN 1 ELSE 0 END AS has_alias_int
1466
1556
  FROM sessions s
1467
1557
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1468
- WHERE s.id = ?`).get(e);return n?{id:n.id,auto_title:n.auto_title,auto_title_source:n.auto_title_source??null,has_alias:n.has_alias_int===1}:null}function aT(e){return e.parents.length===0&&e.children.length===0&&e.citations.length===0&&e.similar.length===0&&e.cousins.length===0&&e.wikiLinks.length===0}function cT(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let n=tn({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(n){case"low_signal":case"recursive_meta":case"programmatic":return{eligible:!0};case"agent":return{eligible:!1,reason:"agent_titled"};case"manual_alias":return{eligible:!1,reason:"manual_alias"};default:return{eligible:!1,reason:"clean"}}}function lT(e){return["You are renaming a Claude Code session. Below is its NEIGHBORHOOD: parent","sessions, child sessions, related sessions, and citations \u2014 assembled by","Claude Recall's cog-graph. The session itself has a low-signal or","self-referential title that needs replacement.","","Read the neighborhood, then mint a single descriptive title that","reflects this session's role in the surrounding work. Constraints:","","- Maximum 80 characters.","- No quotes, no trailing punctuation.","- Output ONLY a JSON object with two fields:",' { "title": "<\u226480 char title>", "evidence": "<one sentence why>" }',"- No markdown, no preface, no commentary outside the JSON.","","--- NEIGHBORHOOD BUNDLE ---",e,"--- END NEIGHBORHOOD BUNDLE ---"].join(`
1469
- `)}function uT(e){let t=e.trim();if(!t)return null;let n=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")n=a.result.trim();else if(typeof a.title=="string")return Zu(a)}}catch{}let s=n.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);s&&(n=s[1].trim());let r=null;try{r=JSON.parse(n)}catch{let o=n.indexOf("{"),a=n.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(n.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:Zu(r)}function Zu(e){let t=e.title;if(typeof t!="string")return null;let n=dT(t).trim();if(!n)return null;let s=e.evidence,r=typeof s=="string"?s.trim():"";return{title:n,evidence:r}}function dT(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function pT(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=Ku?t:t.slice(0,Ku)}function mT(e,t){let n=e.auto_title??"";return!n&&e.has_alias&&(n=f().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:n,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function $o(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let n=iT(e);if(!n)throw new Error(`session not found: ${e}`);let s=cT(n,t.force===!0);if(!s.eligible)return mT(n,s.reason);let r=t.budget??rT,o=js(e,{budget:r});if(aT(o))throw new At(e);if(t.signal?.aborted)return{session_id:e,title:n.auto_title??"",source:n.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=lT(o.bundle),c=t.model??Fs,u=await oT(a,c);if(!u.success){let S=u.stderr.slice(-300);throw new Error(`claude CLI exited ${u.exitCode}: ${S||"no stderr captured"}`)}let d=uT(u.stdout);if(!d)throw new Error("failed to parse regeneration output: not valid JSON {title, evidence}");if(!d.title||!d.title.trim())throw new Error("regeneration produced empty title");let m=pT(d.title);if(!m)throw new Error("regeneration produced empty title after clamp");Ee(e,m,"agent");let b=Le(e)?.auto_title??m;return{session_id:e,title:b,source:"agent",confidence:_T(o),evidence:d.evidence||`regenerated from neighborhood (${gT(o)})`,written:!0}}function gT(e){let t=[];return e.parents.length&&t.push(`${e.parents.length} parents`),e.children.length&&t.push(`${e.children.length} children`),e.citations.length&&t.push(`${e.citations.length} citations`),e.similar.length&&t.push(`${e.similar.length} similar`),e.cousins.length&&t.push(`${e.cousins.length} cousins`),e.wikiLinks.length&&t.push(`${e.wikiLinks.length} wiki-links`),t.join(", ")}function _T(e){let t=e.parents.length,n=e.children.length,s=e.citations.length,r=e.similar.length,o=e.cousins.length,a=e.wikiLinks.length,c=.25*Math.min(1,t)+.15*Math.min(1,n)+.2*Math.min(1,s/2)+.15*Math.min(1,r/3)+.1*Math.min(1,o/3)+.15*Math.min(1,a);return Math.min(.95,c)}import{streamSSE as Ye}from"hono/streaming";import{bodyLimit as Jw}from"hono/body-limit";import{z as D}from"zod";B();B();ct();function dn(e){return f().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}B();function Qu(e){let t=f(),n=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1558
+ WHERE s.id = ?`).get(e);return n?{id:n.id,auto_title:n.auto_title,auto_title_source:n.auto_title_source??null,has_alias:n.has_alias_int===1}:null}function l0(e){return e.parents.length===0&&e.children.length===0&&e.citations.length===0&&e.similar.length===0&&e.cousins.length===0&&e.wikiLinks.length===0}function u0(e,t){if(e.has_alias)return{eligible:!1,reason:"manual_alias"};let n=bn({auto_title:e.auto_title,auto_title_source:e.auto_title_source,has_alias:e.has_alias});if(t)return{eligible:!0};switch(n){case"low_signal":case"recursive_meta":case"programmatic":return{eligible:!0};case"agent":return{eligible:!1,reason:"agent_titled"};case"manual_alias":return{eligible:!1,reason:"manual_alias"};default:return{eligible:!1,reason:"clean"}}}function d0(e){return["You are renaming a Claude Code session. Below is its NEIGHBORHOOD: parent","sessions, child sessions, related sessions, and citations \u2014 assembled by","Claude Recall's cog-graph. The session itself has a low-signal or","self-referential title that needs replacement.","","Read the neighborhood, then mint a single descriptive title that","reflects this session's role in the surrounding work. Constraints:","","- Maximum 80 characters.","- No quotes, no trailing punctuation.","- Output ONLY a JSON object with two fields:",' { "title": "<\u226480 char title>", "evidence": "<one sentence why>" }',"- No markdown, no preface, no commentary outside the JSON.","","--- NEIGHBORHOOD BUNDLE ---",e,"--- END NEIGHBORHOOD BUNDLE ---"].join(`
1559
+ `)}function p0(e){let t=e.trim();if(!t)return null;let n=t;try{let o=JSON.parse(t);if(o&&typeof o=="object"){let a=o;if(typeof a.result=="string")n=a.result.trim();else if(typeof a.title=="string")return Pp(a)}}catch{}let s=n.match(/```(?:json)?\s*\n([\s\S]*?)\n?```/i);s&&(n=s[1].trim());let r=null;try{r=JSON.parse(n)}catch{let o=n.indexOf("{"),a=n.lastIndexOf("}");if(o>=0&&a>o)try{r=JSON.parse(n.slice(o,a+1))}catch{return null}else return null}return!r||typeof r!="object"?null:Pp(r)}function Pp(e){let t=e.title;if(typeof t!="string")return null;let n=m0(t).trim();if(!n)return null;let s=e.evidence,r=typeof s=="string"?s.trim():"";return{title:n,evidence:r}}function m0(e){return e.replace(/^["'`]+|["'`]+$/g,"")}function g0(e){let t=e.replace(/\s+/g," ").trim().replace(/[.!?]+$/g,"").trim();return t.length<=jp?t:t.slice(0,jp)}function f0(e,t){let n=e.auto_title??"";return!n&&e.has_alias&&(n=h().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e.id)?.alias??""),{session_id:e.id,title:n,source:e.auto_title_source,confidence:0,evidence:`skipped: ${t}`,written:!1,skipped:t}}async function Di(e,t={}){if(t.signal?.aborted)return{session_id:e,title:"",source:null,confidence:0,evidence:"aborted before start",written:!1,skipped:"aborted"};let n=c0(e);if(!n)throw new Error(`session not found: ${e}`);let s=u0(n,t.force===!0);if(!s.eligible)return f0(n,s.reason);let r=t.budget??i0,o=mr(e,{budget:r});if(l0(o))throw new Bt(e);if(t.signal?.aborted)return{session_id:e,title:n.auto_title??"",source:n.auto_title_source,confidence:0,evidence:"aborted before CLI call",written:!1,skipped:"aborted"};let a=d0(o.bundle),c=t.model??_r,u=await a0(a,c);if(!u.success){let y=u.stderr.slice(-300);throw new Error(`claude CLI exited ${u.exitCode}: ${y||"no stderr captured"}`)}let d=p0(u.stdout);if(!d)throw new Error("failed to parse regeneration output: not valid JSON {title, evidence}");if(!d.title||!d.title.trim())throw new Error("regeneration produced empty title");let p=g0(d.title);if(!p)throw new Error("regeneration produced empty title after clamp");Ee(e,p,"agent");let S=ve(e)?.auto_title??p;return{session_id:e,title:S,source:"agent",confidence:h0(o),evidence:d.evidence||`regenerated from neighborhood (${_0(o)})`,written:!0}}function _0(e){let t=[];return e.parents.length&&t.push(`${e.parents.length} parents`),e.children.length&&t.push(`${e.children.length} children`),e.citations.length&&t.push(`${e.citations.length} citations`),e.similar.length&&t.push(`${e.similar.length} similar`),e.cousins.length&&t.push(`${e.cousins.length} cousins`),e.wikiLinks.length&&t.push(`${e.wikiLinks.length} wiki-links`),t.join(", ")}function h0(e){let t=e.parents.length,n=e.children.length,s=e.citations.length,r=e.similar.length,o=e.cousins.length,a=e.wikiLinks.length,c=.25*Math.min(1,t)+.15*Math.min(1,n)+.2*Math.min(1,s/2)+.15*Math.min(1,r/3)+.1*Math.min(1,o/3)+.15*Math.min(1,a);return Math.min(.95,c)}import{streamSSE as tt}from"hono/streaming";import{bodyLimit as $k}from"hono/body-limit";import{z as $}from"zod";q();q();bt();function jn(e){return h().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}q();function Fp(e){let t=h(),n=new Date().toISOString();return t.prepare(`INSERT INTO bug_signature_resolutions
1470
1560
  (message_hash, resolved_in_session_id, fix_summary, resolved_at, unresolved_at)
1471
1561
  VALUES (?, ?, ?, ?, NULL)
1472
1562
  ON CONFLICT(message_hash) DO UPDATE SET
1473
1563
  resolved_in_session_id = excluded.resolved_in_session_id,
1474
1564
  fix_summary = excluded.fix_summary,
1475
1565
  resolved_at = excluded.resolved_at,
1476
- unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,n),fT(e.messageHash)}function ed(e){f().prepare(`UPDATE bug_signature_resolutions
1566
+ unresolved_at = NULL`).run(e.messageHash,e.resolvedInSessionId??null,e.fixSummary??null,n),E0(e.messageHash)}function $p(e){h().prepare(`UPDATE bug_signature_resolutions
1477
1567
  SET unresolved_at = ?
1478
- WHERE message_hash = ?`).run(new Date().toISOString(),e)}function fT(e){return f().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function Uo(e){if(e.length===0)return new Map;let t=f(),n=e.map(()=>"?").join(","),s=t.prepare(`SELECT * FROM bug_signature_resolutions
1479
- WHERE message_hash IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.message_hash,o);return r}function Bo(e){return!!e&&e.unresolved_at===null}B();function pn(){return new Date().toISOString()}function Ho(){let e=f(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1568
+ WHERE message_hash = ?`).run(new Date().toISOString(),e)}function E0(e){return h().prepare("SELECT * FROM bug_signature_resolutions WHERE message_hash = ?").get(e)??null}function Pi(e){if(e.length===0)return new Map;let t=h(),n=e.map(()=>"?").join(","),s=t.prepare(`SELECT * FROM bug_signature_resolutions
1569
+ WHERE message_hash IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.message_hash,o);return r}function Fi(e){return!!e&&e.unresolved_at===null}q();function Dn(){return new Date().toISOString()}function $i(){let e=h(),t=e.prepare(`SELECT id, name, description, created_at, updated_at
1480
1570
  FROM macro_repos
1481
1571
  ORDER BY name COLLATE NOCASE`).all();if(t.length===0)return[];let n=t.map(a=>a.id),s=n.map(()=>"?").join(","),r=e.prepare(`SELECT m.macro_repo_id, m.project_id, p.name AS project_name
1482
1572
  FROM macro_repo_members m
1483
1573
  JOIN projects p ON p.id = m.project_id
1484
1574
  WHERE m.macro_repo_id IN (${s})
1485
- ORDER BY p.name COLLATE NOCASE`).all(...n),o=new Map;for(let a of t)o.set(a.id,{...a,member_project_ids:[],member_project_names:[]});for(let a of r){let c=o.get(a.macro_repo_id);c&&(c.member_project_ids.push(a.project_id),c.member_project_names.push(a.project_name))}return Array.from(o.values())}function xt(e){return Ho().find(n=>n.id===e)??null}function td(){return f().prepare(`SELECT p.id, p.name
1575
+ ORDER BY p.name COLLATE NOCASE`).all(...n),o=new Map;for(let a of t)o.set(a.id,{...a,member_project_ids:[],member_project_names:[]});for(let a of r){let c=o.get(a.macro_repo_id);c&&(c.member_project_ids.push(a.project_id),c.member_project_names.push(a.project_name))}return Array.from(o.values())}function Wt(e){return $i().find(n=>n.id===e)??null}function Up(){return h().prepare(`SELECT p.id, p.name
1486
1576
  FROM projects p
1487
1577
  LEFT JOIN macro_repo_members m ON m.project_id = p.id
1488
1578
  WHERE m.project_id IS NULL
1489
- ORDER BY p.name COLLATE NOCASE`).all()}function nd(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let n=f(),s=pn(),r=n.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1490
- VALUES (?, ?, ?, ?)`).run(t,e.description??null,s,s),o=Number(r.lastInsertRowid);return xt(o)}function sd(e,t){let n=xt(e);if(!n)throw new Error(`macro repo ${e} not found`);let s=t.name!==void 0?t.name.trim():n.name;if(!s)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:n.description;return f().prepare(`UPDATE macro_repos
1579
+ ORDER BY p.name COLLATE NOCASE`).all()}function Hp(e){let t=e.name.trim();if(!t)throw new Error("macro repo name is required");let n=h(),s=Dn(),r=n.prepare(`INSERT INTO macro_repos (name, description, created_at, updated_at)
1580
+ VALUES (?, ?, ?, ?)`).run(t,e.description??null,s,s),o=Number(r.lastInsertRowid);return Wt(o)}function Bp(e,t){let n=Wt(e);if(!n)throw new Error(`macro repo ${e} not found`);let s=t.name!==void 0?t.name.trim():n.name;if(!s)throw new Error("macro repo name cannot be empty");let r=t.description!==void 0?t.description:n.description;return h().prepare(`UPDATE macro_repos
1491
1581
  SET name = ?, description = ?, updated_at = ?
1492
- WHERE id = ?`).run(s,r,pn(),e),xt(e)}function rd(e){f().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function od(e,t){let n=f();if(!n.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!n.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);n.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1493
- VALUES (?, ?, ?)`).run(e,t,pn()),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(pn(),e)}function id(e,t){let n=f();n.prepare(`DELETE FROM macro_repo_members
1494
- WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(pn(),e)}B();function ad(e){let t=f(),n=new Date().toISOString(),s=t.prepare(`INSERT INTO bug_synthesis_results
1582
+ WHERE id = ?`).run(s,r,Dn(),e),Wt(e)}function Wp(e){h().prepare("DELETE FROM macro_repos WHERE id = ?").run(e)}function qp(e,t){let n=h();if(!n.prepare("SELECT 1 FROM macro_repos WHERE id = ?").get(e))throw new Error(`macro repo ${e} not found`);if(!n.prepare("SELECT 1 FROM projects WHERE id = ?").get(t))throw new Error(`project ${t} not found`);n.prepare(`INSERT OR IGNORE INTO macro_repo_members (macro_repo_id, project_id, added_at)
1583
+ VALUES (?, ?, ?)`).run(e,t,Dn()),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Dn(),e)}function Xp(e,t){let n=h();n.prepare(`DELETE FROM macro_repo_members
1584
+ WHERE macro_repo_id = ? AND project_id = ?`).run(e,t),n.prepare("UPDATE macro_repos SET updated_at = ? WHERE id = ?").run(Dn(),e)}q();function Jp(e){let t=h(),n=new Date().toISOString(),s=t.prepare(`INSERT INTO bug_synthesis_results
1495
1585
  (scope, target_id, mode, model, output_markdown, input_tokens, output_tokens, context_summary, created_at, job_id)
1496
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.scope,e.target_id,e.mode,e.model,e.output_markdown,e.input_tokens,e.output_tokens,JSON.stringify(e.context_summary??{}),n,e.job_id??null),r=Number(s.lastInsertRowid);return Wo(r)}function cd(e){let t={};try{t=JSON.parse(e.context_summary)}catch{t={}}return{id:e.id,scope:e.scope,target_id:e.target_id,mode:e.mode,model:e.model,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,context_summary:t,created_at:e.created_at,job_id:e.job_id}}function Wo(e){let n=f().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return n?cd(n):null}function ld(e={}){let t=f(),n=[],s=[];e.scope&&(n.push("scope = ?"),s.push(e.scope)),e.target_id&&(n.push("target_id = ?"),s.push(e.target_id));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1586
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.scope,e.target_id,e.mode,e.model,e.output_markdown,e.input_tokens,e.output_tokens,JSON.stringify(e.context_summary??{}),n,e.job_id??null),r=Number(s.lastInsertRowid);return Ui(r)}function Gp(e){let t={};try{t=JSON.parse(e.context_summary)}catch{t={}}return{id:e.id,scope:e.scope,target_id:e.target_id,mode:e.mode,model:e.model,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,context_summary:t,created_at:e.created_at,job_id:e.job_id}}function Ui(e){let n=h().prepare("SELECT * FROM bug_synthesis_results WHERE id = ?").get(e);return n?Gp(n):null}function Yp(e={}){let t=h(),n=[],s=[];e.scope&&(n.push("scope = ?"),s.push(e.scope)),e.target_id&&(n.push("target_id = ?"),s.push(e.target_id));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.min(Math.max(1,e.limit??50),500);return t.prepare(`SELECT * FROM bug_synthesis_results
1497
1587
  ${r}
1498
1588
  ORDER BY created_at DESC
1499
- LIMIT ?`).all(...s,o).map(cd)}function ud(e){let n=f().prepare(`SELECT target_id, COUNT(*) AS n
1589
+ LIMIT ?`).all(...s,o).map(Gp)}function Kp(e){let n=h().prepare(`SELECT target_id, COUNT(*) AS n
1500
1590
  FROM bug_synthesis_results
1501
1591
  WHERE scope = ?
1502
- GROUP BY target_id`).all(e),s=new Map;for(let r of n)s.set(r.target_id,r.n);return s}function dd(e){f().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as hT,timingSafeEqual as ET}from"node:crypto";var bT=6e4,ST=new Set(["127.0.0.1","localhost"]),mn=new Map;function qo(){return Date.now()}function pd(){let e=qo();for(let[t,n]of mn)(n.expiresAt<=e||n.used)&&mn.delete(t)}function Ce(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!ST.has(r.hostname))return e.json({error:"forbidden: cross-origin launcher request rejected"},403)}catch{return e.json({error:"forbidden: invalid Origin header"},403)}let n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.json({error:"forbidden: Sec-Fetch-Site indicates cross-origin"},403):e.req.header("x-recall-launcher")!=="1"?e.json({error:"forbidden: missing X-Recall-Launcher header (this endpoint is callable only from the Recall web UI)"},403):null}function Xo(e){pd();let t=hT(32).toString("hex"),n=qo()+bT;return mn.set(t,{token:t,intent:e,expiresAt:n,used:!1}),{token:t,expiresAt:n}}function Jo(e){if(pd(),typeof e!="string"||e.length!==64)return null;let t;try{t=Buffer.from(e,"hex")}catch{return null}if(t.length!==32)return null;for(let n of mn.values()){if(n.used||n.expiresAt<=qo())continue;let s;try{s=Buffer.from(n.token,"hex")}catch{continue}if(s.length===t.length&&ET(s,t))return n.used=!0,mn.delete(n.token),n.intent}return null}import{existsSync as AT,mkdirSync as FO,readFileSync as xT,writeFileSync as PO}from"node:fs";import{homedir as NT}from"node:os";import{join as bd}from"node:path";import{z as Go}from"zod";import{appendFileSync as TT,existsSync as md,mkdirSync as yT,readFileSync as wT}from"node:fs";import{homedir as RT}from"node:os";import{join as gd}from"node:path";function _d(){return process.env.RECALL_HOME??gd(RT(),".recall")}function kT(){let e=_d();md(e)||yT(e,{recursive:!0})}function fd(){return gd(_d(),"launcher-audit.log")}function re(e){kT();let t={ts:new Date().toISOString(),...e};try{TT(fd(),JSON.stringify(t)+`
1503
- `,"utf8")}catch(n){console.error("[launcher-audit] failed to append:",n)}}function hd(e){let t=fd();if(!md(t))return{input_tokens:0,output_tokens:0,records_counted:0};let n=Date.now()-e,s=0,r=0,o=0,a;try{a=wT(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1504
- `)){if(!c.trim())continue;let u;try{u=JSON.parse(c)}catch{continue}if(u.kind!=="run-completed"&&u.kind!=="synth-completed")continue;let d=Date.parse(u.ts);!Number.isFinite(d)||d<n||(s+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:s,output_tokens:r,records_counted:o}}var OT=1440*60*1e3,LT=Go.object({dailyTokenBudget:Go.number().int().nonnegative().default(1e6),sessionCeiling:Go.number().int().positive().max(1e4).default(500)}),Yo={dailyTokenBudget:1e6,sessionCeiling:500};function CT(){return process.env.RECALL_HOME??bd(NT(),".recall")}function vT(){return bd(CT(),"config.json")}function IT(){let e=vT();if(!AT(e))return{};try{return JSON.parse(xT(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function zo(){let e=IT().launcher;if(!e)return{...Yo};let t=LT.safeParse({...Yo,...e});return t.success?t.data:{...Yo}}function Nt(){let e=zo(),t=hd(OT),n=t.input_tokens+t.output_tokens,s=Math.max(0,e.dailyTokenBudget-n);return{daily_token_budget:e.dailyTokenBudget,session_ceiling:e.sessionCeiling,spent_input_tokens_24h:t.input_tokens,spent_output_tokens_24h:t.output_tokens,spent_total_tokens_24h:n,remaining_tokens_24h:s}}function Ko(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function Vo(e){let t=e.mode==="root_cause"?800:2e3;if(e.scope==="cluster")return{estimated_input_tokens_max:5e3+Math.min(8,Math.max(1,e.member_session_count??1))*3e3,estimated_output_tokens_max:t};let n=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*n),estimated_output_tokens_max:t}}var Ed={pro:45,"max-5x":225,"max-20x":900};function jT(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var MT=4e3;function Sd(e){return Math.max(1,Math.ceil(e/MT))}function We(e,t){let n=jT(t),s=e*n,r=Object.keys(Ed).map(o=>{let a=s/Ed[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:n,per_plan:r,caveat:"Estimates use Anthropic\u2019s public approximate caps and assume a multiplier of ~1x for Haiku, ~5x for Sonnet, ~10x for Opus. Anthropic adjusts these limits without notice; actual consumption depends on session size. Treat as planning guidance, not a contract."}}import{randomUUID as DT}from"node:crypto";var Ot=new Map,gn=new Map,FT=300*1e3;function Ps(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function PT(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Ot.delete(e.jobId),gn.get(e.project)===e.jobId&&gn.delete(e.project)},FT),e.cleanupTimer.unref?.())}function Zo(e){return{jobId:e.jobId,project:e.project,model:e.model,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.progress.total,processed:e.progress.processed,ok:e.progress.ok,failed:e.progress.failed,skipped:e.progress.skipped,total_input_tokens:e.progress.total_input_tokens,total_output_tokens:e.progress.total_output_tokens,current_session_id:e.progress.current_session_id,error:e.error}}function Td(e){let t=dn(e.project);if(!t)return{error:`project "${e.project}" not found`};let n=gn.get(t.name);if(n){let m=Ot.get(n);if(m&&m.status==="running")return{jobId:n,reused:!0};gn.delete(t.name)}let s=DT(),r=e.model??tt,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:s,project:t.name,projectId:t.id,model:r,limit:o,force:a,status:"running",startedAt:u,endedAt:null,events:[],waiters:new Set,controller:c,progress:{total:0,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0},error:null,cleanupTimer:null};return Ot.set(s,d),gn.set(t.name,s),re({kind:"run-launched",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await sc({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:m=>{d.progress=m,Ps(d,"progress",m)},onResult:m=>{!m.ok&&!m.skipped&&Ps(d,"error",{session_id:m.session_id,reason:m.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),Ps(d,"done",Zo(d)),re({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens,sessions_processed:d.progress.processed})}catch(m){let h=m instanceof Error?m.message:String(m??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=h,Ps(d,"done",Zo(d)),re({kind:"run-failed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,reason:h,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{PT(d)}})(),{jobId:s,reused:!1}}async function*yd(e,t=0){let n=Ot.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function wd(e){let t=Ot.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Qo(e){let t=Ot.get(e);return t?Zo(t):null}B();import{randomUUID as $T}from"node:crypto";import{spawn as UT}from"node:child_process";Un();var Rd="claude-haiku-4-5-20251001",Ct=new Map,hn=new Map,BT=300*1e3;function kd(e){return`${e.scope}:${e.target_id}:${e.mode}`}function _n(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function HT(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Ct.delete(e.jobId);let t=kd(e.intent);hn.get(t)===e.jobId&&hn.delete(t)},BT),e.cleanupTimer.unref?.())}function Lt(e){return{jobId:e.jobId,scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,status:e.status,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,startedAt:e.startedAt,endedAt:e.endedAt,error:e.error,context_summary:e.context_summary}}function Ad(){return f().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1592
+ GROUP BY target_id`).all(e),s=new Map;for(let r of n)s.set(r.target_id,r.n);return s}function zp(e){h().prepare("DELETE FROM bug_synthesis_results WHERE id = ?").run(e)}import{randomBytes as b0,timingSafeEqual as S0}from"node:crypto";var y0=6e4,T0=new Set(["127.0.0.1","localhost"]),Pn=new Map;function Hi(){return Date.now()}function Vp(){let e=Hi();for(let[t,n]of Pn)(n.expiresAt<=e||n.used)&&Pn.delete(t)}function De(e){let t=e.req.header("origin")??"";if(t)try{let r=new URL(t);if(!T0.has(r.hostname))return e.json({error:"forbidden: cross-origin launcher request rejected"},403)}catch{return e.json({error:"forbidden: invalid Origin header"},403)}let n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.json({error:"forbidden: Sec-Fetch-Site indicates cross-origin"},403):e.req.header("x-recall-launcher")!=="1"?e.json({error:"forbidden: missing X-Recall-Launcher header (this endpoint is callable only from the Recall web UI)"},403):null}function Bi(e){Vp();let t=b0(32).toString("hex"),n=Hi()+y0;return Pn.set(t,{token:t,intent:e,expiresAt:n,used:!1}),{token:t,expiresAt:n}}function Wi(e){if(Vp(),typeof e!="string"||e.length!==64)return null;let t;try{t=Buffer.from(e,"hex")}catch{return null}if(t.length!==32)return null;for(let n of Pn.values()){if(n.used||n.expiresAt<=Hi())continue;let s;try{s=Buffer.from(n.token,"hex")}catch{continue}if(s.length===t.length&&S0(s,t))return n.used=!0,Pn.delete(n.token),n.intent}return null}import{existsSync as N0,mkdirSync as ZI,readFileSync as O0,writeFileSync as eM}from"node:fs";import{homedir as C0}from"node:os";import{join as rm}from"node:path";import{z as qi}from"zod";import{appendFileSync as w0,existsSync as Qp,mkdirSync as R0,readFileSync as k0}from"node:fs";import{homedir as A0}from"node:os";import{join as Zp}from"node:path";function em(){return process.env.RECALL_HOME??Zp(A0(),".recall")}function x0(){let e=em();Qp(e)||R0(e,{recursive:!0})}function tm(){return Zp(em(),"launcher-audit.log")}function oe(e){x0();let t={ts:new Date().toISOString(),...e};try{w0(tm(),JSON.stringify(t)+`
1593
+ `,"utf8")}catch(n){console.error("[launcher-audit] failed to append:",n)}}function nm(e){let t=tm();if(!Qp(t))return{input_tokens:0,output_tokens:0,records_counted:0};let n=Date.now()-e,s=0,r=0,o=0,a;try{a=k0(t,"utf8")}catch{return{input_tokens:0,output_tokens:0,records_counted:0}}for(let c of a.split(`
1594
+ `)){if(!c.trim())continue;let u;try{u=JSON.parse(c)}catch{continue}if(u.kind!=="run-completed"&&u.kind!=="synth-completed")continue;let d=Date.parse(u.ts);!Number.isFinite(d)||d<n||(s+=Number(u.input_tokens??0),r+=Number(u.output_tokens??0),o+=1)}return{input_tokens:s,output_tokens:r,records_counted:o}}var L0=1440*60*1e3,v0=qi.object({dailyTokenBudget:qi.number().int().nonnegative().default(1e6),sessionCeiling:qi.number().int().positive().max(1e4).default(500)}),Xi={dailyTokenBudget:1e6,sessionCeiling:500};function I0(){return process.env.RECALL_HOME??rm(C0(),".recall")}function M0(){return rm(I0(),"config.json")}function j0(){let e=M0();if(!N0(e))return{};try{return JSON.parse(O0(e,"utf8"))}catch(t){return console.error("[launcher-budget] failed to parse config.json, using defaults:",t),{}}}function Ji(){let e=j0().launcher;if(!e)return{...Xi};let t=v0.safeParse({...Xi,...e});return t.success?t.data:{...Xi}}function qt(){let e=Ji(),t=nm(L0),n=t.input_tokens+t.output_tokens,s=Math.max(0,e.dailyTokenBudget-n);return{daily_token_budget:e.dailyTokenBudget,session_ceiling:e.sessionCeiling,spent_input_tokens_24h:t.input_tokens,spent_output_tokens_24h:t.output_tokens,spent_total_tokens_24h:n,remaining_tokens_24h:s}}function Gi(e){return{estimated_input_tokens_max:e*2e4,estimated_output_tokens_max:e*1e3}}function Yi(e){let t=e.mode==="root_cause"?800:2e3;if(e.scope==="cluster")return{estimated_input_tokens_max:5e3+Math.min(8,Math.max(1,e.member_session_count??1))*3e3,estimated_output_tokens_max:t};let n=Math.max(1,e.cluster_count??1);return{estimated_input_tokens_max:Math.min(5e4,1e3*n),estimated_output_tokens_max:t}}var sm={pro:45,"max-5x":225,"max-20x":900};function D0(e){let t=e.toLowerCase();return t.includes("haiku")?1:t.includes("sonnet")?5:t.includes("opus")?10:5}var P0=4e3;function om(e){return Math.max(1,Math.ceil(e/P0))}function Ve(e,t){let n=D0(t),s=e*n,r=Object.keys(sm).map(o=>{let a=s/sm[o];return{plan:o,fraction:a,pct:Math.round(a*1e3)/10,would_exhaust_window:a>1}});return{model:t,model_multiplier:n,per_plan:r,caveat:"Estimates use Anthropic\u2019s public approximate caps and assume a multiplier of ~1x for Haiku, ~5x for Sonnet, ~10x for Opus. Anthropic adjusts these limits without notice; actual consumption depends on session size. Treat as planning guidance, not a contract."}}import{randomUUID as F0}from"node:crypto";var Xt=new Map,Fn=new Map,$0=300*1e3;function hr(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function U0(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Xt.delete(e.jobId),Fn.get(e.project)===e.jobId&&Fn.delete(e.project)},$0),e.cleanupTimer.unref?.())}function Ki(e){return{jobId:e.jobId,project:e.project,model:e.model,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total:e.progress.total,processed:e.progress.processed,ok:e.progress.ok,failed:e.progress.failed,skipped:e.progress.skipped,total_input_tokens:e.progress.total_input_tokens,total_output_tokens:e.progress.total_output_tokens,current_session_id:e.progress.current_session_id,error:e.error}}function im(e){let t=jn(e.project);if(!t)return{error:`project "${e.project}" not found`};let n=Fn.get(t.name);if(n){let p=Xt.get(n);if(p&&p.status==="running")return{jobId:n,reused:!0};Fn.delete(t.name)}let s=F0(),r=e.model??lt,o=Math.max(1,e.limit??200),a=e.force??!1,c=new AbortController,u=new Date().toISOString(),d={jobId:s,project:t.name,projectId:t.id,model:r,limit:o,force:a,status:"running",startedAt:u,endedAt:null,events:[],waiters:new Set,controller:c,progress:{total:0,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0},error:null,cleanupTimer:null};return Xt.set(s,d),Fn.set(t.name,s),oe({kind:"run-launched",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null}),(async()=>{await Promise.resolve();try{await zc({projectId:t.id,limit:o,force:a,model:r,signal:c.signal,onProgress:p=>{d.progress=p,hr(d,"progress",p)},onResult:p=>{!p.ok&&!p.skipped&&hr(d,"error",{session_id:p.session_id,reason:p.failed??"unknown"})}}),d.status=c.signal.aborted?"cancelled":"done",d.endedAt=new Date().toISOString(),hr(d,"done",Ki(d)),oe({kind:d.status==="cancelled"?"run-cancelled":"run-completed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens,sessions_processed:d.progress.processed})}catch(p){let f=p instanceof Error?p.message:String(p??"unknown error");d.status="failed",d.endedAt=new Date().toISOString(),d.error=f,hr(d,"done",Ki(d)),oe({kind:"run-failed",job_id:s,project:t.name,model:r,limit:o,origin:e.origin??null,reason:f,input_tokens:d.progress.total_input_tokens,output_tokens:d.progress.total_output_tokens})}finally{U0(d)}})(),{jobId:s,reused:!1}}async function*am(e,t=0){let n=Xt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function cm(e){let t=Xt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function zi(e){let t=Xt.get(e);return t?Ki(t):null}q();import{randomUUID as H0}from"node:crypto";import{spawn as B0}from"node:child_process";ds();var lm="claude-haiku-4-5-20251001",Gt=new Map,Hn=new Map,W0=300*1e3;function um(e){return`${e.scope}:${e.target_id}:${e.mode}`}function $n(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function q0(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Gt.delete(e.jobId);let t=um(e.intent);Hn.get(t)===e.jobId&&Hn.delete(t)},W0),e.cleanupTimer.unref?.())}function Jt(e){return{jobId:e.jobId,scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,status:e.status,output_markdown:e.output_markdown,input_tokens:e.input_tokens,output_tokens:e.output_tokens,startedAt:e.startedAt,endedAt:e.endedAt,error:e.error,context_summary:e.context_summary}}function dm(){return h().prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1505
1595
  oi.bug_signatures
1506
1596
  FROM session_output_index oi
1507
1597
  JOIN sessions s ON s.id = oi.session_id
1508
1598
  JOIN projects p ON p.id = s.project_id
1509
1599
  WHERE oi.bug_signatures IS NOT NULL
1510
- AND oi.bug_signatures != '[]'`).all()}function xd(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function WT(e){let t=Ad(),n=[];for(let a of t)for(let c of xd(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&n.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(n.length===0)return null;let s=n[0],r=Array.from(new Set(n.map(a=>a.session_id))),o=Array.from(new Set(n.map(a=>a.project))).sort();return{message_hash:s.sig.message_hash??null,error_type:s.sig.error_type??null,snippet:(s.sig.snippet??"").slice(0,400),file:s.sig.file??null,occurrence_count:n.length,projects:o,member_session_ids:r}}function qT(e){let t=Ad().filter(o=>o.project===e),n=new Map;for(let o of t)for(let a of xd(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=n.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):n.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let s=new Map;try{let o=Array.from(n.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=f(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1600
+ AND oi.bug_signatures != '[]'`).all()}function pm(e){try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function X0(e){let t=dm(),n=[];for(let a of t)for(let c of pm(a.bug_signatures)){let u=c.message_hash??`nohash:${(c.snippet??"").slice(0,64)}`;(c.message_hash===e||u===e)&&n.push({sig:c,session_id:a.session_id,project:a.project,auto_title:a.auto_title})}if(n.length===0)return null;let s=n[0],r=Array.from(new Set(n.map(a=>a.session_id))),o=Array.from(new Set(n.map(a=>a.project))).sort();return{message_hash:s.sig.message_hash??null,error_type:s.sig.error_type??null,snippet:(s.sig.snippet??"").slice(0,400),file:s.sig.file??null,occurrence_count:n.length,projects:o,member_session_ids:r}}function J0(e){let t=dm().filter(o=>o.project===e),n=new Map;for(let o of t)for(let a of pm(o.bug_signatures)){let c=a.message_hash??`nohash:${(a.snippet??"").slice(0,64)}`,u=n.get(c);u?(u.sigs.push(a),u.sessions.add(o.session_id)):n.set(c,{sigs:[a],first:a,sessions:new Set([o.session_id])})}let s=new Map;try{let o=Array.from(n.keys()).filter(a=>!a.startsWith("nohash:"));if(o.length>0){let a=h(),c=o.map(()=>"?").join(","),u=a.prepare(`SELECT message_hash, fix_summary
1511
1601
  FROM bug_signature_resolutions
1512
1602
  WHERE message_hash IN (${c})
1513
- AND unresolved_at IS NULL`).all(...o);for(let d of u)s.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of n){let c=a.first.message_hash??null,u=c?s.get(c)??null:null;r.push({cluster_id:c??o,error_type:a.first.error_type??null,snippet:(a.first.snippet??"").slice(0,200),file:a.first.file??null,occurrence_count:a.sessions.size,resolved:u!==null,fix_summary:u?.fix_summary??null})}return r.sort((o,a)=>a.occurrence_count-o.occurrence_count),r}function XT(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function JT(e,t){let n=f(),s=XT(t),r=[];for(let o of e.slice(0,8)){let a=n.prepare(`SELECT s.id, p.name AS project, s.auto_title
1603
+ AND unresolved_at IS NULL`).all(...o);for(let d of u)s.set(d.message_hash,{fix_summary:d.fix_summary})}}catch{}let r=[];for(let[o,a]of n){let c=a.first.message_hash??null,u=c?s.get(c)??null:null;r.push({cluster_id:c??o,error_type:a.first.error_type??null,snippet:(a.first.snippet??"").slice(0,200),file:a.first.file??null,occurrence_count:a.sessions.size,resolved:u!==null,fix_summary:u?.fix_summary??null})}return r.sort((o,a)=>a.occurrence_count-o.occurrence_count),r}function G0(e){let t=e.replace(/\s+/g," ").trim();return t.length===0?"":t.slice(0,Math.min(30,t.length))}function Y0(e,t){let n=h(),s=G0(t),r=[];for(let o of e.slice(0,8)){let a=n.prepare(`SELECT s.id, p.name AS project, s.auto_title
1514
1604
  FROM sessions s
1515
1605
  JOIN projects p ON p.id = s.project_id
1516
1606
  WHERE s.id = ?`).get(o);if(!a)continue;let c=[];if(s.length>0){let u=n.prepare(`SELECT rowid FROM messages
@@ -1518,13 +1608,13 @@ ${n}`}function XS(e){return Le(e)?.auto_title_source??null}async function JS(e){
1518
1608
  AND is_sidechain = 0
1519
1609
  AND content_text LIKE ?
1520
1610
  ORDER BY rowid
1521
- LIMIT 5`).all(o,`%${s}%`),d=new Set,m=0;for(let h of u){let b=n.prepare(`SELECT rowid, role, content_text FROM messages
1611
+ LIMIT 5`).all(o,`%${s}%`),d=new Set,p=0;for(let f of u){let S=n.prepare(`SELECT rowid, role, content_text FROM messages
1522
1612
  WHERE session_id = ?
1523
1613
  AND is_sidechain = 0
1524
1614
  AND rowid BETWEEN ? AND ?
1525
- ORDER BY rowid`).all(o,h.rowid-2,h.rowid+2);for(let S of b){if(d.has(S.rowid))continue;d.add(S.rowid);let T=(S.content_text??"").slice(0,800);if(m+T.length>4e3)break;m+=T.length,c.push({role:S.role??"unknown",content:T})}if(m>=4e3)break}}r.push({session_id:o,short_id:o.slice(0,8),project:a.project,auto_title:a.auto_title,excerpts:c})}return r}var GT="You are analyzing extracted bug findings from a developer's past Claude Code sessions. You are NOT being asked to fix code. You are being asked to synthesize patterns, identify likely root causes, or prioritize concerns based ONLY on the structured findings you are given. Output Markdown only, no preamble, no apologies, no questions.";function YT(e,t){let n=[];n.push("[CONTEXT]"),n.push("Bug fingerprint:"),n.push(`- error_type: ${e.error_type??"unknown"}`),n.push(`- description: ${e.snippet||"(no snippet)"}`),n.push(`- file: ${e.file??"(no file recorded)"}`),n.push(`- occurrences: ${e.occurrence_count} sessions`),n.push(`- projects: ${e.projects.join(", ")}`),n.push(`- finding_id: ${e.message_hash??"(no hash)"}`),n.push(""),n.push("Member session excerpts:");for(let s of t){let r=s.auto_title??"(untitled)";if(n.push(`=== Session ${s.short_id} | ${s.project} | "${r}" ===`),s.excerpts.length===0)n.push("(no surrounding messages found for this snippet)");else for(let o of s.excerpts)n.push(`${o.role}: ${o.content}`);n.push("")}return n.join(`
1526
- `)}function zT(e,t,n){let s=[];s.push("[CONTEXT]"),s.push(`Project: ${e}`),s.push(`Total clusters: ${n}`),s.push(""),s.push("Clusters (sorted by occurrence_count desc):");for(let r of t)s.push(`- cluster_id: ${r.cluster_id}`),s.push(` error_type: ${r.error_type??"unknown"}`),s.push(` snippet: ${r.snippet||"(none)"}`),r.file&&s.push(` file: ${r.file}`),s.push(` occurrence_count: ${r.occurrence_count}`),s.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&s.push(` fix_summary: ${r.fix_summary}`),s.push("");return t.length<n&&s.push(`(Showing top ${t.length} of ${n} clusters by occurrence.)`),s.join(`
1527
- `)}var KT=`[TASK]
1615
+ ORDER BY rowid`).all(o,f.rowid-2,f.rowid+2);for(let y of S){if(d.has(y.rowid))continue;d.add(y.rowid);let b=(y.content_text??"").slice(0,800);if(p+b.length>4e3)break;p+=b.length,c.push({role:y.role??"unknown",content:b})}if(p>=4e3)break}}r.push({session_id:o,short_id:o.slice(0,8),project:a.project,auto_title:a.auto_title,excerpts:c})}return r}var K0="You are analyzing extracted bug findings from a developer's past Claude Code sessions. You are NOT being asked to fix code. You are being asked to synthesize patterns, identify likely root causes, or prioritize concerns based ONLY on the structured findings you are given. Output Markdown only, no preamble, no apologies, no questions.";function z0(e,t){let n=[];n.push("[CONTEXT]"),n.push("Bug fingerprint:"),n.push(`- error_type: ${e.error_type??"unknown"}`),n.push(`- description: ${e.snippet||"(no snippet)"}`),n.push(`- file: ${e.file??"(no file recorded)"}`),n.push(`- occurrences: ${e.occurrence_count} sessions`),n.push(`- projects: ${e.projects.join(", ")}`),n.push(`- finding_id: ${e.message_hash??"(no hash)"}`),n.push(""),n.push("Member session excerpts:");for(let s of t){let r=s.auto_title??"(untitled)";if(n.push(`=== Session ${s.short_id} | ${s.project} | "${r}" ===`),s.excerpts.length===0)n.push("(no surrounding messages found for this snippet)");else for(let o of s.excerpts)n.push(`${o.role}: ${o.content}`);n.push("")}return n.join(`
1616
+ `)}function V0(e,t,n){let s=[];s.push("[CONTEXT]"),s.push(`Project: ${e}`),s.push(`Total clusters: ${n}`),s.push(""),s.push("Clusters (sorted by occurrence_count desc):");for(let r of t)s.push(`- cluster_id: ${r.cluster_id}`),s.push(` error_type: ${r.error_type??"unknown"}`),s.push(` snippet: ${r.snippet||"(none)"}`),r.file&&s.push(` file: ${r.file}`),s.push(` occurrence_count: ${r.occurrence_count}`),s.push(` resolved: ${r.resolved?"true":"false"}`),r.fix_summary&&s.push(` fix_summary: ${r.fix_summary}`),s.push("");return t.length<n&&s.push(`(Showing top ${t.length} of ${n} clusters by occurrence.)`),s.join(`
1617
+ `)}var Q0=`[TASK]
1528
1618
  Output a Markdown synopsis with these sections:
1529
1619
 
1530
1620
  ## What this bug is
@@ -1546,7 +1636,7 @@ prefer "consider <pattern>" over "do <action>" unless the evidence
1546
1636
  is overwhelming.
1547
1637
 
1548
1638
  ## Confidence
1549
- One of: high / medium / low. With one sentence justifying the level.`,VT=`[TASK]
1639
+ One of: high / medium / low. With one sentence justifying the level.`,Z0=`[TASK]
1550
1640
  Output a Markdown response with these sections:
1551
1641
 
1552
1642
  ## Most likely root cause
@@ -1563,7 +1653,7 @@ list is acceptable but explicitly say "(none found)".
1563
1653
  At most 2 alternatives, each with one sentence why it is less likely.
1564
1654
 
1565
1655
  ## Confidence
1566
- high / medium / low + one-sentence justification.`,ZT=`[TASK]
1656
+ high / medium / low + one-sentence justification.`,eR=`[TASK]
1567
1657
  Output a Markdown response with these sections:
1568
1658
 
1569
1659
  ## Top 5 recurring concerns
@@ -1581,20 +1671,20 @@ Clusters that look small + high-confidence-fix. Empty list is fine.
1581
1671
  Clusters that suggest deeper issues (multiple files, repeated patterns).
1582
1672
 
1583
1673
  ## Confidence
1584
- high / medium / low.`;function QT(e,t){let n;return e.scope==="cluster"?n=e.mode==="root_cause"?VT:KT:n=ZT,[GT,"",t,"",n].join(`
1585
- `)}var ey=null;var fn;function ty(){return fn!==void 0||(fn=gt("claude")??"claude"),fn}function ny(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
1674
+ high / medium / low.`;function tR(e,t){let n;return e.scope==="cluster"?n=e.mode==="root_cause"?Z0:Q0:n=eR,[K0,"",t,"",n].join(`
1675
+ `)}var nR=null;var Un;function sR(){return Un!==void 0||(Un=kt("claude")??"claude"),Un}function rR(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
1586
1676
  `);for(;s!==-1;){let r=t.slice(0,s);t=t.slice(s+1),r.length>0&&e(r),s=t.indexOf(`
1587
- `)}}}function sy(e){return new Promise(t=>{let n=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],s="",r=0,o=0,a=null,c=ty(),u=UT(c,n,{stdio:["ignore","pipe","pipe"],shell:$n(c)||process.platform==="win32"&&fn==="claude"}),d=()=>{try{u.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",d,{once:!0});let h=ny(S=>{let T=S.trim();if(!T.startsWith("{"))return;let R;try{R=JSON.parse(T)}catch{return}if(!R||typeof R!="object")return;let O=R;if(O.type==="assistant"&&O.message?.content){for(let x of O.message.content)x?.type==="text"&&typeof x.text=="string"&&(s+=x.text,e.onPartial(x.text,s));let L=O.message?.usage;L&&(typeof L.input_tokens=="number"&&(r=Math.max(r,L.input_tokens)),typeof L.output_tokens=="number"&&(o=Math.max(o,L.output_tokens)))}});u.stdout.on("data",S=>h(S)),u.stderr.on("data",S=>{let T=S.toString("utf8");T.trim()&&(a=(a??"")+T)});let b=setTimeout(()=>{try{u.kill("SIGKILL")}catch{}},1800*1e3);u.on("close",S=>{clearTimeout(b),e.signal.removeEventListener("abort",d);let T=S===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${S}`);t({output_markdown:s,input_tokens:r,output_tokens:o,error:T})}),u.on("error",S=>{clearTimeout(b),e.signal.removeEventListener("abort",d),t({output_markdown:s,input_tokens:r,output_tokens:o,error:S instanceof Error?S.message:String(S)})})})}function $s(e){if(e.scope==="cluster"){let r=WT(e.target_id);return r?{cluster:r,context_summary:{cluster_count:1,session_count:r.member_session_ids.length,findings_count:r.occurrence_count}}:null}let t=qT(e.target_id);if(t.length===0)return null;let n=t.slice(0,30),s=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:n,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:s}}}function Nd(e){let t=$s(e.intent);if(!t)return{error:e.intent.scope==="cluster"?`cluster "${e.intent.target_id}" not found in any extracted findings`:`project "${e.intent.target_id}" has no extracted findings to synthesize`};let n=kd(e.intent),s=hn.get(n);if(s){let u=Ct.get(s);if(u&&u.status==="running")return{jobId:s,reused:!0};hn.delete(n)}let r=$T(),o=new AbortController,a=new Date().toISOString(),c={jobId:r,intent:e.intent,status:"running",startedAt:a,endedAt:null,events:[],waiters:new Set,controller:o,output_markdown:"",input_tokens:0,output_tokens:0,error:null,context_summary:t.context_summary,cleanupTimer:null};return Ct.set(r,c),hn.set(n,r),re({kind:"synth-launched",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:`${e.intent.scope}/${e.intent.mode}/${e.intent.target_id}`}),(async()=>{await Promise.resolve();try{let u;if(e.intent.scope==="cluster"&&t.cluster){let b=JT(t.cluster.member_session_ids,t.cluster.snippet);u=YT(t.cluster,b)}else if(e.intent.scope==="project"&&t.project_clusters)u=zT(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=QT(e.intent,u),h=await(ey??sy)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(b,S)=>{c.output_markdown=S,_n(c,"partial",Lt(c))}});if(c.output_markdown=h.output_markdown,c.input_tokens=h.input_tokens,c.output_tokens=h.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),_n(c,"done",Lt(c)),re({kind:"synth-cancelled",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else if(h.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=h.error,_n(c,"done",Lt(c)),re({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:h.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),_n(c,"done",Lt(c)),re({kind:"synth-completed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});try{ad({scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,output_markdown:c.output_markdown,input_tokens:c.input_tokens,output_tokens:c.output_tokens,job_id:r})}catch(b){console.error("[synthesize-jobs] failed to persist synthesis result:",b)}}}catch(u){let d=u instanceof Error?u.message:String(u??"unknown error");c.status="failed",c.endedAt=new Date().toISOString(),c.error=d,_n(c,"done",Lt(c)),re({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:d,input_tokens:c.input_tokens,output_tokens:c.output_tokens})}finally{HT(c)}})(),{jobId:r,reused:!1}}async function*Od(e,t=0){let n=Ct.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Ld(e){let t=Ct.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function ei(e){let t=Ct.get(e);return t?Lt(t):null}import{randomUUID as fy}from"node:crypto";B();Kn();import{randomUUID as py}from"node:crypto";B();var Cd=10,vd=20;function ry(e){if(!e)return!1;let t=e.split(`
1588
- `);if(t.length<4)return!1;let n=0,s=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?n++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&s++;return n/t.length>.7||s/t.length>.5}function oy(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let n=new Float32Array(t),s=new Float32Array(e.buffer,e.byteOffset,t);return n.set(s),n}function Id(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];if(t<=0)return!1;let n=1/Math.sqrt(t);for(let s=0;s<e.length;s++)e[s]*=n;return!0}function ti(e){if(e.length===0)return null;let t=e[0].length,n=new Float32Array(t);for(let s of e)if(s.length===t)for(let r=0;r<t;r++)n[r]+=s[r];for(let s=0;s<t;s++)n[s]/=e.length;return Id(n)?n:null}function jd(e){let t=new Map;if(e.length===0)return t;let n=f(),s=e.map(()=>"?").join(","),r=[];try{r=n.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1677
+ `)}}}function oR(e){return new Promise(t=>{let n=["-p",e.prompt,"--output-format","stream-json","--verbose","--allowedTools","","--permission-mode","bypassPermissions","--model",e.model],s="",r=0,o=0,a=null,c=sR(),u=B0(c,n,{stdio:["ignore","pipe","pipe"],shell:us(c)||process.platform==="win32"&&Un==="claude"}),d=()=>{try{u.kill("SIGTERM")}catch{}};e.signal.addEventListener("abort",d,{once:!0});let f=rR(y=>{let b=y.trim();if(!b.startsWith("{"))return;let R;try{R=JSON.parse(b)}catch{return}if(!R||typeof R!="object")return;let k=R;if(k.type==="assistant"&&k.message?.content){for(let P of k.message.content)P?.type==="text"&&typeof P.text=="string"&&(s+=P.text,e.onPartial(P.text,s));let M=k.message?.usage;M&&(typeof M.input_tokens=="number"&&(r=Math.max(r,M.input_tokens)),typeof M.output_tokens=="number"&&(o=Math.max(o,M.output_tokens)))}});u.stdout.on("data",y=>f(y)),u.stderr.on("data",y=>{let b=y.toString("utf8");b.trim()&&(a=(a??"")+b)});let S=setTimeout(()=>{try{u.kill("SIGKILL")}catch{}},1800*1e3);u.on("close",y=>{clearTimeout(S),e.signal.removeEventListener("abort",d);let b=y===0?null:a&&a.trim()||(e.signal.aborted?null:`claude CLI exited with code ${y}`);t({output_markdown:s,input_tokens:r,output_tokens:o,error:b})}),u.on("error",y=>{clearTimeout(S),e.signal.removeEventListener("abort",d),t({output_markdown:s,input_tokens:r,output_tokens:o,error:y instanceof Error?y.message:String(y)})})})}function Er(e){if(e.scope==="cluster"){let r=X0(e.target_id);return r?{cluster:r,context_summary:{cluster_count:1,session_count:r.member_session_ids.length,findings_count:r.occurrence_count}}:null}let t=J0(e.target_id);if(t.length===0)return null;let n=t.slice(0,30),s=t.reduce((r,o)=>r+o.occurrence_count,0);return{project_clusters:n,total_project_clusters:t.length,context_summary:{cluster_count:t.length,session_count:0,findings_count:s}}}function mm(e){let t=Er(e.intent);if(!t)return{error:e.intent.scope==="cluster"?`cluster "${e.intent.target_id}" not found in any extracted findings`:`project "${e.intent.target_id}" has no extracted findings to synthesize`};let n=um(e.intent),s=Hn.get(n);if(s){let u=Gt.get(s);if(u&&u.status==="running")return{jobId:s,reused:!0};Hn.delete(n)}let r=H0(),o=new AbortController,a=new Date().toISOString(),c={jobId:r,intent:e.intent,status:"running",startedAt:a,endedAt:null,events:[],waiters:new Set,controller:o,output_markdown:"",input_tokens:0,output_tokens:0,error:null,context_summary:t.context_summary,cleanupTimer:null};return Gt.set(r,c),Hn.set(n,r),oe({kind:"synth-launched",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:`${e.intent.scope}/${e.intent.mode}/${e.intent.target_id}`}),(async()=>{await Promise.resolve();try{let u;if(e.intent.scope==="cluster"&&t.cluster){let S=Y0(t.cluster.member_session_ids,t.cluster.snippet);u=z0(t.cluster,S)}else if(e.intent.scope==="project"&&t.project_clusters)u=V0(e.intent.target_id,t.project_clusters,t.total_project_clusters??t.project_clusters.length);else throw new Error("inconsistent prepared context");let d=tR(e.intent,u),f=await(nR??oR)({prompt:d,model:e.intent.model,signal:o.signal,onPartial:(S,y)=>{c.output_markdown=y,$n(c,"partial",Jt(c))}});if(c.output_markdown=f.output_markdown,c.input_tokens=f.input_tokens,c.output_tokens=f.output_tokens,o.signal.aborted)c.status="cancelled",c.endedAt=new Date().toISOString(),$n(c,"done",Jt(c)),oe({kind:"synth-cancelled",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else if(f.error)c.status="failed",c.endedAt=new Date().toISOString(),c.error=f.error,$n(c,"done",Jt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:f.error,input_tokens:c.input_tokens,output_tokens:c.output_tokens});else{c.status="done",c.endedAt=new Date().toISOString(),$n(c,"done",Jt(c)),oe({kind:"synth-completed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,input_tokens:c.input_tokens,output_tokens:c.output_tokens});try{Jp({scope:e.intent.scope,target_id:e.intent.target_id,mode:e.intent.mode,model:e.intent.model,output_markdown:c.output_markdown,input_tokens:c.input_tokens,output_tokens:c.output_tokens,job_id:r})}catch(S){console.error("[synthesize-jobs] failed to persist synthesis result:",S)}}}catch(u){let d=u instanceof Error?u.message:String(u??"unknown error");c.status="failed",c.endedAt=new Date().toISOString(),c.error=d,$n(c,"done",Jt(c)),oe({kind:"synth-failed",job_id:r,project:e.intent.scope==="project"?e.intent.target_id:null,model:e.intent.model,limit:null,origin:e.origin??null,reason:d,input_tokens:c.input_tokens,output_tokens:c.output_tokens})}finally{q0(c)}})(),{jobId:r,reused:!1}}async function*gm(e,t=0){let n=Gt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function fm(e){let t=Gt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function Vi(e){let t=Gt.get(e);return t?Jt(t):null}import{randomUUID as ER}from"node:crypto";q();ys();import{randomUUID as gR}from"node:crypto";q();var _m=10,hm=20;function iR(e){if(!e)return!1;let t=e.split(`
1678
+ `);if(t.length<4)return!1;let n=0,s=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?n++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&s++;return n/t.length>.7||s/t.length>.5}function aR(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let n=new Float32Array(t),s=new Float32Array(e.buffer,e.byteOffset,t);return n.set(s),n}function Em(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];if(t<=0)return!1;let n=1/Math.sqrt(t);for(let s=0;s<e.length;s++)e[s]*=n;return!0}function Qi(e){if(e.length===0)return null;let t=e[0].length,n=new Float32Array(t);for(let s of e)if(s.length===t)for(let r=0;r<t;r++)n[r]+=s[r];for(let s=0;s<t;s++)n[s]/=e.length;return Em(n)?n:null}function bm(e){let t=new Map;if(e.length===0)return t;let n=h(),s=e.map(()=>"?").join(","),r=[];try{r=n.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1589
1679
  cm.text AS text, v.embedding AS embedding
1590
1680
  FROM chunk_meta cm
1591
1681
  JOIN vec_chunks v ON v.rowid = cm.rowid
1592
1682
  WHERE cm.stale = 0
1593
1683
  AND cm.session_id IN (${s})
1594
- ORDER BY cm.session_id, cm.rowid ASC`).all(...e)}catch{return t}if(r.length===0)return t;let o=new Map;for(let a of r){let c=o.get(a.session_id);c||(c=[],o.set(a.session_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(A=>!ry(A.text)),d=u.length>0?u:c,m=[];for(let A of d){let $=oy(A.embedding);$&&Id($)&&m.push($)}if(m.length===0)continue;let h=Math.min(Cd,m.length),b=Math.max(0,m.length-Cd),S=m.slice(0,h),T=m.slice(b),R=ti(S),O=ti(T),L=ti(m),x=new Map;for(let A=0;A<S.length&&x.size<vd;A++)x.set(A,S[A]);for(let A=0;A<T.length&&x.size<vd;A++)x.set(b+A,T[A]);let F=Array.from(x.values());t.set(a,{session_id:a,full_mean:L,head_pool:R,tail_pool:O,sample_chunks:F})}return t}function iy(e,t){if(e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function Md(e,t=.8){let n=new Map,s=[],r=[];for(let[d,m]of e)m.full_mean&&(s.push(d),r.push(m.full_mean));if(s.length===0)return n;let o=new Int32Array(s.length);for(let d=0;d<o.length;d++)o[d]=d;let a=d=>{let m=d;for(;o[m]!==m;)m=o[m];let h=d;for(;o[h]!==m;){let b=o[h];o[h]=m,h=b}return m},c=(d,m)=>{let h=a(d),b=a(m);h!==b&&(o[h]=b)};for(let d=0;d<s.length;d++)for(let m=d+1;m<s.length;m++)iy(r[d],r[m])>=t&&c(d,m);let u=new Map;for(let d=0;d<s.length;d++){let m=a(d),h=u.get(m);h===void 0&&(h=u.size,u.set(m,h)),n.set(s[d],h)}return n}var Pe={lo:.4,hi:.7},vt="claude-haiku-4-5-20251001",It=50,Dd=1500,Fd=50,ay={same_workflow:.15,unrelated:-.2,unsure:0};function Pd(e,t,n=Pe){if(n.lo>n.hi)throw new Error(`borderline band invalid: lo=${n.lo} > hi=${n.hi}`);let s=[];for(let r of e){if(r.confidence<n.lo||r.confidence>n.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||s.push({parent:o,child:a,step1:r})}return s.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),s}function $d(e,t=Pe){let n=0;for(let s of e)s.confidence>=t.lo&&s.confidence<=t.hi&&n++;return n}function cy(e,t){let n=e.replace(/\s+/g," ").trim();return n.length>t?n.slice(0,t-1)+"\u2026":n}function ly(e,t){if(e===null)return"unknown";let n=t-e;if(n<0)return"overlap";let s=Math.round(n/6e4);if(s<60)return`${s}m`;let r=Math.round(n/36e5);return r<24?`${r}h`:`${Math.round(n/864e5)}d`}function uy(e){let n=e.parent.recent_user_messages,s=e.child.recent_user_messages,r=n.slice(0,3),o=n.length>3?n.slice(-3):[],a=s.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(m=>` - ${cy(m,500)}`).join(`
1595
- `),u=ly(e.parent.ended_at_ms??e.parent.started_at_ms,e.child.started_at_ms);return["You are evaluating whether two Claude Code sessions belong to the same continuous workflow.","","A deterministic scorer rated this pair in the borderline band. Its signals:",e.step1.reasons.length===0?" (no signals fired strongly)":e.step1.reasons.map(d=>` - ${d}`).join(`
1684
+ ORDER BY cm.session_id, cm.rowid ASC`).all(...e)}catch{return t}if(r.length===0)return t;let o=new Map;for(let a of r){let c=o.get(a.session_id);c||(c=[],o.set(a.session_id,c)),c.push(a)}for(let[a,c]of o){let u=c.filter(D=>!iR(D.text)),d=u.length>0?u:c,p=[];for(let D of d){let v=aR(D.embedding);v&&Em(v)&&p.push(v)}if(p.length===0)continue;let f=Math.min(_m,p.length),S=Math.max(0,p.length-_m),y=p.slice(0,f),b=p.slice(S),R=Qi(y),k=Qi(b),M=Qi(p),P=new Map;for(let D=0;D<y.length&&P.size<hm;D++)P.set(D,y[D]);for(let D=0;D<b.length&&P.size<hm;D++)P.set(S+D,b[D]);let G=Array.from(P.values());t.set(a,{session_id:a,full_mean:M,head_pool:R,tail_pool:k,sample_chunks:G})}return t}function cR(e,t){if(e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function Sm(e,t=.8){let n=new Map,s=[],r=[];for(let[d,p]of e)p.full_mean&&(s.push(d),r.push(p.full_mean));if(s.length===0)return n;let o=new Int32Array(s.length);for(let d=0;d<o.length;d++)o[d]=d;let a=d=>{let p=d;for(;o[p]!==p;)p=o[p];let f=d;for(;o[f]!==p;){let S=o[f];o[f]=p,f=S}return p},c=(d,p)=>{let f=a(d),S=a(p);f!==S&&(o[f]=S)};for(let d=0;d<s.length;d++)for(let p=d+1;p<s.length;p++)cR(r[d],r[p])>=t&&c(d,p);let u=new Map;for(let d=0;d<s.length;d++){let p=a(d),f=u.get(p);f===void 0&&(f=u.size,u.set(p,f)),n.set(s[d],f)}return n}var We={lo:.4,hi:.7},Yt="claude-haiku-4-5-20251001",Kt=50,ym=1500,Tm=50,lR={same_workflow:.15,unrelated:-.2,unsure:0};function wm(e,t,n=We){if(n.lo>n.hi)throw new Error(`borderline band invalid: lo=${n.lo} > hi=${n.hi}`);let s=[];for(let r of e){if(r.confidence<n.lo||r.confidence>n.hi)continue;let o=t.get(r.parent_id),a=t.get(r.child_id);!o||!a||s.push({parent:o,child:a,step1:r})}return s.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),s}function Rm(e,t=We){let n=0;for(let s of e)s.confidence>=t.lo&&s.confidence<=t.hi&&n++;return n}function uR(e,t){let n=e.replace(/\s+/g," ").trim();return n.length>t?n.slice(0,t-1)+"\u2026":n}function dR(e,t){if(e===null)return"unknown";let n=t-e;if(n<0)return"overlap";let s=Math.round(n/6e4);if(s<60)return`${s}m`;let r=Math.round(n/36e5);return r<24?`${r}h`:`${Math.round(n/864e5)}d`}function pR(e){let n=e.parent.recent_user_messages,s=e.child.recent_user_messages,r=n.slice(0,3),o=n.length>3?n.slice(-3):[],a=s.slice(0,3),c=d=>d.length===0?" (none captured)":d.map(p=>` - ${uR(p,500)}`).join(`
1685
+ `),u=dR(e.parent.ended_at_ms??e.parent.started_at_ms,e.child.started_at_ms);return["You are evaluating whether two Claude Code sessions belong to the same continuous workflow.","","A deterministic scorer rated this pair in the borderline band. Its signals:",e.step1.reasons.length===0?" (no signals fired strongly)":e.step1.reasons.map(d=>` - ${d}`).join(`
1596
1686
  `),` step1_confidence: ${e.step1.confidence.toFixed(2)}`,"","PARENT SESSION","First user messages:",c(r),"Last user messages:",c(o),"",`CHILD SESSION (gap from parent: ${u})`,"First user messages:",c(a),"","Reply with EXACTLY one JSON object on a single line, no prose, no markdown:",'{"verdict":"same_workflow"|"unrelated"|"unsure","reason":"<one short sentence>"}',"","Guidance:",'- "same_workflow" = the child is clearly continuing what the parent was doing.','- "unrelated" = different problem, different files, different intent.','- "unsure" = could go either way; do not force a verdict.'].join(`
1597
- `)}function dy(e){if(!e)return null;let t=e.trim();if(!t)return null;let n=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(n=u.result.trim())}catch{}let s=n.match(/\{[\s\S]*\}/);if(!s)return null;let r;try{r=JSON.parse(s[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,a=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(a!=="same_workflow"&&a!=="unrelated"&&a!=="unsure")return null;let c=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:a,reason:c,delta:ay[a]}}async function Ud(e,t={}){if(t.signal?.aborted)return null;let n=t.model??vt,s=uy(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(ye(),et));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(s,{model:n}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:dy(o.stdout)}function Bd(e){let t=[];for(let n of e.proposals){let s=`${n.parent_id}::${n.child_id}`,r=e.rescored.get(s);if(!r){t.push(n);continue}let o=Math.max(0,Math.min(1,n.confidence+r.delta));if(o<e.applyThreshold)continue;let a=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...n,confidence:o,reasons:[...n.reasons,a]})}return t}function Hd(e){return`${e.parent_id}::${e.child_id}`}async function my(e){if(e.signal?.aborted)return null;let t,n;try{({spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(ye(),et)))}catch{return null}if(!n())return null;let s=[];for(let o of e.sessionIds){let a=e.rowById.get(o);if(!a)continue;let c=a.alias||a.auto_title||(a.first_user_message?a.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");s.push(`- ${c}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1687
+ `)}function mR(e){if(!e)return null;let t=e.trim();if(!t)return null;let n=t;try{let u=JSON.parse(t);typeof u.result=="string"&&(n=u.result.trim())}catch{}let s=n.match(/\{[\s\S]*\}/);if(!s)return null;let r;try{r=JSON.parse(s[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,a=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(a!=="same_workflow"&&a!=="unrelated"&&a!=="unsure")return null;let c=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:a,reason:c,delta:lR[a]}}async function km(e,t={}){if(t.signal?.aborted)return null;let n=t.model??Yt,s=pR(e),r=t.spawn;if(!r)try{let a=await Promise.resolve().then(()=>(Te(),ct));if(!a.isClaudeCliAvailable())return null;r=(c,u)=>a.spawnClaudePrompt(c,[],u)}catch{return null}let o;try{o=await Promise.race([r(s,{model:n}),new Promise(a=>{t.signal&&t.signal.addEventListener("abort",()=>a({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:mR(o.stdout)}function Am(e){let t=[];for(let n of e.proposals){let s=`${n.parent_id}::${n.child_id}`,r=e.rescored.get(s);if(!r){t.push(n);continue}let o=Math.max(0,Math.min(1,n.confidence+r.delta));if(o<e.applyThreshold)continue;let a=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...n,confidence:o,reasons:[...n.reasons,a]})}return t}function xm(e){return`${e.parent_id}::${e.child_id}`}async function fR(e){if(e.signal?.aborted)return null;let t,n;try{({spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(Te(),ct)))}catch{return null}if(!n())return null;let s=[];for(let o of e.sessionIds){let a=e.rowById.get(o);if(!a)continue;let c=a.alias||a.auto_title||(a.first_user_message?a.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");s.push(`- ${c}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1598
1688
  - 4 to 8 words
1599
1689
  - Title-case, no trailing punctuation
1600
1690
  - Capture the WORKFLOW (e.g., "Update Remotion deps + lint cleanup"), not any one session
@@ -1605,12 +1695,12 @@ Sessions:
1605
1695
  `+s.join(`
1606
1696
  `)+`
1607
1697
 
1608
- Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(h=>{e.signal&&(a=()=>h(null),e.signal.addEventListener("abort",a,{once:!0}))}),u=await Promise.race([o,c]);if(a&&e.signal&&e.signal.removeEventListener("abort",a),!u||!u.success)return null;let d=u.stdout.trim();if(!d)return null;let m;try{let h=JSON.parse(d);m=typeof h.result=="string"?h.result:d}catch{m=d}return m=m.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),m?m.length>80?m.slice(0,77)+"...":m:null}catch{return null}}function ni(e){return gy(e)}function si(e){let t=new Map;for(let o of e){let a=t.get(o.project);a||(a=[],t.set(o.project,a)),a.push(o)}let n=e.map(o=>o.id),s=new Map;try{s=jd(n)}catch{s=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let m of a){let h=s.get(m.id);h&&c.set(m.id,h)}let u=Md(c,.8),d=[];for(let m of a){let h=_y(m,s,u);h&&d.push(h)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function qd(e){return f().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1698
+ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),a=null,c=new Promise(f=>{e.signal&&(a=()=>f(null),e.signal.addEventListener("abort",a,{once:!0}))}),u=await Promise.race([o,c]);if(a&&e.signal&&e.signal.removeEventListener("abort",a),!u||!u.success)return null;let d=u.stdout.trim();if(!d)return null;let p;try{let f=JSON.parse(d);p=typeof f.result=="string"?f.result:d}catch{p=d}return p=p.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),p?p.length>80?p.slice(0,77)+"...":p:null}catch{return null}}function Zi(e){return _R(e)}function ea(e){let t=new Map;for(let o of e){let a=t.get(o.project);a||(a=[],t.set(o.project,a)),a.push(o)}let n=e.map(o=>o.id),s=new Map;try{s=bm(n)}catch{s=new Map}let r=new Map;for(let[o,a]of t){let c=new Map;for(let p of a){let f=s.get(p.id);f&&c.set(p.id,f)}let u=Sm(c,.8),d=[];for(let p of a){let f=hR(p,s,u);f&&d.push(f)}r.set(o,d)}return{byProject:t,scannablesByProject:r}}function Om(e){return h().prepare(`SELECT COUNT(DISTINCT t.id) AS n
1609
1699
  FROM threads t
1610
1700
  JOIN thread_edges te ON te.thread_id = t.id
1611
1701
  JOIN sessions s ON s.id = te.session_id
1612
1702
  JOIN projects p ON p.id = s.project_id
1613
- WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function gy(e){let t=f(),n={},s="1=1 AND s.message_count > 2";return s+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(s+=" AND p.name = @project",n.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1703
+ WHERE t.id LIKE 'auto-scan-%' AND p.name = ?`).get(e)?.n??0}function _R(e){let t=h(),n={},s="1=1 AND s.message_count > 2";return s+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(s+=" AND p.name = @project",n.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1614
1704
  s.first_user_message, s.auto_title,
1615
1705
  NULLIF(sa.alias, '') AS alias,
1616
1706
  s.file_path
@@ -1618,21 +1708,21 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1618
1708
  JOIN projects p ON p.id = s.project_id
1619
1709
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1620
1710
  WHERE ${s}
1621
- ORDER BY p.name ASC, s.started_at ASC`).all(n)}function _y(e,t,n){if(!e.started_at)return null;let s=Date.parse(e.started_at);if(!Number.isFinite(s))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=Ls(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:s,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:a?.full_mean??null,head_pool:a?.head_pool??null,tail_pool:a?.tail_pool??null,sample_chunks:a?.sample_chunks??[],cluster_id:n?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function Wd(e){let t=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,60).replace(/\n/g," ").trim():`thread from ${e.id.slice(0,8)}`);return t.length>80?t.slice(0,77)+"...":t}async function Xd(e){if(!e.rescore.enabled)return{proposals:e.proposals,ran:!1,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};let t=e.rescore.band??Pe,n=e.rescore.cap??It,s=e.rescore.model??vt,r=new Map(e.scannables.map(b=>[b.id,b])),o=Pd(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>n)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let a=new Map,c=0,u=0,d=0,m=0;for(let b=0;b<o.length&&!e.signal?.aborted;b++){let S=o[b],T=await Ud(S,{model:s,signal:e.signal});T?(a.set(Hd(S.step1),T),T.verdict==="same_workflow"?c++:T.verdict==="unrelated"?u++:d++):m++,e.onProgress?.({phase:"rescoring",current:b+1,total:o.length,verdict:T?.verdict??"failed"})}return{proposals:Bd({proposals:e.proposals,rescored:a,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:c,demoted:u,unsure:d,failed:m,capped:!1}}async function Jd(e){let t=f(),n=new Map(e.rows.map(d=>[d.id,d])),s=gu(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of s){if(c++,e.signal?.aborted)break;let m=n.get(d.rootId),h=Wd(m);if(!e.llmNames){a.set(d.rootId,h),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:h});continue}let S=await my({rootRow:m,sessionIds:d.sessionIds,rowById:n,signal:e.signal,model:e.model})??h;a.set(d.rootId,S),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:S})}return t.transaction(()=>{for(let d of s){if(!a.has(d.rootId))continue;let m=n.get(d.rootId),h=`auto-scan-${py()}`,b=a.get(d.rootId)??Wd(m),S=`Auto-detected workflow chain (${d.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(h,b,S,r),t.prepare(`INSERT INTO thread_edges
1711
+ ORDER BY p.name ASC, s.started_at ASC`).all(n)}function hR(e,t,n){if(!e.started_at)return null;let s=Date.parse(e.started_at);if(!Number.isFinite(s))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=lr(e.file_path,{maxUserMessages:7}),a=t?.get(e.id);return{id:e.id,started_at_ms:s,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:a?.full_mean??null,head_pool:a?.head_pool??null,tail_pool:a?.tail_pool??null,sample_chunks:a?.sample_chunks??[],cluster_id:n?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function Nm(e){let t=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,60).replace(/\n/g," ").trim():`thread from ${e.id.slice(0,8)}`);return t.length>80?t.slice(0,77)+"...":t}async function Cm(e){if(!e.rescore.enabled)return{proposals:e.proposals,ran:!1,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};let t=e.rescore.band??We,n=e.rescore.cap??Kt,s=e.rescore.model??Yt,r=new Map(e.scannables.map(S=>[S.id,S])),o=wm(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>n)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let a=new Map,c=0,u=0,d=0,p=0;for(let S=0;S<o.length&&!e.signal?.aborted;S++){let y=o[S],b=await km(y,{model:s,signal:e.signal});b?(a.set(xm(y.step1),b),b.verdict==="same_workflow"?c++:b.verdict==="unrelated"?u++:d++):p++,e.onProgress?.({phase:"rescoring",current:S+1,total:o.length,verdict:b?.verdict??"failed"})}return{proposals:Am({proposals:e.proposals,rescored:a,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:c,demoted:u,unsure:d,failed:p,capped:!1}}async function Lm(e){let t=h(),n=new Map(e.rows.map(d=>[d.id,d])),s=Zd(e.edges,e.scannables),r=new Date().toISOString(),o=[],a=new Map,c=0;for(let d of s){if(c++,e.signal?.aborted)break;let p=n.get(d.rootId),f=Nm(p);if(!e.llmNames){a.set(d.rootId,f),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:f});continue}let y=await fR({rootRow:p,sessionIds:d.sessionIds,rowById:n,signal:e.signal,model:e.model})??f;a.set(d.rootId,y),e.onProgress?.({phase:"naming",current:c,total:s.length,thread_name:y})}return t.transaction(()=>{for(let d of s){if(!a.has(d.rootId))continue;let p=n.get(d.rootId),f=`auto-scan-${gR()}`,S=a.get(d.rootId)??Nm(p),y=`Auto-detected workflow chain (${d.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(f,S,y,r),t.prepare(`INSERT INTO thread_edges
1622
1712
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1623
- VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(h,d.rootId,r);let T=new Map;for(let R of e.edges)d.sessionIds.includes(R.child_id)&&T.set(R.child_id,R);for(let R of d.sessionIds){if(R===d.rootId)continue;let O=T.get(R);O&&t.prepare(`INSERT INTO thread_edges
1713
+ VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(f,d.rootId,r);let b=new Map;for(let R of e.edges)d.sessionIds.includes(R.child_id)&&b.set(R.child_id,R);for(let R of d.sessionIds){if(R===d.rootId)continue;let k=b.get(R);k&&t.prepare(`INSERT INTO thread_edges
1624
1714
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1625
- VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(h,R,O.parent_id,O.confidence,r)}o.push({thread_id:h,name:b,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var Mt=new Map,En=new Map,hy=300*1e3;function jt(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function Ey(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Mt.delete(e.jobId),En.get(e.project)===e.jobId&&En.delete(e.project)},hy),e.cleanupTimer.unref?.())}function Us(e){return{jobId:e.jobId,project:e.project,threshold:e.threshold,llm_names:e.llmNames,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total_threads:e.totalThreads,threads_named:e.threadsNamed,edges_written:e.edgesWritten,threads_created:e.threadsCreated,current_thread_name:e.currentThreadName,error:e.error,rescore:e.rescore}}var by=500,Sy=30,ri="claude-haiku-4-5-20251001",Gd={"claude-haiku-4-5-20251001":{in:1,out:5,label:"Haiku 4.5"},"claude-sonnet-4-6":{in:3,out:15,label:"Sonnet 4.6"},"claude-opus-4-7":{in:15,out:75,label:"Opus 4.7"}};function Yd(e){return Gd[e]??Gd[ri]}function zd(e){let t=typeof e.threshold=="number"?e.threshold:Ns;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.model??ri,s="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Pe.lo,hi:e.llm_rescore?.band_hi??Pe.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??vt,c=ni({project:e.project});if(c.length===0)return{eligible_sessions:0,proposed_edges:0,estimated_threads:0,estimated_llm_calls:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,existing_auto_scan_threads:0,plan_window_estimate:We(0,n),accuracy_caveat:s,model:n,llm_rescore:r?{enabled:!0,band:o,cap:It,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:We(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=si(c),m=u.get(e.project)??[],h=d.get(e.project)??[],b=r?Math.min(t,o.lo):t,S=Rt(h,b),T=r?S.filter(i=>i.confidence>=t):S,R=Kd(T),O=R*by,L=R*Sy,x=Yd(n),F=O/1e6*x.in,A=L/1e6*x.out,$=F+A,te=We(R,n),H=qd(e.project),se=null;if(r){let i=$d(S,o),l=Yd(a),p=i*Dd,g=i*Fd,_=p/1e6*l.in+g/1e6*l.out;se={enabled:!0,band:o,cap:It,estimated_borderline_pairs:i,estimated_input_tokens_max:p,estimated_output_tokens_max:g,estimated_cost_usd_max:Math.round(_*1e4)/1e4,plan_window_estimate:We(i,a),exceeds_cap:i>It,model:a}}return{eligible_sessions:m.length,proposed_edges:T.length,estimated_threads:R,estimated_llm_calls:R,estimated_input_tokens_max:O,estimated_output_tokens_max:L,estimated_cost_usd_max:Math.round($*1e4)/1e4,existing_auto_scan_threads:H,plan_window_estimate:te,accuracy_caveat:s,model:n,llm_rescore:se}}function Kd(e){let t=new Map,n=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},s=(o,a)=>{let c=n(o),u=n(a);c!==u&&t.set(c,u)};for(let o of e)t.has(o.parent_id)||t.set(o.parent_id,o.parent_id),t.has(o.child_id)||t.set(o.child_id,o.child_id),s(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(n(o));return r.size}function Vd(e){let t=typeof e.threshold=="number"?e.threshold:Ns;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.llm_names??!0,s=e.model??ri,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??Pe.lo,hi:e.llm_rescore?.band_hi??Pe.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??vt,c=En.get(e.project);if(c){let $=Mt.get(c);if($&&$.status==="running")return{jobId:c,reused:!0};En.delete(e.project)}let u=ni({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:m}=si(u),h=d.get(e.project),b=m.get(e.project);if(!h||!b)return{error:`project "${e.project}" not found`};let S=r?Math.min(t,o.lo):t,T=Rt(b,S),R=r?T.filter($=>$.confidence>=t):T;if(T.length===0||R.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let O=fy(),L=new AbortController,x=Kd(R),F=new Date().toISOString(),A={jobId:O,project:e.project,threshold:t,llmNames:n,status:"running",startedAt:F,endedAt:null,events:[],waiters:new Set,controller:L,totalThreads:x,threadsNamed:0,edgesWritten:0,threadsCreated:0,currentThreadName:null,error:null,cleanupTimer:null,rescore:r?{enabled:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1}:null};return Mt.set(O,A),En.set(e.project,O),(async()=>{await Promise.resolve();try{jt(A,"progress",{phase:"linking",edges_proposed:T.length,estimated_threads:x});let $=T;if(r){let H=await Xd({proposals:T,scannables:b,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:L.signal,onProgress:se=>{se.phase==="rescoring"&&jt(A,"progress",{phase:"rescoring",current:se.current,total:se.total,verdict:se.verdict})}});$=H.proposals,A.rescore={enabled:!0,considered:H.considered,promoted:H.promoted,demoted:H.demoted,unsure:H.unsure,failed:H.failed,capped:H.capped}}else $=T.filter(H=>H.confidence>=t);if($.length===0){A.status=L.signal.aborted?"cancelled":"done",A.endedAt=new Date().toISOString(),jt(A,"done",{...Us(A),threads:[]});return}let te=await Jd({project:e.project,rows:h,edges:$,scannables:b,llmNames:n,model:s,signal:L.signal,onProgress:H=>{H.phase==="naming"&&(A.threadsNamed=H.current,A.currentThreadName=H.thread_name??null,jt(A,"progress",{phase:"naming",current:H.current,total:H.total,thread_name:H.thread_name}))}});A.threadsCreated=te.threads.length,A.edgesWritten=$.length+te.threads.length,A.status=L.signal.aborted?"cancelled":"done",A.endedAt=new Date().toISOString(),jt(A,"done",{...Us(A),threads:te.threads})}catch($){A.status="failed",A.endedAt=new Date().toISOString(),A.error=$ instanceof Error?$.message:String($??"unknown error"),jt(A,"done",Us(A))}finally{Ey(A)}})(),{jobId:O,reused:!1}}async function*Zd(e,t=0){let n=Mt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Qd(e){let t=Mt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function oi(e){let t=Mt.get(e);return t?Us(t):null}Fn();import{randomUUID as Ty}from"node:crypto";var Bs=new Map;function ep(e){let t={id:Ty(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return Bs.set(t.id,t),t}function Hs(e){return Bs.get(e)}function Dt(e,t){for(let n of e.listeners)n(t)}function tp(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function np(e){let t=Bs.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),Dt(t,{type:"status",status:"cancelled"}),!0):!1}function sp(e){return Bs.delete(e)}pt();function Ws(e){let{session:t,knownTags:n,minTags:s,maxTags:r}=e,o=n.slice(0,50).map(c=>c.tag).join(", "),a=t.current_tags.length>0?`already applied, do not repeat: [${t.current_tags.join(", ")}]`:"currently has no tags";return[`You are tagging a software engineering session. Produce ${s}-${r} concise, lowercase, hyphen-separated tags that describe:`," - the domain or subsystem touched (auth, db, frontend, ...)"," - the kind of work (bugfix, feature, refactor, research, ...)"," - any specific tool, library, or product if prominent","","Prefer tags from the known-tags list below when applicable \u2014 consistency over creativity. Never invent marketing-sounding labels. Never tag based on speculation; only on observed work.","",`known tags (most used first, up to 50): ${o||"(none yet)"}`,"","Session:",` project: ${t.project}`,t.alias?` alias: ${JSON.stringify(t.alias)}`:" alias: (none)",t.git_branch?` git_branch: ${t.git_branch}`:"",` ${a}`," first_user_message:",rp(t.first_user_message," ")," message_sample:",rp(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${s}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1626
- `)}function rp(e,t){return e.split(`
1715
+ VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(f,R,k.parent_id,k.confidence,r)}o.push({thread_id:f,name:S,session_count:d.sessionIds.length})}})(),{project:e.project,threads:o}}var Vt=new Map,Bn=new Map,bR=300*1e3;function zt(e,t,n){e.events.push({id:e.events.length+1,kind:t,data:n});for(let s of e.waiters)s();e.waiters.clear()}function SR(e){e.cleanupTimer||(e.cleanupTimer=setTimeout(()=>{Vt.delete(e.jobId),Bn.get(e.project)===e.jobId&&Bn.delete(e.project)},bR),e.cleanupTimer.unref?.())}function br(e){return{jobId:e.jobId,project:e.project,threshold:e.threshold,llm_names:e.llmNames,status:e.status,startedAt:e.startedAt,endedAt:e.endedAt,total_threads:e.totalThreads,threads_named:e.threadsNamed,edges_written:e.edgesWritten,threads_created:e.threadsCreated,current_thread_name:e.currentThreadName,error:e.error,rescore:e.rescore}}var yR=500,TR=30,ta="claude-haiku-4-5-20251001",vm={"claude-haiku-4-5-20251001":{in:1,out:5,label:"Haiku 4.5"},"claude-sonnet-4-6":{in:3,out:15,label:"Sonnet 4.6"},"claude-opus-4-7":{in:15,out:75,label:"Opus 4.7"}};function Im(e){return vm[e]??vm[ta]}function Mm(e){let t=typeof e.threshold=="number"?e.threshold:ar;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.model??ta,s="",r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??We.lo,hi:e.llm_rescore?.band_hi??We.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??Yt,c=Zi({project:e.project});if(c.length===0)return{eligible_sessions:0,proposed_edges:0,estimated_threads:0,estimated_llm_calls:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,existing_auto_scan_threads:0,plan_window_estimate:Ve(0,n),accuracy_caveat:s,model:n,llm_rescore:r?{enabled:!0,band:o,cap:Kt,estimated_borderline_pairs:0,estimated_input_tokens_max:0,estimated_output_tokens_max:0,estimated_cost_usd_max:0,plan_window_estimate:Ve(0,a),exceeds_cap:!1,model:a}:null};let{byProject:u,scannablesByProject:d}=ea(c),p=u.get(e.project)??[],f=d.get(e.project)??[],S=r?Math.min(t,o.lo):t,y=Ut(f,S),b=r?y.filter(i=>i.confidence>=t):y,R=jm(b),k=R*yR,M=R*TR,P=Im(n),G=k/1e6*P.in,D=M/1e6*P.out,v=G+D,F=Ve(R,n),U=Om(e.project),L=null;if(r){let i=Rm(y,o),l=Im(a),m=i*ym,g=i*Tm,_=m/1e6*l.in+g/1e6*l.out;L={enabled:!0,band:o,cap:Kt,estimated_borderline_pairs:i,estimated_input_tokens_max:m,estimated_output_tokens_max:g,estimated_cost_usd_max:Math.round(_*1e4)/1e4,plan_window_estimate:Ve(i,a),exceeds_cap:i>Kt,model:a}}return{eligible_sessions:p.length,proposed_edges:b.length,estimated_threads:R,estimated_llm_calls:R,estimated_input_tokens_max:k,estimated_output_tokens_max:M,estimated_cost_usd_max:Math.round(v*1e4)/1e4,existing_auto_scan_threads:U,plan_window_estimate:F,accuracy_caveat:s,model:n,llm_rescore:L}}function jm(e){let t=new Map,n=o=>{let a=o;for(;t.get(a)!==a;)a=t.get(a);return a},s=(o,a)=>{let c=n(o),u=n(a);c!==u&&t.set(c,u)};for(let o of e)t.has(o.parent_id)||t.set(o.parent_id,o.parent_id),t.has(o.child_id)||t.set(o.child_id,o.child_id),s(o.parent_id,o.child_id);let r=new Set;for(let o of t.keys())r.add(n(o));return r.size}function Dm(e){let t=typeof e.threshold=="number"?e.threshold:ar;if(!Number.isFinite(t)||t<0||t>1)return{error:"threshold must be a number in [0, 1]"};let n=e.llm_names??!0,s=e.model??ta,r=!!e.llm_rescore?.enabled,o={lo:e.llm_rescore?.band_lo??We.lo,hi:e.llm_rescore?.band_hi??We.hi};if(r&&(!Number.isFinite(o.lo)||!Number.isFinite(o.hi)||o.lo<0||o.hi>1||o.lo>o.hi))return{error:"rescore band must satisfy 0 \u2264 lo \u2264 hi \u2264 1"};let a=e.llm_rescore?.model??Yt,c=Bn.get(e.project);if(c){let v=Vt.get(c);if(v&&v.status==="running")return{jobId:c,reused:!0};Bn.delete(e.project)}let u=Zi({project:e.project});if(u.length===0)return{error:`no eligible sessions in project "${e.project}"`};let{byProject:d,scannablesByProject:p}=ea(u),f=d.get(e.project),S=p.get(e.project);if(!f||!S)return{error:`project "${e.project}" not found`};let y=r?Math.min(t,o.lo):t,b=Ut(S,y),R=r?b.filter(v=>v.confidence>=t):b;if(b.length===0||R.length===0&&!r)return{error:"no edges above threshold; nothing to apply"};let k=ER(),M=new AbortController,P=jm(R),G=new Date().toISOString(),D={jobId:k,project:e.project,threshold:t,llmNames:n,status:"running",startedAt:G,endedAt:null,events:[],waiters:new Set,controller:M,totalThreads:P,threadsNamed:0,edgesWritten:0,threadsCreated:0,currentThreadName:null,error:null,cleanupTimer:null,rescore:r?{enabled:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1}:null};return Vt.set(k,D),Bn.set(e.project,k),(async()=>{await Promise.resolve();try{zt(D,"progress",{phase:"linking",edges_proposed:b.length,estimated_threads:P});let v=b;if(r){let U=await Cm({proposals:b,scannables:S,applyThreshold:t,rescore:{enabled:!0,band:o,model:a},signal:M.signal,onProgress:L=>{L.phase==="rescoring"&&zt(D,"progress",{phase:"rescoring",current:L.current,total:L.total,verdict:L.verdict})}});v=U.proposals,D.rescore={enabled:!0,considered:U.considered,promoted:U.promoted,demoted:U.demoted,unsure:U.unsure,failed:U.failed,capped:U.capped}}else v=b.filter(U=>U.confidence>=t);if(v.length===0){D.status=M.signal.aborted?"cancelled":"done",D.endedAt=new Date().toISOString(),zt(D,"done",{...br(D),threads:[]});return}let F=await Lm({project:e.project,rows:f,edges:v,scannables:S,llmNames:n,model:s,signal:M.signal,onProgress:U=>{U.phase==="naming"&&(D.threadsNamed=U.current,D.currentThreadName=U.thread_name??null,zt(D,"progress",{phase:"naming",current:U.current,total:U.total,thread_name:U.thread_name}))}});D.threadsCreated=F.threads.length,D.edgesWritten=v.length+F.threads.length,D.status=M.signal.aborted?"cancelled":"done",D.endedAt=new Date().toISOString(),zt(D,"done",{...br(D),threads:F.threads})}catch(v){D.status="failed",D.endedAt=new Date().toISOString(),D.error=v instanceof Error?v.message:String(v??"unknown error"),zt(D,"done",br(D))}finally{SR(D)}})(),{jobId:k,reused:!1}}async function*Pm(e,t=0){let n=Vt.get(e);if(!n)return;let s=t;for(;;){for(;s<n.events.length;){let r=n.events[s];if(!r)break;if(s+=1,yield r,r.kind==="done")return}if(n.status!=="running")return;await new Promise(r=>n.waiters.add(r))}}function Fm(e){let t=Vt.get(e);return!t||t.status!=="running"?!1:(t.controller.abort(),!0)}function na(e){let t=Vt.get(e);return t?br(t):null}cs();import{randomUUID as wR}from"node:crypto";var Sr=new Map;function $m(e){let t={id:wR(),status:"pending",createdAt:new Date().toISOString(),finishedAt:null,total:e,completed:0,results:[],error:null,controller:new AbortController,listeners:new Set};return Sr.set(t.id,t),t}function yr(e){return Sr.get(e)}function Qt(e,t){for(let n of e.listeners)n(t)}function Um(e,t){return e.listeners.add(t),()=>{e.listeners.delete(t)}}function Hm(e){let t=Sr.get(e);return t?(t.controller.abort(),t.status="cancelled",t.finishedAt=new Date().toISOString(),Qt(t,{type:"status",status:"cancelled"}),!0):!1}function Bm(e){return Sr.delete(e)}wt();function Tr(e){let{session:t,knownTags:n,minTags:s,maxTags:r}=e,o=n.slice(0,50).map(c=>c.tag).join(", "),a=t.current_tags.length>0?`already applied, do not repeat: [${t.current_tags.join(", ")}]`:"currently has no tags";return[`You are tagging a software engineering session. Produce ${s}-${r} concise, lowercase, hyphen-separated tags that describe:`," - the domain or subsystem touched (auth, db, frontend, ...)"," - the kind of work (bugfix, feature, refactor, research, ...)"," - any specific tool, library, or product if prominent","","Prefer tags from the known-tags list below when applicable \u2014 consistency over creativity. Never invent marketing-sounding labels. Never tag based on speculation; only on observed work.","",`known tags (most used first, up to 50): ${o||"(none yet)"}`,"","Session:",` project: ${t.project}`,t.alias?` alias: ${JSON.stringify(t.alias)}`:" alias: (none)",t.git_branch?` git_branch: ${t.git_branch}`:"",` ${a}`," first_user_message:",Wm(t.first_user_message," ")," message_sample:",Wm(t.message_sample," "),"","Return a single JSON object matching this schema exactly, with no prose before or after:",`{"tags": string[] length ${s}-${r}, "confidence": number 0-1, "rationale": string one short sentence}`].filter(Boolean).join(`
1716
+ `)}function Wm(e,t){return e.split(`
1627
1717
  `).map(n=>t+n).join(`
1628
- `)}pt();import{z as bn}from"zod";var yy=bn.object({tags:bn.array(bn.string()).min(1),confidence:bn.number().min(0).max(1),rationale:bn.string().min(1).max(500)});function wy(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function qs(e,t={}){let n=t.maxTags??10,s;try{s=JSON.parse(wy(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=yy.safeParse(s);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>Ze(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,n);return a.length===0?{ok:!1,reason:"no usable tags after normalization"}:{ok:!0,data:{tags:a,confidence:r.data.confidence,rationale:r.data.rationale}}}import Ry from"@anthropic-ai/sdk";async function Xs(e){let s=(await new Ry({apiKey:e.apiKey}).messages.create({model:e.model,max_tokens:512,temperature:.2,messages:[{role:"user",content:e.prompt}]},e.signal?{signal:e.signal}:void 0)).content.find(r=>r.type==="text");if(!s||s.type!=="text")throw new Error("Anthropic response contained no text block");return s.text}function ky(e){if(!(e instanceof Error))return!1;let t=e;return t.status===429||t.status===502||t.status===503||t.status===504}async function Js(e,t={}){let n=t.maxAttempts??3,s=t.baseDelayMs??500,r;for(let o=0;o<n;o++)try{return await e()}catch(a){if(r=a,!ky(a)||o===n-1)throw a;await new Promise(c=>setTimeout(c,s*Math.pow(2,o)))}throw r}async function op(e,t){e.status="running",Dt(e,{type:"status",status:"running"});let n=dt();for(let s of t.sessions){if(e.controller.signal.aborted)break;let r=Ws({session:s,knownTags:n,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:s.id,project:s.project,alias:s.alias,first_user_message:s.first_user_message,current_tags:s.current_tags,suggestion:null,error:null,applied:!1};try{let a=await Js(()=>Xs({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=qs(a,{maxTags:t.maxTags});c.ok?o.suggestion=c.data:o.error=c.reason}catch(a){o.error=a instanceof Error?a.message:String(a)}e.results.push(o),e.completed+=1,Dt(e,{type:"result",result:o}),Dt(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let s=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;Dt(e,{type:"done",summary:{ok:s,failed:r}})}}function ip(e,t){let n=0,s=0;for(let r of t){let o=e.results.find(a=>a.sessionId===r.sessionId);if(o){for(let a of r.tags)try{let{added:c}=ut(r.sessionId,a);c&&(n+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),s+=1}o.applied=!0}}return{applied:n,failed:s}}pt();Fn();var Z={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},ii=new Set;function Tn(){return{status:Z.status,processed:Z.processed,total:Z.total,currentSessionId:Z.currentSessionId,lastError:Z.lastError,lastRunAt:Z.lastRunAt}}function ap(e){return ii.add(e),()=>{ii.delete(e)}}function Sn(){let e=Tn();for(let t of ii)t(e)}async function Gs(){let e=Fe();if(!(!e.enabled||!e.autopilot)&&!(e.backend!=="api"||!e.apiKey)&&!Z.running){Z.running=!0,Z.status="scanning",Z.processed=0,Z.total=0,Z.currentSessionId=null,Z.lastError=null,Z.controller=new AbortController,Sn();try{let t=mt({untaggedOnly:!0,limit:200});if(Z.total=t.length,Sn(),t.length===0){Z.status="idle",Z.lastRunAt=new Date().toISOString();return}let n=dt();for(let s of t){if(Z.controller.signal.aborted)break;let r=Fe();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;Z.currentSessionId=s.id,Sn();let o=Ws({session:s,knownTags:n,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await Js(()=>Xs({apiKey:r.apiKey,model:r.model,prompt:o,signal:Z.controller.signal})),c=qs(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{ut(s.id,u)}catch(d){console.error("[autopilot] addTag failed:",s.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",s.id,a)}Z.processed+=1,Sn(),await new Promise(a=>setTimeout(a,1500))}Z.status="idle",Z.lastRunAt=new Date().toISOString()}catch(t){Z.status="error",Z.lastError=t instanceof Error?t.message:String(t),console.error("[autopilot] fatal:",t)}finally{Z.running=!1,Z.currentSessionId=null,Z.controller=null,Sn()}}}Un();import{existsSync as lp,readFileSync as Ay,writeFileSync as up}from"node:fs";import{homedir as xy}from"node:os";import{dirname as Ny,join as Oy,resolve as Ly}from"node:path";import{fileURLToPath as Cy}from"node:url";var Ft=Oy(xy(),".claude.json"),vy=Ny(Cy(import.meta.url)),Iy=Ly(vy,"..","mcp","server.js");function ai(){if(!lp(Ft))return{};try{let e=Ay(Ft,"utf8"),t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch(e){return console.error("[mcp-installer] failed to parse ~/.claude.json:",e),{}}}var cp=new Map;function jy(e){let t=cp.get(e);if(t!==void 0)return t;let n=gt(e)!==null;return cp.set(e,n),n}function My(){return jy("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[Iy]}}function qe(){let t=ai().mcpServers?.recall;return{configPath:Ft,configExists:lp(Ft),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function dp(){let e=ai(),t=e.mcpServers??{},n=My(),s=t.recall;if(s?.command===n.command&&JSON.stringify(s?.args??[])===JSON.stringify(n.args))return qe();let o={command:n.command};n.args.length>0&&(o.args=n.args);let a={...e,mcpServers:{...t,recall:o}};return up(Ft,JSON.stringify(a,null,2)),qe()}function pp(){let e=ai(),t=e.mcpServers??{};if(!t.recall)return qe();let{recall:n,...s}=t,r={...e,mcpServers:s};return up(Ft,JSON.stringify(r,null,2)),qe()}import{existsSync as mp,mkdirSync as Dy,readFileSync as Fy,writeFileSync as gp}from"node:fs";import{homedir as Py}from"node:os";import{join as _p}from"node:path";import{z as Xe}from"zod";function fp(){return process.env.RECALL_HOME??_p(Py(),".recall")}function hp(){let e=fp();mp(e)||Dy(e,{recursive:!0})}function li(){return _p(fp(),"onboarding.json")}var Ys=Xe.object({version:Xe.literal(1).default(1),completed:Xe.boolean().default(!1),skipped:Xe.boolean().default(!1),finishedAt:Xe.string().nullable().default(null),completedSteps:Xe.array(Xe.string().max(100)).max(50).default([]),threadsIntroSeen:Xe.boolean().default(!1)}),ci={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function zs(){let e=li();if(!mp(e))return{...ci};try{let t=JSON.parse(Fy(e,"utf8")),n=Ys.safeParse(t);return n.success?n.data:{...ci}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...ci}}}function Ep(e){hp();let t=zs(),n=Ys.parse({...t,...e,completedSteps:$y([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return gp(li(),JSON.stringify(n,null,2)),n}function bp(){hp();let e=zs(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return gp(li(),JSON.stringify(t,null,2)),t}function $y(e){let t=new Set,n=[];for(let s of e)t.has(s)||(t.add(s),n.push(s));return n}ye();kr();Rr();B();B();var Uy=500,Ks=new Set,Sp=null,Vs=!1;function Tp(){return Ks.size}function By(){return`
1718
+ `)}wt();import{z as Wn}from"zod";var RR=Wn.object({tags:Wn.array(Wn.string()).min(1),confidence:Wn.number().min(0).max(1),rationale:Wn.string().min(1).max(500)});function kR(e){let t=e.trim();return t.startsWith("```")?t.replace(/^```(?:json)?\s*/i,"").replace(/```$/i,"").trim():t}function wr(e,t={}){let n=t.maxTags??10,s;try{s=JSON.parse(kR(e))}catch{return{ok:!1,reason:"not valid JSON"}}let r=RR.safeParse(s);if(!r.success)return{ok:!1,reason:r.error.issues.map(c=>c.message).join("; ")};let o=r.data.tags.map(c=>it(c)).filter(c=>c.length>0),a=Array.from(new Set(o)).slice(0,n);return a.length===0?{ok:!1,reason:"no usable tags after normalization"}:{ok:!0,data:{tags:a,confidence:r.data.confidence,rationale:r.data.rationale}}}import AR from"@anthropic-ai/sdk";async function Rr(e){let s=(await new AR({apiKey:e.apiKey}).messages.create({model:e.model,max_tokens:512,temperature:.2,messages:[{role:"user",content:e.prompt}]},e.signal?{signal:e.signal}:void 0)).content.find(r=>r.type==="text");if(!s||s.type!=="text")throw new Error("Anthropic response contained no text block");return s.text}function xR(e){if(!(e instanceof Error))return!1;let t=e;return t.status===429||t.status===502||t.status===503||t.status===504}async function kr(e,t={}){let n=t.maxAttempts??3,s=t.baseDelayMs??500,r;for(let o=0;o<n;o++)try{return await e()}catch(a){if(r=a,!xR(a)||o===n-1)throw a;await new Promise(c=>setTimeout(c,s*Math.pow(2,o)))}throw r}async function qm(e,t){e.status="running",Qt(e,{type:"status",status:"running"});let n=Tt();for(let s of t.sessions){if(e.controller.signal.aborted)break;let r=Tr({session:s,knownTags:n,minTags:t.minTags,maxTags:t.maxTags}),o={sessionId:s.id,project:s.project,alias:s.alias,first_user_message:s.first_user_message,current_tags:s.current_tags,suggestion:null,error:null,applied:!1};try{let a=await kr(()=>Rr({apiKey:t.apiKey,model:t.model,prompt:r,signal:e.controller.signal})),c=wr(a,{maxTags:t.maxTags});c.ok?o.suggestion=c.data:o.error=c.reason}catch(a){o.error=a instanceof Error?a.message:String(a)}e.results.push(o),e.completed+=1,Qt(e,{type:"result",result:o}),Qt(e,{type:"progress",completed:e.completed,total:e.total})}if(!e.controller.signal.aborted){e.status="completed",e.finishedAt=new Date().toISOString();let s=e.results.filter(o=>o.suggestion&&!o.error).length,r=e.results.filter(o=>o.error).length;Qt(e,{type:"done",summary:{ok:s,failed:r}})}}function Xm(e,t){let n=0,s=0;for(let r of t){let o=e.results.find(a=>a.sessionId===r.sessionId);if(o){for(let a of r.tags)try{let{added:c}=yt(r.sessionId,a);c&&(n+=1)}catch(c){console.error("[scanner] apply failed:",r.sessionId,a,c),s+=1}o.applied=!0}}return{applied:n,failed:s}}wt();cs();var ee={running:!1,status:"idle",processed:0,total:0,currentSessionId:null,lastError:null,lastRunAt:null,controller:null},sa=new Set;function Xn(){return{status:ee.status,processed:ee.processed,total:ee.total,currentSessionId:ee.currentSessionId,lastError:ee.lastError,lastRunAt:ee.lastRunAt}}function Jm(e){return sa.add(e),()=>{sa.delete(e)}}function qn(){let e=Xn();for(let t of sa)t(e)}async function Ar(){let e=Be();if(!(!e.enabled||!e.autopilot)&&!(e.backend!=="api"||!e.apiKey)&&!ee.running){ee.running=!0,ee.status="scanning",ee.processed=0,ee.total=0,ee.currentSessionId=null,ee.lastError=null,ee.controller=new AbortController,qn();try{let t=Rt({untaggedOnly:!0,limit:200});if(ee.total=t.length,qn(),t.length===0){ee.status="idle",ee.lastRunAt=new Date().toISOString();return}let n=Tt();for(let s of t){if(ee.controller.signal.aborted)break;let r=Be();if(!r.autopilot||!r.enabled||r.backend!=="api"||!r.apiKey)break;ee.currentSessionId=s.id,qn();let o=Tr({session:s,knownTags:n,minTags:r.minTagsPerSession,maxTags:r.maxTagsPerSession});try{let a=await kr(()=>Rr({apiKey:r.apiKey,model:r.model,prompt:o,signal:ee.controller.signal})),c=wr(a,{maxTags:r.maxTagsPerSession});if(c.ok)for(let u of c.data.tags)try{yt(s.id,u)}catch(d){console.error("[autopilot] addTag failed:",s.id,u,d)}}catch(a){console.error("[autopilot] scan failed for",s.id,a)}ee.processed+=1,qn(),await new Promise(a=>setTimeout(a,1500))}ee.status="idle",ee.lastRunAt=new Date().toISOString()}catch(t){ee.status="error",ee.lastError=t instanceof Error?t.message:String(t),console.error("[autopilot] fatal:",t)}finally{ee.running=!1,ee.currentSessionId=null,ee.controller=null,qn()}}}ds();import{copyFileSync as NR,existsSync as xr,readFileSync as Ym,writeFileSync as Km}from"node:fs";import{homedir as OR}from"node:os";import{dirname as CR,join as LR,resolve as vR}from"node:path";import{fileURLToPath as IR}from"node:url";var Pe=LR(OR(),".claude.json"),MR=CR(IR(import.meta.url)),jR=vR(MR,"..","mcp","server.js");function ra(){if(!xr(Pe))return{};try{let e=Ym(Pe,"utf8"),t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch(e){return console.error("[mcp-installer] failed to parse ~/.claude.json:",e),{}}}var Gm=new Map;function DR(e){let t=Gm.get(e);if(t!==void 0)return t;let n=kt(e)!==null;return Gm.set(e,n),n}function PR(){return DR("claude-recall-mcp")?{command:"claude-recall-mcp",args:[]}:{command:process.execPath,args:[jR]}}function qe(){let t=ra().mcpServers?.recall;return{configPath:Pe,configExists:xr(Pe),installed:!!(t&&typeof t.command=="string"),command:t?.command??null,args:t?.args??null}}function oa(){let e=ra(),t=e.mcpServers??{},n=PR(),s=t.recall;if(s?.command===n.command&&JSON.stringify(s?.args??[])===JSON.stringify(n.args))return qe();let o={command:n.command};n.args.length>0&&(o.args=n.args);let a={...e,mcpServers:{...t,recall:o}};return Km(Pe,JSON.stringify(a,null,2)),qe()}function zm(){if(!xr(Pe))return{repointed:!1,reason:"config_missing"};let e;try{e=Ym(Pe,"utf8")}catch(f){return console.error("[mcp-installer] validate: failed to read ~/.claude.json:",f),{repointed:!1,reason:"read_error"}}let t;try{t=JSON.parse(e)}catch(f){return console.error("[mcp-installer] validate: ~/.claude.json is not valid JSON \u2014 skipping (operator must fix):",f),{repointed:!1,reason:"parse_error"}}if(!t||typeof t!="object"||Array.isArray(t))return{repointed:!1,reason:"not_an_object"};let s=t.mcpServers;if(!s||typeof s!="object")return{repointed:!1,reason:"no_mcp_servers"};let r=s.recall;if(!r)return{repointed:!1,reason:"no_recall_entry"};let o=r.args;if(!Array.isArray(o)||o.length===0||typeof o[0]!="string")return{repointed:!1,reason:"no_args_path"};let a=o[0];if(xr(a))return{repointed:!1,reason:"path_live"};let c=new Date().toISOString().replace(/[:.]/g,"-"),u=`${Pe}.bak.${c}`;try{NR(Pe,u)}catch(f){return console.error(`[mcp-installer] validate: backup write failed (${u}) \u2014 refusing to repoint to avoid risking original:`,f),{repointed:!1,reason:"backup_failed"}}try{oa()}catch(f){return console.error(`[mcp-installer] validate: installMcp() failed during repoint \u2014 backup preserved at ${u}:`,f),{repointed:!1,reason:"install_failed"}}let d=qe(),p=d.args&&d.args.length>0?d.args[0]:d.command;return console.log(`[mcp-installer] repointed stale ~/.claude.json from ${a} to ${p??"(command-only)"} (backup: ${u})`),{repointed:!0,reason:"stale_args[0]"}}function Vm(){let e=ra(),t=e.mcpServers??{};if(!t.recall)return qe();let{recall:n,...s}=t,r={...e,mcpServers:s};return Km(Pe,JSON.stringify(r,null,2)),qe()}import{existsSync as Qm,mkdirSync as FR,readFileSync as $R,writeFileSync as Zm}from"node:fs";import{homedir as UR}from"node:os";import{join as eg}from"node:path";import{z as Qe}from"zod";function tg(){return process.env.RECALL_HOME??eg(UR(),".recall")}function ng(){let e=tg();Qm(e)||FR(e,{recursive:!0})}function aa(){return eg(tg(),"onboarding.json")}var Nr=Qe.object({version:Qe.literal(1).default(1),completed:Qe.boolean().default(!1),skipped:Qe.boolean().default(!1),finishedAt:Qe.string().nullable().default(null),completedSteps:Qe.array(Qe.string().max(100)).max(50).default([]),threadsIntroSeen:Qe.boolean().default(!1)}),ia={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:[],threadsIntroSeen:!1};function Or(){let e=aa();if(!Qm(e))return{...ia};try{let t=JSON.parse($R(e,"utf8")),n=Nr.safeParse(t);return n.success?n.data:{...ia}}catch(t){return console.error("[onboarding-state] failed to parse onboarding.json, using defaults:",t),{...ia}}}function sg(e){ng();let t=Or(),n=Nr.parse({...t,...e,completedSteps:HR([...t.completedSteps??[],...e.completedSteps??[]]),version:1});return Zm(aa(),JSON.stringify(n,null,2)),n}function rg(){ng();let e=Or(),t={version:1,completed:!1,skipped:!1,finishedAt:null,completedSteps:e.completedSteps,threadsIntroSeen:e.threadsIntroSeen};return Zm(aa(),JSON.stringify(t,null,2)),t}function HR(e){let t=new Set,n=[];for(let s of e)t.has(s)||(t.add(s),n.push(s));return n}Te();co();ao();q();q();var BR=500,Cr=new Set,og=null,Lr=!1;function ig(){return Cr.size}function WR(){return`
1629
1719
  SELECT m.uuid, m.session_id, m.timestamp, m.raw_json
1630
1720
  FROM messages m
1631
1721
  LEFT JOIN message_usage mu ON mu.message_uuid = m.uuid
1632
1722
  WHERE m.role = 'assistant' AND mu.message_uuid IS NULL
1633
1723
  AND m.uuid NOT IN (SELECT value FROM json_each(?))
1634
1724
  LIMIT ?
1635
- `}function yp(e,t){let n=t.limit??Number.MAX_SAFE_INTEGER,s=Math.max(1,t.chunkSize??Uy),r=e.prepare(By()),o=e.prepare(`
1725
+ `}function ag(e,t){let n=t.limit??Number.MAX_SAFE_INTEGER,s=Math.max(1,t.chunkSize??BR),r=e.prepare(WR()),o=e.prepare(`
1636
1726
  INSERT INTO message_usage (
1637
1727
  message_uuid, session_id, model,
1638
1728
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1642,7 +1732,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1642
1732
  @input, @output, @cc, @cr, @ts
1643
1733
  )
1644
1734
  ON CONFLICT(message_uuid) DO NOTHING
1645
- `),a=0,c=0,u=new Set;for(;a<n;){let d=Math.min(s,n-a),m=JSON.stringify([...Ks]),h=r.all(m,d);if(h.length===0)break;let b=new Set;if(e.transaction(()=>{for(let T of h){let R;try{R=JSON.parse(T.raw_json)}catch{Ks.add(T.uuid);continue}let O=zr(R.message);if(!O){Ks.add(T.uuid);continue}o.run({uuid:T.uuid,session_id:T.session_id,model:R.message?.model??null,input:O.inputTokens,output:O.outputTokens,cc:O.cacheCreateTokens,cr:O.cacheReadTokens,ts:T.timestamp}),c+=1,b.add(T.session_id)}for(let T of b)os(e,T),u.add(T)})(),a+=h.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:h.length<d}),h.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function wp(e={}){return yp(f(),e)}function Rp(e={}){return Vs?!1:(Vs=!0,queueMicrotask(()=>{try{let t=yp(f(),e);Sp={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{Vs=!1}}),!0)}function kp(){return Vs}function ui(){return Sp}var Hy=[[/opus[-_ ]?4[-_. ]?7/i,{label:"Opus 4.7",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/opus[-_ ]?4[-_. ]?6/i,{label:"Opus 4.6",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4[-_. ]?6/i,{label:"Sonnet 4.6",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/sonnet[-_ ]?4[-_. ]?5/i,{label:"Sonnet 4.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku[-_ ]?4[-_. ]?5/i,{label:"Haiku 4.5",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}],[/opus[-_ ]?4(?!.*[5-9])/i,{label:"Opus 4",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4(?!.*[5-9])/i,{label:"Sonnet 4",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?7[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?7/i,{label:"Sonnet 3.7",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?5/i,{label:"Sonnet 3.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?haiku|haiku[-_ ]?3[-_. ]?5/i,{label:"Haiku 3.5",inputCentsPerMtok:80,outputCentsPerMtok:400,cacheCreateCentsPerMtok:100,cacheReadCentsPerMtok:8}],[/3[-_ ]?opus|opus[-_ ]?3/i,{label:"Opus 3",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/3[-_ ]?haiku|haiku(?!.*3[-_. ]?5)/i,{label:"Haiku 3",inputCentsPerMtok:25,outputCentsPerMtok:125,cacheCreateCentsPerMtok:30,cacheReadCentsPerMtok:3}],[/opus/i,{label:"Opus",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet/i,{label:"Sonnet",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku/i,{label:"Haiku",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}]],Ap={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function Je(e){if(!e)return Ap;for(let[t,n]of Hy)if(t.test(e))return n;return Ap}function Ae(e,t){if(e.byModel&&Object.keys(e.byModel).length>0){let a={input:0,output:0,cacheCreate:0,cacheRead:0},c=0;for(let[d,m]of Object.entries(e.byModel)){let h=Je(d);a.input+=m.inputTokens/1e6*h.inputCentsPerMtok,a.output+=m.outputTokens/1e6*h.outputCentsPerMtok,a.cacheCreate+=m.cacheCreateTokens/1e6*h.cacheCreateCentsPerMtok,a.cacheRead+=m.cacheReadTokens/1e6*h.cacheReadCentsPerMtok,c+=m.inputTokens+m.outputTokens+m.cacheCreateTokens+m.cacheReadTokens}let u=a.input+a.output+a.cacheCreate+a.cacheRead;return{cents:u,dollars:u/100,totalTokens:c,parts:a}}let n=Je(t),s={input:e.inputTokens/1e6*n.inputCentsPerMtok,output:e.outputTokens/1e6*n.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*n.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*n.cacheReadCentsPerMtok},r=s.input+s.output+s.cacheCreate+s.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:s}}function Pt(e){let t=e/100;return t===0?"$0.00":t<.01?"<$0.01":t<1?`$${t.toFixed(2)}`:t<100?`$${t.toFixed(2)}`:t<1e4?`$${t.toFixed(0)}`:`$${(t/1e3).toFixed(1)}k`}function $t(e){return!Number.isFinite(e)||e<0?"0":e<1e3?String(Math.round(e)):e<1e6?`${(e/1e3).toFixed(1)}k`:e<1e9?`${(e/1e6).toFixed(2)}M`:e<1e12?`${(e/1e9).toFixed(2)}B`:`${(e/1e12).toFixed(2)}T`}function di(e){let t=new Map;for(let s of e){let r=s.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=s.input_tokens,o.outputTokens+=s.output_tokens,o.cacheCreateTokens+=s.cache_create_tokens,o.cacheReadTokens+=s.cache_read_tokens,o.messageCount+=s.n,t.set(r,o)}let n=[];for(let[s,r]of t.entries()){let o=Ae({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},s);n.push({model:s,modelLabel:Je(s).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return n.sort((s,r)=>r.cost.cents-s.cost.cents)}function pi(e){let t={};for(let n of e)t[n.model??"__unknown__"]={inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheCreateTokens:n.cacheCreateTokens,cacheReadTokens:n.cacheReadTokens};return{byModel:t}}function xp(e){let t=f(),n=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1735
+ `),a=0,c=0,u=new Set;for(;a<n;){let d=Math.min(s,n-a),p=JSON.stringify([...Cr]),f=r.all(p,d);if(f.length===0)break;let S=new Set;if(e.transaction(()=>{for(let b of f){let R;try{R=JSON.parse(b.raw_json)}catch{Cr.add(b.uuid);continue}let k=Io(R.message);if(!k){Cr.add(b.uuid);continue}o.run({uuid:b.uuid,session_id:b.session_id,model:R.message?.model??null,input:k.inputTokens,output:k.outputTokens,cc:k.cacheCreateTokens,cr:k.cacheReadTokens,ts:b.timestamp}),c+=1,S.add(b.session_id)}for(let b of S)Cs(e,b),u.add(b)})(),a+=f.length,t.onProgress?.({scanned:a,inserted:c,sessionsTouched:u.size,done:f.length<d}),f.length<d)break}return{scanned:a,inserted:c,sessionsTouched:u.size,done:!0}}function cg(e={}){return ag(h(),e)}function lg(e={}){return Lr?!1:(Lr=!0,queueMicrotask(()=>{try{let t=ag(h(),e);og={scanned:t.scanned,inserted:t.inserted,sessionsTouched:t.sessionsTouched,finishedAt:new Date().toISOString()}}catch(t){console.error("[stats.backfill] failed:",t)}finally{Lr=!1}}),!0)}function ug(){return Lr}function ca(){return og}var qR=[[/opus[-_ ]?4[-_. ]?7/i,{label:"Opus 4.7",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/opus[-_ ]?4[-_. ]?6/i,{label:"Opus 4.6",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4[-_. ]?6/i,{label:"Sonnet 4.6",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/sonnet[-_ ]?4[-_. ]?5/i,{label:"Sonnet 4.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku[-_ ]?4[-_. ]?5/i,{label:"Haiku 4.5",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}],[/opus[-_ ]?4(?!.*[5-9])/i,{label:"Opus 4",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4(?!.*[5-9])/i,{label:"Sonnet 4",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?7[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?7/i,{label:"Sonnet 3.7",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?5/i,{label:"Sonnet 3.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?haiku|haiku[-_ ]?3[-_. ]?5/i,{label:"Haiku 3.5",inputCentsPerMtok:80,outputCentsPerMtok:400,cacheCreateCentsPerMtok:100,cacheReadCentsPerMtok:8}],[/3[-_ ]?opus|opus[-_ ]?3/i,{label:"Opus 3",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/3[-_ ]?haiku|haiku(?!.*3[-_. ]?5)/i,{label:"Haiku 3",inputCentsPerMtok:25,outputCentsPerMtok:125,cacheCreateCentsPerMtok:30,cacheReadCentsPerMtok:3}],[/opus/i,{label:"Opus",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet/i,{label:"Sonnet",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku/i,{label:"Haiku",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}]],dg={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function Ze(e){if(!e)return dg;for(let[t,n]of qR)if(t.test(e))return n;return dg}function Ae(e,t){if(e.byModel&&Object.keys(e.byModel).length>0){let a={input:0,output:0,cacheCreate:0,cacheRead:0},c=0;for(let[d,p]of Object.entries(e.byModel)){let f=Ze(d);a.input+=p.inputTokens/1e6*f.inputCentsPerMtok,a.output+=p.outputTokens/1e6*f.outputCentsPerMtok,a.cacheCreate+=p.cacheCreateTokens/1e6*f.cacheCreateCentsPerMtok,a.cacheRead+=p.cacheReadTokens/1e6*f.cacheReadCentsPerMtok,c+=p.inputTokens+p.outputTokens+p.cacheCreateTokens+p.cacheReadTokens}let u=a.input+a.output+a.cacheCreate+a.cacheRead;return{cents:u,dollars:u/100,totalTokens:c,parts:a}}let n=Ze(t),s={input:e.inputTokens/1e6*n.inputCentsPerMtok,output:e.outputTokens/1e6*n.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*n.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*n.cacheReadCentsPerMtok},r=s.input+s.output+s.cacheCreate+s.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:s}}function Zt(e){let t=e/100;return t===0?"$0.00":t<.01?"<$0.01":t<1?`$${t.toFixed(2)}`:t<100?`$${t.toFixed(2)}`:t<1e4?`$${t.toFixed(0)}`:`$${(t/1e3).toFixed(1)}k`}function en(e){return!Number.isFinite(e)||e<0?"0":e<1e3?String(Math.round(e)):e<1e6?`${(e/1e3).toFixed(1)}k`:e<1e9?`${(e/1e6).toFixed(2)}M`:e<1e12?`${(e/1e9).toFixed(2)}B`:`${(e/1e12).toFixed(2)}T`}function la(e){let t=new Map;for(let s of e){let r=s.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=s.input_tokens,o.outputTokens+=s.output_tokens,o.cacheCreateTokens+=s.cache_create_tokens,o.cacheReadTokens+=s.cache_read_tokens,o.messageCount+=s.n,t.set(r,o)}let n=[];for(let[s,r]of t.entries()){let o=Ae({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},s);n.push({model:s,modelLabel:Ze(s).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return n.sort((s,r)=>r.cost.cents-s.cost.cents)}function ua(e){let t={};for(let n of e)t[n.model??"__unknown__"]={inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheCreateTokens:n.cacheCreateTokens,cacheReadTokens:n.cacheReadTokens};return{byModel:t}}function pg(e){let t=h(),n=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1646
1736
  s.message_count,
1647
1737
  s.total_input_tokens, s.total_output_tokens,
1648
1738
  s.total_cache_create_tokens, s.total_cache_read_tokens,
@@ -1657,7 +1747,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1657
1747
  COUNT(*) AS n
1658
1748
  FROM message_usage
1659
1749
  WHERE session_id = ?
1660
- GROUP BY model`).all(e),r=di(s),o=n.total_input_tokens??0,a=n.total_output_tokens??0,c=n.total_cache_create_tokens??0,u=n.total_cache_read_tokens??0,d=Ae({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...pi(r)},n.primary_model);return{sessionId:n.id,project:n.project,startedAt:n.started_at,endedAt:n.ended_at,messageCount:n.message_count,primaryModel:n.primary_model,primaryModelLabel:Je(n.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:Pt(d.cents),tokens:$t(d.totalTokens),model:Je(n.primary_model).label}}}function Np(e){let t=f(),n=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT mu.model,
1750
+ GROUP BY model`).all(e),r=la(s),o=n.total_input_tokens??0,a=n.total_output_tokens??0,c=n.total_cache_create_tokens??0,u=n.total_cache_read_tokens??0,d=Ae({inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,...ua(r)},n.primary_model);return{sessionId:n.id,project:n.project,startedAt:n.started_at,endedAt:n.ended_at,messageCount:n.message_count,primaryModel:n.primary_model,primaryModelLabel:Ze(n.primary_model).label,inputTokens:o,outputTokens:a,cacheCreateTokens:c,cacheReadTokens:u,totalTokens:d.totalTokens,cost:d,byModel:r,display:{dollars:Zt(d.cents),tokens:en(d.totalTokens),model:Ze(n.primary_model).label}}}function mg(e){let t=h(),n=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT mu.model,
1661
1751
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1662
1752
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1663
1753
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1666,12 +1756,12 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1666
1756
  FROM message_usage mu
1667
1757
  JOIN sessions s ON s.id = mu.session_id
1668
1758
  WHERE s.project_id = ?
1669
- GROUP BY mu.model`).all(n.id),r=di(s),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1759
+ GROUP BY mu.model`).all(n.id),r=la(s),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1670
1760
  COALESCE(SUM(total_output_tokens), 0) AS output_tokens,
1671
1761
  COALESCE(SUM(total_cache_create_tokens), 0) AS cache_create_tokens,
1672
1762
  COALESCE(SUM(total_cache_read_tokens), 0) AS cache_read_tokens,
1673
1763
  COUNT(*) AS session_count
1674
- FROM sessions WHERE project_id = ?`).get(n.id),a=Ae({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...pi(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1764
+ FROM sessions WHERE project_id = ?`).get(n.id),a=Ae({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...ua(r)},null),u=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1675
1765
  s.total_input_tokens, s.total_output_tokens,
1676
1766
  s.total_cache_create_tokens, s.total_cache_read_tokens,
1677
1767
  s.primary_model
@@ -1682,7 +1772,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1682
1772
  + COALESCE(s.total_output_tokens,0)
1683
1773
  + COALESCE(s.total_cache_create_tokens,0)
1684
1774
  + COALESCE(s.total_cache_read_tokens,0)) DESC
1685
- LIMIT 10`).all(n.id).map(d=>{let m=Ae({inputTokens:d.total_input_tokens??0,outputTokens:d.total_output_tokens??0,cacheCreateTokens:d.total_cache_create_tokens??0,cacheReadTokens:d.total_cache_read_tokens??0},d.primary_model);return{sessionId:d.id,alias:d.alias,startedAt:d.started_at,totalTokens:m.totalTokens,cost:m}});return{project:n.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:a.totalTokens,cost:a,byModel:r,topSessions:u,display:{dollars:Pt(a.cents),tokens:$t(a.totalTokens)}}}function Op(e="all"){let t=f(),n=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,s=n?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=n?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=n?{since:n}:{},a=l=>n?t.prepare(l).get(o):t.prepare(l).get(),c=l=>n?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1775
+ LIMIT 10`).all(n.id).map(d=>{let p=Ae({inputTokens:d.total_input_tokens??0,outputTokens:d.total_output_tokens??0,cacheCreateTokens:d.total_cache_create_tokens??0,cacheReadTokens:d.total_cache_read_tokens??0},d.primary_model);return{sessionId:d.id,alias:d.alias,startedAt:d.started_at,totalTokens:p.totalTokens,cost:p}});return{project:n.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:a.totalTokens,cost:a,byModel:r,topSessions:u,display:{dollars:Zt(a.cents),tokens:en(a.totalTokens)}}}function gg(e="all"){let t=h(),n=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,s=n?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=n?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=n?{since:n}:{},a=l=>n?t.prepare(l).get(o):t.prepare(l).get(),c=l=>n?t.prepare(l).all(o):t.prepare(l).all(),u=c(`SELECT mu.model,
1686
1776
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1687
1777
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1688
1778
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1691,7 +1781,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1691
1781
  FROM message_usage mu
1692
1782
  JOIN sessions s ON s.id = mu.session_id
1693
1783
  ${s}
1694
- GROUP BY mu.model`),d=di(u),m=0,h=0,b=0,S=0;for(let l of d)m+=l.inputTokens,h+=l.outputTokens,b+=l.cacheCreateTokens,S+=l.cacheReadTokens;let T=Ae({inputTokens:m,outputTokens:h,cacheCreateTokens:b,cacheReadTokens:S,...pi(d)},null),R=n?a(`SELECT
1784
+ GROUP BY mu.model`),d=la(u),p=0,f=0,S=0,y=0;for(let l of d)p+=l.inputTokens,f+=l.outputTokens,S+=l.cacheCreateTokens,y+=l.cacheReadTokens;let b=Ae({inputTokens:p,outputTokens:f,cacheCreateTokens:S,cacheReadTokens:y,...ua(d)},null),R=n?a(`SELECT
1695
1785
  (SELECT COUNT(DISTINCT m.session_id)
1696
1786
  FROM messages m
1697
1787
  JOIN sessions s2 ON s2.id = m.session_id
@@ -1705,7 +1795,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1705
1795
  WHERE (COALESCE(total_input_tokens,0)
1706
1796
  +COALESCE(total_output_tokens,0)
1707
1797
  +COALESCE(total_cache_create_tokens,0)
1708
- +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`).get(),O=c(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
1798
+ +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`).get(),k=c(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
1709
1799
  mu.model,
1710
1800
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1711
1801
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
@@ -1715,7 +1805,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1715
1805
  JOIN sessions s ON s.id = mu.session_id
1716
1806
  ${s}
1717
1807
  GROUP BY day, mu.model
1718
- ORDER BY day ASC`),L=new Map;for(let l of O){if(!l.day)continue;let p=Ae({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.model),g=L.get(l.day)??{tokens:0,cents:0};g.tokens+=p.totalTokens,g.cents+=p.cents,L.set(l.day,g)}let x=[...L.entries()].map(([l,p])=>({day:l,tokens:p.tokens,cents:p.cents})).sort((l,p)=>l.day.localeCompare(p.day)),A=c(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1808
+ ORDER BY day ASC`),M=new Map;for(let l of k){if(!l.day)continue;let m=Ae({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.model),g=M.get(l.day)??{tokens:0,cents:0};g.tokens+=m.totalTokens,g.cents+=m.cents,M.set(l.day,g)}let P=[...M.entries()].map(([l,m])=>({day:l,tokens:m.tokens,cents:m.cents})).sort((l,m)=>l.day.localeCompare(m.day)),D=c(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1719
1809
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1720
1810
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1721
1811
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1731,7 +1821,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1731
1821
  + COALESCE(SUM(mu.output_tokens),0)
1732
1822
  + COALESCE(SUM(mu.cache_create_tokens),0)
1733
1823
  + COALESCE(SUM(mu.cache_read_tokens),0)) DESC
1734
- LIMIT 10`).map(l=>{let p=Ae({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.primary_model);return{sessionId:l.id,project:l.project,alias:l.alias,startedAt:l.started_at,totalTokens:p.totalTokens,cost:p}}),$=c(`SELECT p.id AS project_id,
1824
+ LIMIT 10`).map(l=>{let m=Ae({inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens},l.primary_model);return{sessionId:l.id,project:l.project,alias:l.alias,startedAt:l.started_at,totalTokens:m.totalTokens,cost:m}}),v=c(`SELECT p.id AS project_id,
1735
1825
  p.name AS project,
1736
1826
  mu.model,
1737
1827
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
@@ -1743,29 +1833,29 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1743
1833
  JOIN sessions s ON s.id = mu.session_id
1744
1834
  LEFT JOIN projects p ON p.id = s.project_id
1745
1835
  ${s}
1746
- GROUP BY p.id, mu.model`),te=new Map;for(let l of $){let p=l.project_id??"__none__",g=te.get(p);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},te.set(p,g)),l.sessions>g.sessionsApprox&&(g.sessionsApprox=l.sessions),g.byModel[l.model??"__unknown__"]={inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens}}let H=[...te.values()].map(l=>{let p=0,g=0,_=0,E=0;for(let w of Object.values(l.byModel))p+=w.inputTokens,g+=w.outputTokens,_+=w.cacheCreateTokens,E+=w.cacheReadTokens;let y=Ae({inputTokens:p,outputTokens:g,cacheCreateTokens:_,cacheReadTokens:E,byModel:l.byModel},null);return{project:l.project,sessions:l.sessionsApprox,totalTokens:y.totalTokens,cost:y}});H.sort((l,p)=>p.totalTokens-l.totalTokens);let se=H.slice(0,20),i=t.prepare(`SELECT
1836
+ GROUP BY p.id, mu.model`),F=new Map;for(let l of v){let m=l.project_id??"__none__",g=F.get(m);g||(g={project:l.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},F.set(m,g)),l.sessions>g.sessionsApprox&&(g.sessionsApprox=l.sessions),g.byModel[l.model??"__unknown__"]={inputTokens:l.input_tokens,outputTokens:l.output_tokens,cacheCreateTokens:l.cache_create_tokens,cacheReadTokens:l.cache_read_tokens}}let U=[...F.values()].map(l=>{let m=0,g=0,_=0,E=0;for(let w of Object.values(l.byModel))m+=w.inputTokens,g+=w.outputTokens,_+=w.cacheCreateTokens,E+=w.cacheReadTokens;let T=Ae({inputTokens:m,outputTokens:g,cacheCreateTokens:_,cacheReadTokens:E,byModel:l.byModel},null);return{project:l.project,sessions:l.sessionsApprox,totalTokens:T.totalTokens,cost:T}});U.sort((l,m)=>m.totalTokens-l.totalTokens);let L=U.slice(0,20),i=t.prepare(`SELECT
1747
1837
  (SELECT COUNT(*) FROM messages WHERE role='assistant') AS assistant_messages,
1748
- (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:R.total_sessions,sessionsWithUsage:R.sessions_with_usage,inputTokens:m,outputTokens:h,cacheCreateTokens:b,cacheReadTokens:S,totalTokens:T.totalTokens,cost:T,daily:x,byModel:d,topSessions:A,topRepos:se,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min(Tp(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:Pt(T.cents),tokens:$t(T.totalTokens)}}}B();function yn(e){return Math.max(0,Math.min(1,e))}function mi(e){let t=f(),n=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT COUNT(*) AS cnt,
1838
+ (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:R.total_sessions,sessionsWithUsage:R.sessions_with_usage,inputTokens:p,outputTokens:f,cacheCreateTokens:S,cacheReadTokens:y,totalTokens:b.totalTokens,cost:b,daily:P,byModel:d,topSessions:D,topRepos:L,backfill:{assistantMessages:i.assistant_messages,messagesWithUsage:i.messages_with_usage,pending:Math.max(0,i.assistant_messages-i.messages_with_usage),unrecoverable:Math.min(ig(),Math.max(0,i.assistant_messages-i.messages_with_usage))},display:{dollars:Zt(b.cents),tokens:en(b.totalTokens)}}}q();function Jn(e){return Math.max(0,Math.min(1,e))}function da(e){let t=h(),n=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT COUNT(*) AS cnt,
1749
1839
  MAX(started_at) AS latest
1750
- FROM sessions WHERE project_id = ?`).get(e),r=s.cnt;if(r===0)return{projectId:e,projectName:n.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=yn(r/10),a=s.latest?(Date.now()-new Date(s.latest).getTime())/(1e3*60*60*24):90,c=yn(1-a/90),d=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1751
- FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,m=yn((d-2)/3),h=t.prepare(`SELECT COUNT(*) AS total,
1840
+ FROM sessions WHERE project_id = ?`).get(e),r=s.cnt;if(r===0)return{projectId:e,projectName:n.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=Jn(r/10),a=s.latest?(Date.now()-new Date(s.latest).getTime())/(1e3*60*60*24):90,c=Jn(1-a/90),d=t.prepare(`SELECT AVG(message_count) AS avg_msgs
1841
+ FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,p=Jn((d-2)/3),f=t.prepare(`SELECT COUNT(*) AS total,
1752
1842
  SUM(CASE WHEN m.content_text IS NOT NULL AND m.content_text != '' THEN 1 ELSE 0 END) AS covered
1753
1843
  FROM messages m
1754
1844
  JOIN sessions s ON s.id = m.session_id
1755
- WHERE s.project_id = ?`).get(e),b=h.total>0?h.covered/h.total:.5,S=yn(b),T=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
1845
+ WHERE s.project_id = ?`).get(e),S=f.total>0?f.covered/f.total:.5,y=Jn(S),b=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
1756
1846
  COUNT(DISTINCT st.session_id) AS tagged
1757
1847
  FROM sessions s
1758
1848
  LEFT JOIN session_tags st ON st.session_id = s.id
1759
- WHERE s.project_id = ?`).get(e),R=T.total>0?T.tagged/T.total:0,O=yn(R),L=Math.round((o*.2+c*.25+m*.15+S*.2+O*.2)*100);return{projectId:e,projectName:n.name,score:L,breakdown:{sessionCount:{raw:r,score:o,weight:.2},recency:{daysSinceLastSession:Math.round(a),score:c,weight:.25},fragmentation:{avgMessages:Math.round(d*10)/10,score:m,weight:.15},searchCoverage:{ratio:Math.round(b*100)/100,score:S,weight:.2},tagCoverage:{ratio:Math.round(R*100)/100,score:O,weight:.2}}}}function Lp(){let t=f().prepare("SELECT id FROM projects ORDER BY name").all(),n=[];for(let s of t){let r=mi(s.id);r&&n.push(r)}return n}B();import{execFile as Wy}from"node:child_process";import{promisify as qy}from"node:util";import{stat as Xy}from"node:fs/promises";var Jy=qy(Wy),Gy=60,Yy=7,zy=7,Ky=5e3;function Vy(){let e=f(),t=n=>{try{return!!e.prepare(n).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1849
+ WHERE s.project_id = ?`).get(e),R=b.total>0?b.tagged/b.total:0,k=Jn(R),M=Math.round((o*.2+c*.25+p*.15+y*.2+k*.2)*100);return{projectId:e,projectName:n.name,score:M,breakdown:{sessionCount:{raw:r,score:o,weight:.2},recency:{daysSinceLastSession:Math.round(a),score:c,weight:.25},fragmentation:{avgMessages:Math.round(d*10)/10,score:p,weight:.15},searchCoverage:{ratio:Math.round(S*100)/100,score:y,weight:.2},tagCoverage:{ratio:Math.round(R*100)/100,score:k,weight:.2}}}}function fg(){let t=h().prepare("SELECT id FROM projects ORDER BY name").all(),n=[];for(let s of t){let r=da(s.id);r&&n.push(r)}return n}q();import{execFile as XR}from"node:child_process";import{promisify as JR}from"node:util";import{stat as GR}from"node:fs/promises";var YR=JR(XR),KR=60,zR=7,VR=7,QR=5e3;function ZR(){let e=h(),t=n=>{try{return!!e.prepare(n).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1760
1850
  WHERE (COALESCE(total_input_tokens,0)
1761
1851
  + COALESCE(total_output_tokens,0)
1762
1852
  + COALESCE(total_cache_create_tokens,0)
1763
1853
  + COALESCE(total_cache_read_tokens,0)) > 0
1764
- LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function gi(e){if(!e)return[];let t=new Set,n=[];for(let s of e.split(",")){let r=s.trim().toLowerCase();!r||t.has(r)||(t.add(r),n.push(r))}return n}function Cp(e){return{sessionId:e.session_id,project:e.project,alias:e.alias,startedAt:e.started_at,endedAt:e.ended_at,firstUserMessage:e.first_user_message}}function Zy(){let e=f(),t=e.prepare(`SELECT ss.keywords
1854
+ LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function pa(e){if(!e)return[];let t=new Set,n=[];for(let s of e.split(",")){let r=s.trim().toLowerCase();!r||t.has(r)||(t.add(r),n.push(r))}return n}function _g(e){return{sessionId:e.session_id,project:e.project,alias:e.alias,startedAt:e.started_at,endedAt:e.ended_at,firstUserMessage:e.first_user_message}}function ek(){let e=h(),t=e.prepare(`SELECT ss.keywords
1765
1855
  FROM session_semantic ss
1766
1856
  JOIN sessions s ON s.id = ss.session_id
1767
1857
  WHERE s.started_at IS NOT NULL
1768
- AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:Yy});if(t.length===0)return null;let n=new Set;for(let o of t)for(let a of gi(o.keywords))n.add(a);if(n.size===0)return null;let s=e.prepare(`SELECT ss.session_id AS session_id,
1858
+ AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:zR});if(t.length===0)return null;let n=new Set;for(let o of t)for(let a of pa(o.keywords))n.add(a);if(n.size===0)return null;let s=e.prepare(`SELECT ss.session_id AS session_id,
1769
1859
  ss.summary AS summary,
1770
1860
  ss.keywords AS keywords,
1771
1861
  p.name AS project,
@@ -1781,7 +1871,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1781
1871
  WHERE s.started_at IS NOT NULL
1782
1872
  AND s.message_count > 2
1783
1873
  AND julianday('now') - julianday(s.started_at) >= @ageDays
1784
- ORDER BY s.started_at ASC`).all({ageDays:Gy});if(s.length===0)return null;let r=null;for(let o of s){let c=gi(o.keywords).filter(u=>n.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{...Cp(r.row),summary:r.row.summary,keywords:gi(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function Qy(){let t=f().prepare(`SELECT s.id AS session_id,
1874
+ ORDER BY s.started_at ASC`).all({ageDays:KR});if(s.length===0)return null;let r=null;for(let o of s){let c=pa(o.keywords).filter(u=>n.has(u));c.length!==0&&(!r||c.length>r.overlap.length)&&(r={row:o,overlap:c})}return r?{..._g(r.row),summary:r.row.summary,keywords:pa(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function tk(){let t=h().prepare(`SELECT s.id AS session_id,
1785
1875
  p.name AS project,
1786
1876
  NULLIF(sa.alias, '') AS alias,
1787
1877
  s.started_at AS started_at,
@@ -1800,33 +1890,51 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1800
1890
  AND (COALESCE(s.total_input_tokens, 0)
1801
1891
  + COALESCE(s.total_output_tokens, 0)
1802
1892
  + COALESCE(s.total_cache_create_tokens, 0)
1803
- + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:zy});if(t.length===0)return null;let n=null;for(let s of t){let r=Ae({inputTokens:s.input_tokens,outputTokens:s.output_tokens,cacheCreateTokens:s.cache_create_tokens,cacheReadTokens:s.cache_read_tokens},s.primary_model);r.cents<=0||(!n||r.cents>n.cents)&&(n={row:s,cents:r.cents,totalTokens:r.totalTokens})}return n?{...Cp(n.row),totalTokens:n.totalTokens,costCents:n.cents,costDisplay:Pt(n.cents),tokensDisplay:$t(n.totalTokens),primaryModel:n.row.primary_model,primaryModelLabel:Je(n.row.primary_model).label}:null}async function ew(e){try{if(!(await Xy(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await Jy("git",["rev-parse","HEAD"],{cwd:e,timeout:Ky}),n=t.trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}async function tw(){let e=f(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1893
+ + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:VR});if(t.length===0)return null;let n=null;for(let s of t){let r=Ae({inputTokens:s.input_tokens,outputTokens:s.output_tokens,cacheCreateTokens:s.cache_create_tokens,cacheReadTokens:s.cache_read_tokens},s.primary_model);r.cents<=0||(!n||r.cents>n.cents)&&(n={row:s,cents:r.cents,totalTokens:r.totalTokens})}return n?{..._g(n.row),totalTokens:n.totalTokens,costCents:n.cents,costDisplay:Zt(n.cents),tokensDisplay:en(n.totalTokens),primaryModel:n.row.primary_model,primaryModelLabel:Ze(n.row.primary_model).label}:null}async function nk(e){try{if(!(await GR(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await YR("git",["rev-parse","HEAD"],{cwd:e,timeout:QR}),n=t.trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}async function sk(){let e=h(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1804
1894
  FROM sessions s
1805
1895
  WHERE s.cwd IS NOT NULL AND s.started_at IS NOT NULL
1806
1896
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1807
- LIMIT 1`).get();if(!t?.cwd)return null;let n=await ew(t.cwd);if(!n)return null;let s=is(n);if(s.length===0)return null;let r=s[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1897
+ LIMIT 1`).get();if(!t?.cwd)return null;let n=await nk(t.cwd);if(!n)return null;let s=Ls(n);if(s.length===0)return null;let r=s[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1808
1898
  FROM sessions s
1809
- WHERE s.id = ?`).get(r.sessionId);return{sessionId:r.sessionId,project:r.project,alias:r.alias,startedAt:r.startedAt,endedAt:o?.ended_at??r.endedAt,firstUserMessage:o?.first_user_message??null,commitSha:r.commitSha,shortSha:r.commitSha.slice(0,7),subject:r.subject,committedAt:r.committedAt,cwd:t.cwd}}async function vp(){let e=Vy(),t=e.semantic?Promise.resolve().then(()=>{try{return Zy()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),n=e.cost?Promise.resolve().then(()=>{try{return Qy()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),s=e.git?tw().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,n,s]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}ct();B();var _i=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 fi(e,t={}){let n=t.limit??nw()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new _i(r,n)}function nw(){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)}ur();import{Worker as sw}from"node:worker_threads";import{join as rw}from"node:path";var hi=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"},ow=1e4;function iw(){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 Ei(e){let t=e.timeoutMs??iw()??ow,n=(e.workerFactory??aw)();return new Promise((s,r)=>{let o=setTimeout(()=>{n.terminate().catch(()=>{}),r(new hi(t))},t);n.once("message",a=>{clearTimeout(o);let c=a;c&&c.ok===!0&&Array.isArray(c.hits)?s(c.hits):r(new Error(c?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",a=>{clearTimeout(o),n.terminate().catch(()=>{}),r(a)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function aw(){let e=rw(Ln(),"dist","daemon","query-worker.js");return new sw(e)}async function Ip(e,t=50){return fi(f()),Ei({query:e,limit:t})}async function jp(e,t=10,n=.65){let s=f();fi(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=await Ei({precomputedVector:o.embedding,limit:t*5}),c=new Map;for(let d of a){if(d.sessionId===e)continue;let m=c.get(d.sessionId);(m===void 0||d.distance<m)&&c.set(d.sessionId,d.distance)}let u=[];for(let[d,m]of c){let h=1-m;h>=n&&u.push({sessionId:d,similarity:h})}return u.sort((d,m)=>m.similarity-d.similarity),u.slice(0,t)}function cw(){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 Mp(e){let t=cw(),n=new Map;for(let r of e)for(let o=0;o<r.length;o++){let a=r[o],c=1/(t+o+1),u=n.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):n.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let s=[];for(let[r,o]of n)s.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return s.sort((r,o)=>o.score-r.score),s}B();ct();B();function Dp(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function lw(e){let t=[],o=0;for(;o<e.length;){let a=[],c=0;for(;a.length<5&&o<e.length;){let d=e[o],m=Dp(d.content_text??"");if(c+m.length>2e3&&a.length>=3)break;a.push(d),c+=m.length,o++}if(a.length===0)break;let u=a.map(d=>{let m=d.role??"system",h=Dp(d.content_text??"");return`[${m}] ${h}`}).join(`
1810
-
1811
- `);t.push({messageUuids:a.map(d=>d.uuid),text:u}),o<e.length&&a.length>=3&&(o=Math.max(o-1,o-1))}return t}function Fp(e){let n=f().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return lw(n)}var Hp=2e3,uw=1e4,it=null,Zs=!1,Wp=null,bi=0;function qp(e){bi=Math.max(0,Math.floor(e))}var ve=new Set,Xp=-1;function Jp(e){ve.add(e)}function Gp(){ve.add(Xp)}function Pp(e){if(ve.has(Xp))return!0;if(ve.size===0)return!1;let t=f().prepare("SELECT project_id FROM sessions WHERE id = ?").get(e);return t?ve.has(t.project_id):!1}var $p=10,dw=200,pw=2e3;function mw(){let e=Number(process.env.RECALL_VECTOR_HARD_CAP??"");return Number.isFinite(e)&&e>0?Math.min(pw,Math.floor(e)):dw}var gw=3,Up=1e3,wn=new Map,Si=new Set;function Bp(e,t,n,s){if(e.size>=Up&&(e instanceof Map,!e.has(t))){let r=e.keys().next();r.done||(e.delete(r.value),console.warn(`[vector-worker] ${s??"tracking-map"} hit ${Up}-entry cap; evicted oldest entry`))}e instanceof Map?e.set(t,n):e.add(t)}var Rn=[],_w=50;function Ti(){if(Rn.length<8)return null;let e=Rn.slice(-30),t=e.reduce((s,r)=>s+r.chunks,0),n=e.reduce((s,r)=>s+r.durationMs,0);return n<=0||t===0?null:{samples:e.length,chunksPerSec:t/(n/1e3),sessionsPerSec:e.length/(n/1e3),avgChunksPerSession:t/e.length}}function fw(){return f().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}async function hw(){let e=f(),t=e.prepare("SELECT DISTINCT session_id FROM chunk_queue ORDER BY id LIMIT 1").get();if(!t)return ve.size>0&&ve.clear(),!1;if(Pp(t.session_id)||Si.has(t.session_id))return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(t.session_id),!0;let n=t.session_id,s=Date.now();try{e.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(n),e.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(n);let r=Fp(n),o=bi>0?bi:1/0,a=Math.min(o,mw()),c=a<r.length?r.slice(0,a):r;if(c.length===0)return e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),!0;let u=e.prepare("INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at) VALUES (?, ?, ?, 'bge-base-en-v1.5', 768, 0, datetime('now'))"),d=e.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)"),m=0,h=!1;for(let b=0;b<c.length;b+=$p){if(Pp(n)){h=!0;break}let S=Math.min(c.length,b+$p),T=c.slice(b,S),R=T.map(x=>x.text),O=await at(R);e.transaction(()=>{for(let x=0;x<T.length;x++){let F=u.run(n,JSON.stringify(T[x].messageUuids),T[x].text),A=Buffer.from(O[x].buffer,O[x].byteOffset,O[x].byteLength);d.run(BigInt(F.lastInsertRowid),A)}})(),m+=T.length,await new Promise(x=>setImmediate(x))}h&&(e.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(n),e.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(n)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n),Wp=new Date().toISOString(),Rn.push({ts:Date.now(),chunks:m,durationMs:Date.now()-s}),Rn.length>_w&&Rn.shift(),wn.has(n)&&wn.delete(n)}catch(r){console.error("[vector-worker] failed for session",n,r);let o=(wn.get(n)??0)+1;Bp(wn,n,o,"failureCounts"),o>=gw&&(Bp(Si,n,void 0,"blacklist"),wn.delete(n),console.error(`[vector-worker] blacklisted session ${n} after ${o} failures \u2014 will not retry until daemon restart`)),e.prepare("DELETE FROM chunk_queue WHERE session_id = ?").run(n)}finally{if(ve.size>0)try{let r=e.prepare("SELECT project_id FROM sessions WHERE id = ?").get(n);r!==void 0&&ve.has(r.project_id)&&(e.prepare("SELECT 1 FROM chunk_queue cq JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ? LIMIT 1").get(r.project_id)||ve.delete(r.project_id))}catch(r){console.error("[vector-worker] cancelledProjects cleanup failed:",r)}}return!0}async function Yp(){if(!_e().loaded)return;let e=await hw();it!==null&&clearTimeout(it),it=setTimeout(()=>{Yp()},e?Hp:uw)}function kn(){if(!Zs){if(!_e().loaded){console.error("[vector-worker] cannot start: embedder not loaded");return}Zs=!0,it=setTimeout(()=>{Yp()},Hp)}}function zp(){Zs=!1,it!==null&&(clearTimeout(it),it=null)}function Ie(){return{running:Zs,queueDepth:fw(),lastProcessedAt:Wp,blacklistedCount:Si.size}}ee();import{existsSync as Zp,mkdirSync as Kp,rmSync as Vp,createWriteStream as Ew,statSync as bw}from"node:fs";import{join as Qs}from"node:path";import{createHash as Sw}from"node:crypto";import{readFile as Tw}from"node:fs/promises";var yw="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",Qp=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function em(){return Qs(W,"models","BAAI","bge-base-en-v1.5")}function Ge(){let e=em();return Qp.every(t=>Zp(Qs(e,t.path)))}async function tm(e){let t=em();Kp(t,{recursive:!0}),Kp(Qs(t,"onnx"),{recursive:!0});for(let n of Qp){let s=Qs(t,n.path),r=yw+n.path,o=0;Zp(s)&&(o=bw(s).size);let a={};o>0&&(a.Range=`bytes=${o}-`);let c=await fetch(r,{headers:a});if(!c.ok&&c.status!==206)throw new Error(`Failed to download ${n.path}: HTTP ${c.status}`);let u=c.headers.get("content-length"),d=u?o+Number(u):0,m=c.body;if(!m)throw new Error(`No response body for ${n.path}`);let h=Ew(s,{flags:o>0?"a":"w"}),b=m.getReader(),S=o;for(;;){let{done:O,value:L}=await b.read();if(O)break;h.write(Buffer.from(L)),S+=L.byteLength,e?.(n.path,S,d)}if(h.end(),await new Promise((O,L)=>{h.on("finish",O),h.on("error",L)}),n.sha256==="TODO_PLACEHOLDER")throw Vp(s),new Error(`Refusing to install: SHA-256 not pinned for ${n.path}. Update model-download.ts.`);let T=await Tw(s);if(Sw("sha256").update(T).digest("hex")!==n.sha256)throw Vp(s),new Error(`SHA-256 mismatch for ${n.path}`)}}B();var ww=[/\btask\s+complete/i,/\bdone\b/i,/\bfinished\b/i,/\bimplemented\b/i,/\bcompleted\b/i,/\bshipped\b/i,/\ball\s+(?:tests?\s+)?pass/i,/\bsuccessfully\b/i],Rw=1440*60*1e3;function kw(e){let t=f(),n=t.prepare(`SELECT content_text, tool_names FROM messages
1899
+ WHERE s.id = ?`).get(r.sessionId);return{sessionId:r.sessionId,project:r.project,alias:r.alias,startedAt:r.startedAt,endedAt:o?.ended_at??r.endedAt,firstUserMessage:o?.first_user_message??null,commitSha:r.commitSha,shortSha:r.commitSha.slice(0,7),subject:r.subject,committedAt:r.committedAt,cwd:t.cwd}}async function hg(){let e=ZR(),t=e.semantic?Promise.resolve().then(()=>{try{return ek()}catch(c){return console.error("[discover.rediscovered]",c),null}}):Promise.resolve(null),n=e.cost?Promise.resolve().then(()=>{try{return tk()}catch(c){return console.error("[discover.expensive]",c),null}}):Promise.resolve(null),s=e.git?sk().catch(c=>(console.error("[discover.authored]",c),null)):Promise.resolve(null),[r,o,a]=await Promise.all([t,n,s]);return{rediscovered:r,expensive:o,authored:a,availability:e,generatedAt:new Date().toISOString()}}bt();q();var ma=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,n){super(`semantic search refused: vec_chunks has ${t} rows (cap ${n}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=n}};function ga(e,t={}){let n=t.limit??rk()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new ma(r,n)}function rk(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}Vn();import{Worker as ok}from"node:worker_threads";import{join as ik}from"node:path";var fa=class extends Error{constructor(n){super(`semantic kNN exceeded ${n}ms wall-clock budget -- query aborted`);this.timeoutMs=n}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},ak=1e4;function ck(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function _a(e){let t=e.timeoutMs??ck()??ak,n=(e.workerFactory??lk)();return new Promise((s,r)=>{let o=setTimeout(()=>{n.terminate().catch(()=>{}),r(new fa(t))},t);n.once("message",a=>{clearTimeout(o);let c=a;c&&c.ok===!0&&Array.isArray(c.hits)?s(c.hits):r(new Error(c?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",a=>{clearTimeout(o),n.terminate().catch(()=>{}),r(a)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function lk(){let e=ik(ht(),"dist","daemon","query-worker.js");return new ok(e)}async function Eg(e,t=50){return ga(h()),_a({query:e,limit:t})}async function bg(e,t=10,n=.65){let s=h();ga(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let a=await _a({precomputedVector:o.embedding,limit:t*5}),c=new Map;for(let d of a){if(d.sessionId===e)continue;let p=c.get(d.sessionId);(p===void 0||d.distance<p)&&c.set(d.sessionId,d.distance)}let u=[];for(let[d,p]of c){let f=1-p;f>=n&&u.push({sessionId:d,similarity:f})}return u.sort((d,p)=>p.similarity-d.similarity),u.slice(0,t)}function uk(){let e=process.env.RECALL_RRF_K;if(e){let t=parseInt(e,10);if(!isNaN(t)&&t>=1&&t<=1e3)return t}return 60}function Sg(e){let t=uk(),n=new Map;for(let r of e)for(let o=0;o<r.length;o++){let a=r[o],c=1/(t+o+1),u=n.get(a.id);u?(u.score+=c,u.lanes.push(a.lane),o+1<u.bestRank&&(u.bestRank=o+1,u.bestData=a.data)):n.set(a.id,{score:c,lanes:[a.lane],bestRank:o+1,bestData:a.data})}let s=[];for(let[r,o]of n)s.push({id:r,score:o.score,lanes:o.lanes,data:o.bestData});return s.sort((r,o)=>o.score-r.score),s}ne();import{existsSync as wg,mkdirSync as yg,rmSync as Tg,createWriteStream as dk,statSync as pk}from"node:fs";import{join as vr}from"node:path";import{createHash as mk}from"node:crypto";import{readFile as gk}from"node:fs/promises";var fk="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",Rg=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function kg(){return vr(W,"models","BAAI","bge-base-en-v1.5")}function et(){let e=kg();return Rg.every(t=>wg(vr(e,t.path)))}async function Ag(e){let t=kg();yg(t,{recursive:!0}),yg(vr(t,"onnx"),{recursive:!0});for(let n of Rg){let s=vr(t,n.path),r=fk+n.path,o=0;wg(s)&&(o=pk(s).size);let a={};o>0&&(a.Range=`bytes=${o}-`);let c=await fetch(r,{headers:a});if(!c.ok&&c.status!==206)throw new Error(`Failed to download ${n.path}: HTTP ${c.status}`);let u=c.headers.get("content-length"),d=u?o+Number(u):0,p=c.body;if(!p)throw new Error(`No response body for ${n.path}`);let f=dk(s,{flags:o>0?"a":"w"}),S=p.getReader(),y=o;for(;;){let{done:k,value:M}=await S.read();if(k)break;f.write(Buffer.from(M)),y+=M.byteLength,e?.(n.path,y,d)}if(f.end(),await new Promise((k,M)=>{f.on("finish",k),f.on("error",M)}),n.sha256==="TODO_PLACEHOLDER")throw Tg(s),new Error(`Refusing to install: SHA-256 not pinned for ${n.path}. Update model-download.ts.`);let b=await gk(s);if(mk("sha256").update(b).digest("hex")!==n.sha256)throw Tg(s),new Error(`SHA-256 mismatch for ${n.path}`)}}q();var _k=[/\btask\s+complete/i,/\bdone\b/i,/\bfinished\b/i,/\bimplemented\b/i,/\bcompleted\b/i,/\bshipped\b/i,/\ball\s+(?:tests?\s+)?pass/i,/\bsuccessfully\b/i],hk=1440*60*1e3;function Ek(e){let t=h(),n=t.prepare(`SELECT content_text, tool_names FROM messages
1812
1900
  WHERE session_id = ? AND role = 'assistant'
1813
- ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let u of n)if(u.content_text&&ww.some(d=>d.test(u.content_text))){s=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1814
- WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let u of r){let d=u.tool_names??"",m=u.content_text??"";/\bWrite\b|\bEdit\b/.test(d)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(m)&&/pass|ok|✓/i.test(m)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(m)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(m)||/tsc.*(?:0 errors|no errors)/i.test(m))&&(o.buildSuccess=!0)}return s?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:s}:{status:"neutral",evidence:o,claimFound:s}}function Aw(e){let t=kw(e);return f().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function nm(e){let n=f().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(n?.verification_status&&n.verification_computed_at&&Date.now()-n.verification_computed_at<Rw){let s={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:n.verification_status,evidence:s,claimFound:n.verification_status!=="neutral"}}return Aw(e)}import{readFileSync as xw,writeFileSync as Nw,mkdirSync as Ow,chmodSync as Lw}from"node:fs";import{join as sm}from"node:path";import{homedir as rm}from"node:os";function om(){return sm(rm(),".recall","config.json")}function im(){try{return JSON.parse(xw(om(),"utf-8"))}catch{return{}}}function Cw(e){let t=om();Ow(sm(rm(),".recall"),{recursive:!0}),Nw(t,JSON.stringify(e,null,2)+`
1815
- `,"utf-8"),Lw(t,384)}function yi(){let t=im().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function am(e){let t=im();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},Cw(t)}var Uw=5e3,Li={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},wi=0,Ri=Li,ki=null;async function Bw(){return Date.now()-wi>=Uw&&!ki&&(ki=rs().then(n=>(Ri=n,wi=Date.now(),n)).catch(()=>(wi=Date.now(),Ri=Li,Li)).finally(()=>{ki=null})),Ri}var Hw=2e3,Ww=6,nr=new Map;function An(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function qw(e,t){let n=Date.now(),s=(nr.get(e)??[]).filter(a=>n-a.ts<Hw);return nr.set(e,s),s.length<2||s[s.length-1].name===t?!1:s.slice(0,-1).some(a=>a.name===t)}function Xw(e,t){let n=nr.get(e)??[];for(n.push({name:t,ts:Date.now()});n.length>Ww;)n.shift();nr.set(e,n)}function lm(e,t){let n=t.trim();if(!n)return 0;if(le(n)){let o=bt(n);if(!o)return 0;n=o}if(de(n))return 0;if(qw(e,n))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${n}", flap signature (competing editor sync sources)`),0;Xw(e,n);let s=M.sessionsFor(e),r=0;for(let o of s)try{if(Te(o)===n)continue;he(o,n),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${n}" propagated to ${r} session(s)`),r}var um=(()=>{try{let e=sr(vi(Ii(import.meta.url)),"..","..","package.json");return JSON.parse(Oi(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),Ai=!1,xi=!1,Ni=!1,Gw=vi(Ii(import.meta.url)),Ci=sr(Gw,"..","web"),mm=sr(Ci,"index.html"),Yw=jw(mm);function dm(){return f().prepare(`SELECT
1901
+ ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let u of n)if(u.content_text&&_k.some(d=>d.test(u.content_text))){s=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1902
+ WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let u of r){let d=u.tool_names??"",p=u.content_text??"";/\bWrite\b|\bEdit\b/.test(d)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(p)&&/pass|ok|✓/i.test(p)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(p)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(p)||/tsc.*(?:0 errors|no errors)/i.test(p))&&(o.buildSuccess=!0)}return s?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:s}:{status:"neutral",evidence:o,claimFound:s}}function bk(e){let t=Ek(e);return h().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function xg(e){let n=h().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(n?.verification_status&&n.verification_computed_at&&Date.now()-n.verification_computed_at<hk){let s={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:n.verification_status,evidence:s,claimFound:n.verification_status!=="neutral"}}return bk(e)}import{readFileSync as Sk,writeFileSync as yk,mkdirSync as Tk,chmodSync as wk}from"node:fs";import{join as Ng}from"node:path";import{homedir as Og}from"node:os";function Cg(){return Ng(Og(),".recall","config.json")}function Lg(){try{return JSON.parse(Sk(Cg(),"utf-8"))}catch{return{}}}function Rk(e){let t=Cg();Tk(Ng(Og(),".recall"),{recursive:!0}),yk(t,JSON.stringify(e,null,2)+`
1903
+ `,"utf-8"),wk(t,384)}function ha(){let t=Lg().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function vg(e){let t=Lg();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},Rk(t)}var Ik=5e3,ka={scanned:0,linked:0,renamed:0,skipped_manual:0,ambiguous_cwd:0},Ea=0,ba=ka,Sa=null;async function Mk(){return Date.now()-Ea>=Ik&&!Sa&&(Sa=Os().then(n=>(ba=n,Ea=Date.now(),n)).catch(()=>(Ea=Date.now(),ba=ka,ka)).finally(()=>{Sa=null})),ba}var jk=2e3,Dk=6,jr=new Map;function Gn(e){return e.replace(/[\\%_]/g,t=>"\\"+t)}function Pk(e,t){let n=Date.now(),s=(jr.get(e)??[]).filter(a=>n-a.ts<jk);return jr.set(e,s),s.length<2||s[s.length-1].name===t?!1:s.slice(0,-1).some(a=>a.name===t)}function Fk(e,t){let n=jr.get(e)??[];for(n.push({name:t,ts:Date.now()});n.length>Dk;)n.shift();jr.set(e,n)}function Mg(e,t){let n=t.trim();if(!n)return 0;if(le(n)){let o=Ct(n);if(!o)return 0;n=o}if(de(n))return 0;if(Pk(e,n))return console.log(`[terminal] dropping rename of pid ${e} \u2192 "${n}", flap signature (competing editor sync sources)`),0;Fk(e,n);let s=j.sessionsFor(e),r=0;for(let o of s)try{if(ye(o)===n)continue;he(o,n),r++}catch{}return r>0&&console.log(`[terminal] rename of pid ${e} \u2192 "${n}" propagated to ${r} session(s)`),r}var jg=(()=>{try{let e=Dr(xa(Na(import.meta.url)),"..","..","package.json");return JSON.parse(Ra(e,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})(),ya=!1,Ta=!1,wa=!1,Uk=xa(Na(import.meta.url)),Aa=Dr(Uk,"..","web"),Fg=Dr(Aa,"index.html"),Hk=xk(Fg);function Dg(){return h().prepare(`SELECT
1816
1904
  (SELECT COUNT(*) FROM projects) AS projects,
1905
+ (SELECT COUNT(DISTINCT COALESCE(repo_root, decoded_path)) FROM projects) AS repos,
1817
1906
  (SELECT COUNT(*) FROM sessions) AS sessions,
1818
1907
  (SELECT COUNT(*) FROM messages) AS messages,
1819
1908
  (SELECT MIN(started_at) FROM sessions WHERE started_at IS NOT NULL) AS earliest,
1820
- (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var zw=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,Kw=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function je(e,t){if((await Ke()).tier!=="pro")return e.json({error:"pro_required",message:"This feature requires a Claude Recall Pro license.",upgrade_url:"https://clauderecall.com/pricing",activate_command:"recall activate <license-key>"},402);await t()}var er=new Map,gm=0,pm=0,_m=null,Vw=6e4;function Zw(){gm+=1;let e=Date.now();e-pm<Vw||(pm=e,console.warn("[auth] /api/terminal/* request rejected without a valid X-Recall-Token. The VS Code / Cursor extension is likely outdated relative to the daemon \u2014 tab names will not flow through, and session titles will fall back to the heuristic first-message snippet. Reinstall: `code --install-extension extensions/vscode/clauderecall-vscode-*.vsix` and restart the extension host. Run `recall doctor` for a full pipeline view."))}function Qw(){_m=new Date().toISOString()}function e0(){let e=wc();return{silentTerminalRejections:gm,lastTerminalSyncAt:_m,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns}}}function tr(e,t){if(t)return"";let n=e;return` AND COALESCE(${n}.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[skill]%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are summarizing a Claude Code session%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are extracting a structured Output Index%' AND COALESCE(${n}.title_quality, '') != 'programmatic'`}function t0(e){let t=new vw;if(t.use("*",Jw({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let p=i.req.raw.headers.get("host")??"";if(!zw.test(p))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!Kw.test(g))return i.text("Forbidden: cross-origin request rejected",403);await l()}),e){let i=Buffer.from(e,"utf8");t.use("/api/*",async(l,p)=>{if(l.req.method==="GET"&&l.req.path==="/api/health")return p();let g=l.req.raw.headers.get("x-recall-token")??"";!g&&l.req.method==="GET"&&(g=new URL(l.req.url).searchParams.get("token")??"");let _=!1;if(g.length===e.length)try{_=Pw(Buffer.from(g,"utf8"),i)}catch{_=!1}return _?p():(l.req.path.startsWith("/api/terminal/")&&Zw(),l.json({error:"unauthorized"},401))})}t.use("*",async(i,l)=>{await l(),i.res.headers.set("Content-Security-Policy","default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://clauderecall.com; font-src 'self' data:; connect-src 'self' data: https://clauderecall.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"),i.res.headers.set("X-Content-Type-Options","nosniff"),i.res.headers.set("X-Frame-Options","DENY"),i.res.headers.set("Referrer-Policy","no-referrer"),i.res.headers.set("Cross-Origin-Resource-Policy","same-origin")}),t.get("/api/health",i=>i.json({status:"ok",version:um,uptimeSeconds:Math.round(process.uptime()),pipeline:e0()})),t.get("/api/stats",i=>i.json(dm())),t.get("/api/stats/session/:id",i=>{let l=xp(i.req.param("id"));return l?i.json(l):i.json({error:"session not found"},404)}),t.get("/api/stats/project/:name",i=>{let l=Np(i.req.param("name"));return l?i.json(l):i.json({error:"project not found"},404)}),t.get("/api/stats/overview",i=>{let l=i.req.query("range"),p=l==="7d"?"7d":l==="30d"?"30d":"all";return i.json(Op(p))}),t.post("/api/stats/backfill",async i=>{let l=await i.req.json().catch(()=>({})),p=l.limit?Math.max(1,Math.min(1e5,Number(l.limit))):5e3;if(p>5e3){let _=Rp({limit:p});return i.json({mode:"background",started:_,alreadyRunning:!_&&kp(),limit:p,lastRun:ui()})}let g=wp({limit:p});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:p,result:g,lastRun:ui()})}),t.get("/api/stats/health",i=>i.json(Lp())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),p=mi(l);return p?i.json(p):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:yi()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&am(l.enabled),i.json({enabled:yi()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),p=nm(l);return i.json(p)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=f().prepare(`SELECT tool_names, raw_json FROM messages
1821
- WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''`).all(l),_=g.length,E=new Set;for(let y of g){if(!/Read|Write|Edit/.test(y.tool_names))continue;let w=y.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(w)for(let N of w){let k=N.match(/":\s*"([^"]+)"/);k&&E.add(k[1])}}return i.json({filesReferenced:E.size,toolCallCount:_})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),p=to(l);if(p.length>0||i.req.query("refresh")!=="1")return i.json({commits:p});let g=await eo(l);return i.json({commits:to(l),status:g.status})}),t.get("/api/commits/:sha/session",i=>{let l=i.req.param("sha");return/^[0-9a-fA-F]{4,40}$/.test(l)?i.json({sessions:is(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await Ke();return i.json(l)}),t.post("/api/feedback",async i=>{let l;try{l=await i.req.json()}catch{return i.json({error:"invalid json"},400)}if(!l||typeof l!="object")return i.json({error:"invalid body"},400);let p=process.env.RECALL_FEEDBACK_API??"https://clauderecall.com/api/feedback",g=p==="https://clauderecall.com/api/feedback",_=await Ke(),E=Ht(),y=g&&_.tier==="pro"&&E?E.license_jwt:null,w=(()=>{try{let C=sr(vi(Ii(import.meta.url)),"..","..","package.json");return JSON.parse(Oi(C,"utf8")).version}catch{return"unknown"}})(),N=l,k={score:N.score,comment:N.comment??null,surface:"web",version:typeof N.version=="string"?N.version:w,os:typeof N.os=="string"?N.os:process.platform,trigger_kind:typeof N.trigger_kind=="string"?N.trigger_kind:"manual",license_jwt:y};try{let C=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(k)}),I=await C.json().catch(()=>({}));return i.json(I,C.status)}catch(C){let I=C instanceof Error?C.message:"network error";return i.json({error:"upstream_unreachable",detail:I},502)}}),t.get("/api/discover/today",je,async i=>{try{return i.json(await vp())}catch(l){return console.error("[discover.today]",l),i.json({rediscovered:null,expensive:null,authored:null,availability:{semantic:!1,cost:!1,git:!1},generatedAt:new Date().toISOString(),error:l.message},500)}}),t.get("/api/macro-repos",i=>i.json({macro_repos:Ho(),orphan_projects:td()})),t.get("/api/bug-synthesis",i=>{let l=i.req.query("scope"),p=i.req.query("target_id"),g=i.req.query("limit"),_=l==="cluster"||l==="project"?l:void 0,E=g?Math.max(1,Number(g)):50,y=ld({scope:_,target_id:p??void 0,limit:E});return i.json({results:y})}),t.get("/api/bug-synthesis/counts",i=>{let l=i.req.query("scope");if(l!=="cluster"&&l!=="project")return i.json({error:'scope must be "cluster" or "project"'},400);let p=ud(l);return i.json({counts:Array.from(p.entries()).map(([g,_])=>({target_id:g,count:_}))})}),t.get("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=Wo(l);return p?i.json({result:p}):i.json({error:"not found"},404)}),t.delete("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(dd(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let n=D.object({name:D.string().min(1).max(100),description:D.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),p=n.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=nd({name:p.data.name,description:p.data.description??null});return i.json({macro_repo:g},201)}catch(g){let _=g instanceof Error?g.message:String(g);return _.includes("UNIQUE constraint")?i.json({error:`a macro repo named "${p.data.name}" already exists`},409):i.json({error:_},400)}});let s=D.object({name:D.string().min(1).max(100).optional(),description:D.string().max(500).nullable().optional()});t.patch("/api/macro-repos/:id",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),g=s.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{let _=sd(l,g.data);return i.json({macro_repo:_})}catch(_){let E=_ instanceof Error?_.message:String(_);return E.includes("not found")?i.json({error:E},404):E.includes("UNIQUE constraint")?i.json({error:"another macro repo already has that name"},409):i.json({error:E},400)}}),t.delete("/api/macro-repos/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(rd(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=D.object({project_id:D.number().int().positive()});t.post("/api/macro-repos/:id/members",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let p=await i.req.json().catch(()=>null),g=r.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);try{return od(l,g.data.project_id),i.json({macro_repo:xt(l)})}catch(_){let E=_ instanceof Error?_.message:String(_);return i.json({error:E},E.includes("not found")?404:400)}}),t.delete("/api/macro-repos/:id/members/:projectId",i=>{let l=Number(i.req.param("id")),p=Number(i.req.param("projectId"));return!Number.isFinite(l)||!Number.isFinite(p)?i.json({error:"invalid id"},400):(id(l,p),i.json({macro_repo:xt(l)}))}),t.get("/api/projects",i=>{let l=f(),p=i.req.query("system")==="1"||i.req.query("system")==="true",g=tr("s",p),_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1909
+ (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest`).get()}var Bk=/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i,Wk=/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;async function Fe(e,t){if((await rt()).tier!=="pro")return e.json({error:"pro_required",message:"This feature requires a Claude Recall Pro license.",upgrade_url:"https://clauderecall.com/pricing",activate_command:"recall activate <license-key>"},402);await t()}var Ir=new Map,$g=0,Pg=0,Ug=null,qk=6e4;function Xk(){$g+=1;let e=Date.now();e-Pg<qk||(Pg=e,console.warn("[auth] /api/terminal/* request rejected without a valid X-Recall-Token. The VS Code / Cursor extension is likely outdated relative to the daemon \u2014 tab names will not flow through, and session titles will fall back to the heuristic first-message snippet. Reinstall: `code --install-extension extensions/vscode/clauderecall-vscode-*.vsix` and restart the extension host. Run `recall doctor` for a full pipeline view."))}function Jk(){Ug=new Date().toISOString()}var Gk=6e4,Yk=5,Xe=[];function Kk(e){let t=Date.now(),n=Xe[Xe.length-1];if(!n||t-n.ts>=Gk)for(Xe.push({ts:t,remainingChunks:e});Xe.length>Yk;)Xe.shift();if(Xe.length<2)return{deltaPerSec:0};let s=Xe[0],r=Xe[Xe.length-1],o=Math.max(1,(r.ts-s.ts)/1e3),a=r.remainingChunks-s.remainingChunks;return{deltaPerSec:Math.max(0,a/o)}}function zk(){let e=_l();return{silentTerminalRejections:$g,lastTerminalSyncAt:Ug,autoExtract:{circuitBroken:e.broken,brokenAt:e.brokenAt,reason:e.reason,consecutiveZeroTokenRuns:e.consecutiveZeroTokenRuns},watcherReindexHotFiles:Ks(10)}}function Mr(e,t){if(t)return"";let n=e;return` AND COALESCE(${n}.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(${n}.auto_title, '') NOT LIKE '[skill]%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are summarizing a Claude Code session%' AND COALESCE(${n}.auto_title, '') NOT LIKE 'You are extracting a structured Output Index%' AND COALESCE(${n}.title_quality, '') != 'programmatic'`}function Vk(e){let t=new kk;if(t.use("*",$k({maxSize:1*1024*1024})),t.use("*",async(i,l)=>{let m=i.req.raw.headers.get("host")??"";if(!Bk.test(m))return i.text("Forbidden: invalid Host header",403);let g=i.req.raw.headers.get("origin");if(g&&!Wk.test(g))return i.text("Forbidden: cross-origin request rejected",403);await l()}),e){let i=Buffer.from(e,"utf8");t.use("/api/*",async(l,m)=>{if(l.req.method==="GET"&&l.req.path==="/api/health")return m();let g=l.req.raw.headers.get("x-recall-token")??"";!g&&l.req.method==="GET"&&(g=new URL(l.req.url).searchParams.get("token")??"");let _=!1;if(g.length===e.length)try{_=Lk(Buffer.from(g,"utf8"),i)}catch{_=!1}return _?m():(l.req.path.startsWith("/api/terminal/")&&Xk(),l.json({error:"unauthorized"},401))})}t.use("*",async(i,l)=>{await l(),i.res.headers.set("Content-Security-Policy","default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://clauderecall.com; font-src 'self' data:; connect-src 'self' data: https://clauderecall.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"),i.res.headers.set("X-Content-Type-Options","nosniff"),i.res.headers.set("X-Frame-Options","DENY"),i.res.headers.set("Referrer-Policy","no-referrer"),i.res.headers.set("Cross-Origin-Resource-Policy","same-origin")}),t.get("/api/health",i=>i.json({status:"ok",version:jg,uptimeSeconds:Math.round(process.uptime()),pipeline:zk()})),t.get("/api/stats",i=>i.json(Dg())),t.get("/api/stats/session/:id",i=>{let l=pg(i.req.param("id"));return l?i.json(l):i.json({error:"session not found"},404)}),t.get("/api/stats/project/:name",i=>{let l=mg(i.req.param("name"));return l?i.json(l):i.json({error:"project not found"},404)}),t.get("/api/stats/overview",i=>{let l=i.req.query("range"),m=l==="7d"?"7d":l==="30d"?"30d":"all";return i.json(gg(m))}),t.post("/api/stats/backfill",async i=>{let l=await i.req.json().catch(()=>({})),m=l.limit?Math.max(1,Math.min(1e5,Number(l.limit))):5e3;if(m>5e3){let _=lg({limit:m});return i.json({mode:"background",started:_,alreadyRunning:!_&&ug(),limit:m,lastRun:ca()})}let g=cg({limit:m});return i.json({mode:"sync",started:!1,alreadyRunning:!1,limit:m,result:g,lastRun:ca()})}),t.get("/api/stats/health",i=>i.json(fg())),t.get("/api/stats/health/:projectId",i=>{let l=Number(i.req.param("projectId")),m=da(l);return m?i.json(m):i.json({error:"project not found"},404)}),t.get("/api/config/verification",i=>i.json({enabled:ha()})),t.put("/api/config/verification",async i=>{let l=await i.req.json();return typeof l.enabled=="boolean"&&vg(l.enabled),i.json({enabled:ha()})}),t.get("/api/sessions/:id/verification",i=>{let l=i.req.param("id"),m=xg(l);return i.json(m)}),t.get("/api/sessions/:id/share-stats",i=>{let l=i.req.param("id"),g=h().prepare(`SELECT tool_names, raw_json FROM messages
1910
+ WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''`).all(l),_=g.length,E=new Set;for(let T of g){if(!/Read|Write|Edit/.test(T.tool_names))continue;let w=T.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(w)for(let x of w){let A=x.match(/":\s*"([^"]+)"/);A&&E.add(A[1])}}return i.json({filesReferenced:E.size,toolCallCount:_})}),t.get("/api/sessions/:id/commits",async i=>{let l=i.req.param("id"),m=$o(l);if(m.length>0||i.req.query("refresh")!=="1")return i.json({commits:m});let g=await Fo(l);return i.json({commits:$o(l),status:g.status})}),t.get("/api/commits/:sha/session",i=>{let l=i.req.param("sha");return/^[0-9a-fA-F]{4,40}$/.test(l)?i.json({sessions:Ls(l)}):i.json({error:"invalid sha format"},400)}),t.get("/api/license/status",async i=>{let l=await rt();return i.json(l)}),t.post("/api/feedback",async i=>{let l;try{l=await i.req.json()}catch{return i.json({error:"invalid json"},400)}if(!l||typeof l!="object")return i.json({error:"invalid body"},400);let m=process.env.RECALL_FEEDBACK_API??"https://clauderecall.com/api/feedback",g=m==="https://clauderecall.com/api/feedback",_=await rt(),E=sn(),T=g&&_.tier==="pro"&&E?E.license_jwt:null,w=(()=>{try{let N=Dr(xa(Na(import.meta.url)),"..","..","package.json");return JSON.parse(Ra(N,"utf8")).version}catch{return"unknown"}})(),x=l,A={score:x.score,comment:x.comment??null,surface:"web",version:typeof x.version=="string"?x.version:w,os:typeof x.os=="string"?x.os:process.platform,trigger_kind:typeof x.trigger_kind=="string"?x.trigger_kind:"manual",license_jwt:T};try{let N=await fetch(m,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(A)}),C=await N.json().catch(()=>({}));return i.json(C,N.status)}catch(N){let C=N instanceof Error?N.message:"network error";return i.json({error:"upstream_unreachable",detail:C},502)}}),t.get("/api/discover/today",Fe,async i=>{try{return i.json(await hg())}catch(l){return console.error("[discover.today]",l),i.json({rediscovered:null,expensive:null,authored:null,availability:{semantic:!1,cost:!1,git:!1},generatedAt:new Date().toISOString(),error:l.message},500)}}),t.get("/api/macro-repos",i=>i.json({macro_repos:$i(),orphan_projects:Up()})),t.get("/api/bug-synthesis",i=>{let l=i.req.query("scope"),m=i.req.query("target_id"),g=i.req.query("limit"),_=l==="cluster"||l==="project"?l:void 0,E=g?Math.max(1,Number(g)):50,T=Yp({scope:_,target_id:m??void 0,limit:E});return i.json({results:T})}),t.get("/api/bug-synthesis/counts",i=>{let l=i.req.query("scope");if(l!=="cluster"&&l!=="project")return i.json({error:'scope must be "cluster" or "project"'},400);let m=Kp(l);return i.json({counts:Array.from(m.entries()).map(([g,_])=>({target_id:g,count:_}))})}),t.get("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let m=Ui(l);return m?i.json({result:m}):i.json({error:"not found"},404)}),t.delete("/api/bug-synthesis/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(zp(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let n=$.object({name:$.string().min(1).max(100),description:$.string().max(500).nullable().optional()});t.post("/api/macro-repos",async i=>{let l=await i.req.json().catch(()=>null),m=n.safeParse(l);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);try{let g=Hp({name:m.data.name,description:m.data.description??null});return i.json({macro_repo:g},201)}catch(g){let _=g instanceof Error?g.message:String(g);return _.includes("UNIQUE constraint")?i.json({error:`a macro repo named "${m.data.name}" already exists`},409):i.json({error:_},400)}});let s=$.object({name:$.string().min(1).max(100).optional(),description:$.string().max(500).nullable().optional()});t.patch("/api/macro-repos/:id",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let m=await i.req.json().catch(()=>null),g=s.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);try{let _=Bp(l,g.data);return i.json({macro_repo:_})}catch(_){let E=_ instanceof Error?_.message:String(_);return E.includes("not found")?i.json({error:E},404):E.includes("UNIQUE constraint")?i.json({error:"another macro repo already has that name"},409):i.json({error:E},400)}}),t.delete("/api/macro-repos/:id",i=>{let l=Number(i.req.param("id"));return Number.isFinite(l)?(Wp(l),i.json({ok:!0})):i.json({error:"invalid id"},400)});let r=$.object({project_id:$.number().int().positive()});t.post("/api/macro-repos/:id/members",async i=>{let l=Number(i.req.param("id"));if(!Number.isFinite(l))return i.json({error:"invalid id"},400);let m=await i.req.json().catch(()=>null),g=r.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);try{return qp(l,g.data.project_id),i.json({macro_repo:Wt(l)})}catch(_){let E=_ instanceof Error?_.message:String(_);return i.json({error:E},E.includes("not found")?404:400)}}),t.delete("/api/macro-repos/:id/members/:projectId",i=>{let l=Number(i.req.param("id")),m=Number(i.req.param("projectId"));return!Number.isFinite(l)||!Number.isFinite(m)?i.json({error:"invalid id"},400):(Xp(l,m),i.json({macro_repo:Wt(l)}))}),t.get("/api/projects",i=>{let l=h(),m=i.req.query("system")==="1"||i.req.query("system")==="true",g=Mr("s",m),_=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1822
1911
  COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count,
1823
1912
  COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count,
1824
1913
  MAX(COALESCE(s.ended_at, s.started_at)) AS latest
1825
1914
  FROM projects p
1826
1915
  LEFT JOIN sessions s ON s.project_id = p.id
1827
1916
  GROUP BY p.id
1828
- ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all();return i.json(_)}),t.get("/api/graph/:project",i=>{let l=f(),p=i.req.param("project"),g=l.prepare(`SELECT id, name, decoded_path FROM projects
1829
- WHERE name = ? LIMIT 1`).get(p);if(!g)return i.json({error:`project "${p}" not found`},404);let _=i.req.query("system")==="1"||i.req.query("system")==="true",E=l.prepare(`SELECT s.id,
1917
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),E=i.req.query("groupBy"),T=E==="repo"||E==="repo-flat"?E:"folder";if(T==="folder")return i.json(_);let w=T==="repo-flat"?"COALESCE(p.main_repo, p.repo_root, p.decoded_path)":"COALESCE(p.repo_root, p.decoded_path)",x=l.prepare(`SELECT ${w} AS repo_root,
1918
+ COUNT(DISTINCT p.id) AS folder_count,
1919
+ COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count_total,
1920
+ COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count_total,
1921
+ MAX(COALESCE(s.ended_at, s.started_at)) AS latest,
1922
+ MAX(p.is_repo) AS is_repo,
1923
+ ${T==="repo-flat"?"0":"MAX(p.is_worktree)"} AS is_worktree,
1924
+ ${T==="repo-flat"?"NULL":"MAX(p.main_repo)"} AS main_repo
1925
+ FROM projects p
1926
+ LEFT JOIN sessions s ON s.project_id = p.id
1927
+ GROUP BY ${w}
1928
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`).all(),A=l.prepare(`SELECT p.id, p.name, p.decoded_path,
1929
+ COUNT(CASE WHEN s.id IS NOT NULL${g} THEN 1 END) AS session_count,
1930
+ COALESCE(SUM(CASE WHEN s.id IS NOT NULL${g} THEN s.message_count ELSE 0 END), 0) AS message_count,
1931
+ MAX(COALESCE(s.ended_at, s.started_at)) AS latest
1932
+ FROM projects p
1933
+ LEFT JOIN sessions s ON s.project_id = p.id
1934
+ WHERE ${w} = ?
1935
+ GROUP BY p.id
1936
+ ORDER BY MAX(COALESCE(s.ended_at, s.started_at, '')) DESC`),N=x.map(C=>({repo_root:C.repo_root,name:C.repo_root?C.repo_root.split("/").filter(Boolean).pop()??C.repo_root:"(unknown)",is_repo:C.is_repo===1,is_worktree:C.is_worktree===1,main_repo:C.main_repo,session_count_total:C.session_count_total,message_count_total:C.message_count_total,latest:C.latest,folders:A.all(C.repo_root)}));return i.json({groupBy:T,groups:N})}),t.get("/api/graph/:project",i=>{let l=h(),m=i.req.param("project"),g=l.prepare(`SELECT id, name, decoded_path FROM projects
1937
+ WHERE name = ? LIMIT 1`).get(m);if(!g)return i.json({error:`project "${m}" not found`},404);let _=i.req.query("system")==="1"||i.req.query("system")==="true",E=l.prepare(`SELECT s.id,
1830
1938
  s.auto_title,
1831
1939
  s.auto_title_source,
1832
1940
  s.first_user_message,
@@ -1836,12 +1944,12 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1836
1944
  sa.alias AS alias
1837
1945
  FROM sessions s
1838
1946
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1839
- WHERE s.project_id = ?${tr("s",_)}
1840
- ORDER BY s.started_at`).all(g.id),y=E.map(k=>k.id),w=[];if(y.length>0){let k=y.map(()=>"?").join(",");w=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1947
+ WHERE s.project_id = ?${Mr("s",_)}
1948
+ ORDER BY s.started_at`).all(g.id),T=E.map(A=>A.id),w=[];if(T.length>0){let A=T.map(()=>"?").join(",");w=l.prepare(`SELECT thread_id, session_id, parent_session_id, role
1841
1949
  FROM thread_edges
1842
- WHERE session_id IN (${k})
1950
+ WHERE session_id IN (${A})
1843
1951
  AND (parent_session_id IS NULL
1844
- OR parent_session_id IN (${k}))`).all(...y,...y)}let N=E.map(k=>{let C=k.alias??k.auto_title??k.first_user_message??k.id.slice(0,8),I=null,j=null;if(k.auto_title?.startsWith("/")){let v=k.auto_title.split(" \xB7 ");I=v[0],j=v.length>1?v.slice(1).join(" \xB7 "):null}return{id:k.id.slice(0,8),full_id:k.id,title:C,alias:k.alias,auto_title:k.auto_title,auto_title_source:k.auto_title_source,title_quality:k.title_quality,started_at:k.started_at,msgs:k.message_count,skill:I,brand:j}});return i.json({project:g,sessions:N,thread_edges:w})});let o=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),a=new Set(["pending","approved","rejected"]),c=new Set(["L1","L2","L3","L4","user"]);t.get("/api/links",i=>{let l=i.req.query("source_id")??void 0,p=i.req.query("target_id")??void 0,g=i.req.query("type"),_=i.req.query("approved"),E=i.req.query("limit"),y;if(g){if(!o.has(g))return i.json({error:`invalid type: ${g}`},400);y=g}let w=_==="1"||_==="true",N=E?Number(E):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<1))return i.json({error:"invalid limit"},400);try{let k=Mn({sourceSessionId:l,targetSessionId:p,linkType:y,approvedOnly:w,limit:N});return i.json({links:k})}catch(k){return i.json({error:k.message},400)}}),t.get("/api/links/suggestions",i=>{let l=i.req.query("status"),p=i.req.query("source_id")??void 0,g=i.req.query("target_id")??void 0,_=i.req.query("inferred_by"),E=i.req.query("limit"),y;if(l){if(!a.has(l))return i.json({error:`invalid status: ${l}`},400);y=l}let w;if(_){if(!c.has(_))return i.json({error:`invalid inferred_by: ${_}`},400);w=_}let N=E?Number(E):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<1))return i.json({error:"invalid limit"},400);try{let k=lt({status:y,sourceSessionId:p,targetSessionId:g,inferredBy:w,limit:N}),C=new Set;for(let v of k)C.add(v.source_session_id),C.add(v.target_session_id);let I=new Map;if(C.size>0){let v=Array.from(C),P=v.map(()=>"?").join(","),K=f().prepare(`SELECT s.id,
1952
+ OR parent_session_id IN (${A}))`).all(...T,...T)}let x=E.map(A=>{let N=A.alias??A.auto_title??A.first_user_message??A.id.slice(0,8),C=null,I=null;if(A.auto_title?.startsWith("/")){let O=A.auto_title.split(" \xB7 ");C=O[0],I=O.length>1?O.slice(1).join(" \xB7 "):null}return{id:A.id.slice(0,8),full_id:A.id,title:N,alias:A.alias,auto_title:A.auto_title,auto_title_source:A.auto_title_source,title_quality:A.title_quality,started_at:A.started_at,msgs:A.message_count,skill:C,brand:I}});return i.json({project:g,sessions:x,thread_edges:w})});let o=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),a=new Set(["pending","approved","rejected"]),c=new Set(["L1","L2","L3","L4","user"]);t.get("/api/links",i=>{let l=i.req.query("source_id")??void 0,m=i.req.query("target_id")??void 0,g=i.req.query("type"),_=i.req.query("approved"),E=i.req.query("limit"),T;if(g){if(!o.has(g))return i.json({error:`invalid type: ${g}`},400);T=g}let w=_==="1"||_==="true",x=E?Number(E):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<1))return i.json({error:"invalid limit"},400);try{let A=is({sourceSessionId:l,targetSessionId:m,linkType:T,approvedOnly:w,limit:x});return i.json({links:A})}catch(A){return i.json({error:A.message},400)}}),t.get("/api/links/suggestions",i=>{let l=i.req.query("status"),m=i.req.query("source_id")??void 0,g=i.req.query("target_id")??void 0,_=i.req.query("inferred_by"),E=i.req.query("limit"),T;if(l){if(!a.has(l))return i.json({error:`invalid status: ${l}`},400);T=l}let w;if(_){if(!c.has(_))return i.json({error:`invalid inferred_by: ${_}`},400);w=_}let x=E?Number(E):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<1))return i.json({error:"invalid limit"},400);try{let A=St({status:T,sourceSessionId:m,targetSessionId:g,inferredBy:w,limit:x}),N=new Set;for(let O of A)N.add(O.source_session_id),N.add(O.target_session_id);let C=new Map;if(N.size>0){let O=Array.from(N),H=O.map(()=>"?").join(","),Q=h().prepare(`SELECT s.id,
1845
1953
  NULLIF(sa.alias, '') AS alias,
1846
1954
  s.auto_title,
1847
1955
  s.first_user_message,
@@ -1849,7 +1957,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1849
1957
  FROM sessions s
1850
1958
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1851
1959
  LEFT JOIN projects p ON p.id = s.project_id
1852
- WHERE s.id IN (${P})`).all(...v);for(let G of K){let U=G.first_user_message?G.first_user_message.slice(0,80):null,X=G.alias??G.auto_title??U??G.id.slice(0,8);I.set(G.id,{title:X,project:G.project})}}let j=k.map(v=>{let P=I.get(v.source_session_id),q=I.get(v.target_session_id);return{...v,source_title:P?.title??v.source_session_id.slice(0,8),source_project:P?.project??null,target_title:q?.title??v.target_session_id.slice(0,8),target_project:q?.project??null}});return i.json({suggestions:j})}catch(k){return i.json({error:k.message},400)}}),t.get("/api/output-index/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=Ve(l);return p?i.json(p):i.json({error:`no output index for session ${l}`},404)});let u=new Set(["pagerank","embedding-rerank","hybrid"]);t.get("/api/neighborhood/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let p=i.req.query("budget"),g=p!==void 0?Number(p):4e3;if(!Number.isFinite(g)||g<100)return i.json({error:"budget must be a number \u2265 100"},400);let _=i.req.query("scoring")??"hybrid";if(!u.has(_))return i.json({error:`invalid scoring: ${_}; valid: pagerank, embedding-rerank, hybrid`},400);let E=_,y=i.req.query("max_depth"),w=y!==void 0?Number(y):2;if(!Number.isFinite(w)||w<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let N,k=i.req.query("edge_types");if(k){let P=k.split(",").map(q=>q.trim()).filter(Boolean);for(let q of P)if(!o.has(q))return i.json({error:`invalid edge_type: ${q}`},400);N=P}let C=i.req.query("include_wiki_links"),I=C===void 0?!0:!(C==="0"||C==="false"),j=i.req.query("include_suggestions"),v=j==="1"||j==="true";try{let P=js(l,{budget:g,scoring:E,maxDepth:w,edgeTypes:N,includeWikiLinks:I,includeSuggestions:v});return i.json(P)}catch(P){let q=P instanceof Error?P.message:"unknown error",K=/not found/.test(q)?404:500;return i.json({error:q},K)}}),t.get("/api/bug-patterns",i=>{let l=i.req.query("min_count"),p=i.req.query("status"),g=i.req.query("project")??void 0,_=i.req.query("limit"),E=i.req.query("offset"),y=l?Number(l):void 0;if(y!==void 0&&(!Number.isFinite(y)||y<1))return i.json({error:"min_count must be a positive integer"},400);let w;if(p==="open")w=!1;else if(p==="resolved")w=!0;else if(p&&p!=="all")return i.json({error:`invalid status: ${p}; valid: open, resolved, all`},400);let N=_?Number(_):void 0;if(N!==void 0&&(!Number.isFinite(N)||N<1))return i.json({error:"invalid limit"},400);let k=E?Number(E):void 0;if(k!==void 0&&(!Number.isFinite(k)||k<0))return i.json({error:"invalid offset"},400);try{let C=ca({minOccurrenceCount:y,hasResolved:w,project:g,limit:N,offset:k});return i.json(C)}catch(C){return i.json({error:C.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=f(),g=l.prepare(`SELECT p.name AS project,
1960
+ WHERE s.id IN (${H})`).all(...O);for(let Y of Q){let B=Y.first_user_message?Y.first_user_message.slice(0,80):null,X=Y.alias??Y.auto_title??B??Y.id.slice(0,8);C.set(Y.id,{title:X,project:Y.project})}}let I=A.map(O=>{let H=C.get(O.source_session_id),J=C.get(O.target_session_id);return{...O,source_title:H?.title??O.source_session_id.slice(0,8),source_project:H?.project??null,target_title:J?.title??O.target_session_id.slice(0,8),target_project:J?.project??null}});return i.json({suggestions:I})}catch(A){return i.json({error:A.message},400)}}),t.get("/api/output-index/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let m=ot(l);return m?i.json(m):i.json({error:`no output index for session ${l}`},404)});let u=new Set(["pagerank","embedding-rerank","hybrid"]);t.get("/api/neighborhood/:sessionId",i=>{let l=i.req.param("sessionId");if(!l)return i.json({error:"sessionId required"},400);let m=i.req.query("budget"),g=m!==void 0?Number(m):4e3;if(!Number.isFinite(g)||g<100)return i.json({error:"budget must be a number \u2265 100"},400);let _=i.req.query("scoring")??"hybrid";if(!u.has(_))return i.json({error:`invalid scoring: ${_}; valid: pagerank, embedding-rerank, hybrid`},400);let E=_,T=i.req.query("max_depth"),w=T!==void 0?Number(T):2;if(!Number.isFinite(w)||w<1)return i.json({error:"max_depth must be a number \u2265 1"},400);let x,A=i.req.query("edge_types");if(A){let H=A.split(",").map(J=>J.trim()).filter(Boolean);for(let J of H)if(!o.has(J))return i.json({error:`invalid edge_type: ${J}`},400);x=H}let N=i.req.query("include_wiki_links"),C=N===void 0?!0:!(N==="0"||N==="false"),I=i.req.query("include_suggestions"),O=I==="1"||I==="true";try{let H=mr(l,{budget:g,scoring:E,maxDepth:w,edgeTypes:x,includeWikiLinks:C,includeSuggestions:O});return i.json(H)}catch(H){let J=H instanceof Error?H.message:"unknown error",Q=/not found/.test(J)?404:500;return i.json({error:J},Q)}}),t.get("/api/bug-patterns",i=>{let l=i.req.query("min_count"),m=i.req.query("status"),g=i.req.query("project")??void 0,_=i.req.query("limit"),E=i.req.query("offset"),T=l?Number(l):void 0;if(T!==void 0&&(!Number.isFinite(T)||T<1))return i.json({error:"min_count must be a positive integer"},400);let w;if(m==="open")w=!1;else if(m==="resolved")w=!0;else if(m&&m!=="all")return i.json({error:`invalid status: ${m}; valid: open, resolved, all`},400);let x=_?Number(_):void 0;if(x!==void 0&&(!Number.isFinite(x)||x<1))return i.json({error:"invalid limit"},400);let A=E?Number(E):void 0;if(A!==void 0&&(!Number.isFinite(A)||A<0))return i.json({error:"invalid offset"},400);try{let N=Za({minOccurrenceCount:T,hasResolved:w,project:g,limit:x,offset:A});return i.json(N)}catch(N){return i.json({error:N.message},400)}}),t.get("/api/bug-patterns/setup-status",i=>{let l=h(),g=l.prepare(`SELECT p.name AS project,
1853
1961
  COUNT(s.id) AS total_sessions,
1854
1962
  SUM(CASE WHEN oi.session_id IS NOT NULL THEN 1 ELSE 0 END) AS extracted_sessions,
1855
1963
  MAX(oi.extracted_at) AS last_extracted_at
@@ -1857,20 +1965,20 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1857
1965
  LEFT JOIN sessions s ON s.project_id = p.id
1858
1966
  LEFT JOIN session_output_index oi ON oi.session_id = s.id
1859
1967
  GROUP BY p.id
1860
- ORDER BY total_sessions DESC`).all().map(y=>({project:y.project,total_sessions:y.total_sessions??0,extracted_sessions:y.extracted_sessions??0,remaining_sessions:(y.total_sessions??0)-(y.extracted_sessions??0),last_extracted_at:y.last_extracted_at})),_=g.reduce((y,w)=>(y.total_sessions+=w.total_sessions,y.extracted_sessions+=w.extracted_sessions,y.remaining_sessions+=w.remaining_sessions,y),{total_sessions:0,extracted_sessions:0,remaining_sessions:0}),E=l.prepare("SELECT COUNT(*) AS n FROM bug_pattern_clusters").get();return i.json({projects:g,totals:{..._,cluster_count:E.n}})});let d=D.object({project:D.string().min(1),model:D.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:D.number().int().positive().optional(),force:D.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=Ce(i);if(l)return l;let p=xo();if(p>0&&p<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(p/1024**3).toFixed(2)} GB free \u2014 extract-outputs needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:p},507);let g=await i.req.json().catch(()=>null),_=d.safeParse(g);if(!_.success)return i.json({error:"invalid request body",details:_.error.format()},400);let E={project:_.data.project,model:_.data.model??tt,limit:_.data.limit??200,force:_.data.force??!1},y=zo();if(E.limit>y.sessionCeiling)return re({kind:"run-rejected",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,reason:`limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}`}),i.json({error:`requested limit ${E.limit} exceeds session ceiling ${y.sessionCeiling}. Lower the limit or edit launcher.sessionCeiling in ~/.recall/config.json.`},400);let w=dn(E.project);if(!w)return i.json({error:`project "${E.project}" not found`},404);let k=ft({projectId:w.id,limit:E.limit,force:E.force}).eligible.length,C=Ko(k),I=We(k,E.model),j=Nt(),v=C.estimated_input_tokens_max+C.estimated_output_tokens_max>j.remaining_tokens_24h;if(re({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:k}),k===0)return i.json({eligible_session_count:0,...C,plan_window_estimate:I,budget:j,would_exceed_budget:!1,preflight_token:null,expires_at:null,message:"No eligible sessions to extract. Pass force=true to re-extract sessions already at the current extractor version."});let{token:P,expiresAt:q}=Xo(E);return i.json({preflight_token:P,expires_at:new Date(q).toISOString(),eligible_session_count:k,...C,plan_window_estimate:I,budget:j,would_exceed_budget:v})});let m=D.object({preflight_token:D.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=Ce(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=m.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);let _=Jo(g.data.preflight_token);if(!_)return re({kind:"run-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let E=Nt(),w=(()=>{let I=dn(_.project);return I?ft({projectId:I.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,N=Ko(w),k=N.estimated_input_tokens_max+N.estimated_output_tokens_max;if(k>E.remaining_tokens_24h)return re({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${k} exceeds remaining 24h budget ${E.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:E,projected_spend:k},429);let C=Td({project:_.project,model:_.model,limit:_.limit,force:_.force,origin:i.req.header("origin")??null});return"error"in C?i.json({error:C.error},400):i.json({jobId:C.jobId,reused:C.reused},C.reused?409:200)}),t.get("/api/extract-outputs/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Qo(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ye(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of yd(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=Qo(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/extract-outputs/jobs/:jobId",i=>{let l=Ce(i);return l||(wd(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/extract-outputs/budget",i=>i.json(Nt()));let h=D.object({scope:D.enum(["cluster","project"]),target_id:D.string().min(1),mode:D.enum(["synopsis","priorities","root_cause"]),model:D.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let q=xo();if(q>0&&q<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(q/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:q},507)}let l=Ce(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=h.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _={scope:g.data.scope,target_id:g.data.target_id,mode:g.data.mode,model:g.data.model??Rd},E=$s(_);if(!E)return re({kind:"synth-rejected",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:"target not found"}),i.json({error:_.scope==="cluster"?`cluster "${_.target_id}" not found in any extracted findings`:`project "${_.target_id}" has no extracted findings to synthesize`},404);let y=Vo({scope:_.scope,mode:_.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),w=y.estimated_input_tokens_max+y.estimated_output_tokens_max,N=Sd(w),k=We(N,_.model),C=Nt(),j=y.estimated_input_tokens_max+y.estimated_output_tokens_max>C.remaining_tokens_24h;re({kind:"synth-preflight",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:`${_.scope}/${_.mode}/${_.target_id}`});let{token:v,expiresAt:P}=Xo({project:_.target_id,model:_.model,limit:1,force:!1});return er.set(v,_),setTimeout(()=>er.delete(v),9e4).unref?.(),i.json({preflight_token:v,expires_at:new Date(P).toISOString(),estimated_input_tokens_max:y.estimated_input_tokens_max,estimated_output_tokens_max:y.estimated_output_tokens_max,plan_window_estimate:k,budget:C,would_exceed_budget:j,context_summary:E.context_summary})});let b=D.object({preflight_token:D.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=Ce(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=b.safeParse(p);if(!g.success)return i.json({error:"invalid request body"},400);let _=Jo(g.data.preflight_token),E=er.get(g.data.preflight_token)??null;if(er.delete(g.data.preflight_token),!E||!_)return re({kind:"synth-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let y=Nt(),w=$s(E);if(!w)return i.json({error:E.scope==="cluster"?`cluster "${E.target_id}" no longer exists`:`project "${E.target_id}" has no findings`},404);let N=Vo({scope:E.scope,mode:E.mode,member_session_count:w.context_summary.session_count,cluster_count:w.context_summary.cluster_count}),k=N.estimated_input_tokens_max+N.estimated_output_tokens_max;if(k>y.remaining_tokens_24h)return re({kind:"synth-rejected",job_id:null,project:E.scope==="project"?E.target_id:null,model:E.model,limit:null,origin:i.req.header("origin")??null,reason:`projected spend ${k} exceeds remaining 24h budget ${y.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:y,projected_spend:k},429);let C=Nd({intent:E,origin:i.req.header("origin")??null});return"error"in C?i.json({error:C.error},400):i.json({jobId:C.jobId,reused:C.reused},C.reused?409:200)}),t.get("/api/bug-patterns/synthesize/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!ei(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ye(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of Od(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=ei(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=Ce(i);return l||(Ld(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/bug-signatures",i=>{let l=i.req.query("project")??null,p=Math.min(Math.max(1,Number(i.req.query("limit")??100)),500),g=f(),_=["oi.bug_signatures IS NOT NULL"],E=[];l&&(_.push("p.name = ?"),E.push(l));let w=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1968
+ ORDER BY total_sessions DESC`).all().map(T=>({project:T.project,total_sessions:T.total_sessions??0,extracted_sessions:T.extracted_sessions??0,remaining_sessions:(T.total_sessions??0)-(T.extracted_sessions??0),last_extracted_at:T.last_extracted_at})),_=g.reduce((T,w)=>(T.total_sessions+=w.total_sessions,T.extracted_sessions+=w.extracted_sessions,T.remaining_sessions+=w.remaining_sessions,T),{total_sessions:0,extracted_sessions:0,remaining_sessions:0}),E=l.prepare("SELECT COUNT(*) AS n FROM bug_pattern_clusters").get();return i.json({projects:g,totals:{..._,cluster_count:E.n}})});let d=$.object({project:$.string().min(1),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),limit:$.number().int().positive().optional(),force:$.boolean().optional()});t.post("/api/extract-outputs/preflight",async i=>{let l=De(i);if(l)return l;let m=Ri();if(m>0&&m<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(m/1024**3).toFixed(2)} GB free \u2014 extract-outputs needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:m},507);let g=await i.req.json().catch(()=>null),_=d.safeParse(g);if(!_.success)return i.json({error:"invalid request body",details:_.error.format()},400);let E={project:_.data.project,model:_.data.model??lt,limit:_.data.limit??200,force:_.data.force??!1},T=Ji();if(E.limit>T.sessionCeiling)return oe({kind:"run-rejected",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,reason:`limit ${E.limit} exceeds session ceiling ${T.sessionCeiling}`}),i.json({error:`requested limit ${E.limit} exceeds session ceiling ${T.sessionCeiling}. Lower the limit or edit launcher.sessionCeiling in ~/.recall/config.json.`},400);let w=jn(E.project);if(!w)return i.json({error:`project "${E.project}" not found`},404);let A=xt({projectId:w.id,limit:E.limit,force:E.force}).eligible.length,N=Gi(A),C=Ve(A,E.model),I=qt(),O=N.estimated_input_tokens_max+N.estimated_output_tokens_max>I.remaining_tokens_24h;if(oe({kind:"preflight",job_id:null,project:E.project,model:E.model,limit:E.limit,origin:i.req.header("origin")??null,sessions_eligible:A}),A===0)return i.json({eligible_session_count:0,...N,plan_window_estimate:C,budget:I,would_exceed_budget:!1,preflight_token:null,expires_at:null,message:"No eligible sessions to extract. Pass force=true to re-extract sessions already at the current extractor version."});let{token:H,expiresAt:J}=Bi(E);return i.json({preflight_token:H,expires_at:new Date(J).toISOString(),eligible_session_count:A,...N,plan_window_estimate:C,budget:I,would_exceed_budget:O})});let p=$.object({preflight_token:$.string().length(64)});t.post("/api/extract-outputs/run",async i=>{let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=p.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);let _=Wi(g.data.preflight_token);if(!_)return oe({kind:"run-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let E=qt(),w=(()=>{let C=jn(_.project);return C?xt({projectId:C.id,limit:_.limit,force:_.force}):null})()?.eligible.length??0,x=Gi(w),A=x.estimated_input_tokens_max+x.estimated_output_tokens_max;if(A>E.remaining_tokens_24h)return oe({kind:"run-rejected",job_id:null,project:_.project,model:_.model,limit:_.limit,origin:i.req.header("origin")??null,reason:`projected spend ${A} exceeds remaining 24h budget ${E.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:E,projected_spend:A},429);let N=im({project:_.project,model:_.model,limit:_.limit,force:_.force,origin:i.req.header("origin")??null});return"error"in N?i.json({error:N.error},400):i.json({jobId:N.jobId,reused:N.reused},N.reused?409:200)}),t.get("/api/extract-outputs/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!zi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return tt(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of am(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/extract-outputs/jobs/:jobId",i=>{let l=zi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/extract-outputs/jobs/:jobId",i=>{let l=De(i);return l||(cm(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/extract-outputs/budget",i=>i.json(qt()));let f=$.object({scope:$.enum(["cluster","project"]),target_id:$.string().min(1),mode:$.enum(["synopsis","priorities","root_cause"]),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()});t.post("/api/bug-patterns/synthesize/preflight",async i=>{{let J=Ri();if(J>0&&J<1*1024**3)return i.json({error:"insufficient-disk-space",message:`${(J/1024**3).toFixed(2)} GB free \u2014 bug synthesis needs at least 1 GB headroom. Free up disk and retry.`,freeBytes:J},507)}let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=f.safeParse(m);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _={scope:g.data.scope,target_id:g.data.target_id,mode:g.data.mode,model:g.data.model??lm},E=Er(_);if(!E)return oe({kind:"synth-rejected",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:"target not found"}),i.json({error:_.scope==="cluster"?`cluster "${_.target_id}" not found in any extracted findings`:`project "${_.target_id}" has no extracted findings to synthesize`},404);let T=Yi({scope:_.scope,mode:_.mode,member_session_count:E.context_summary.session_count,cluster_count:E.context_summary.cluster_count}),w=T.estimated_input_tokens_max+T.estimated_output_tokens_max,x=om(w),A=Ve(x,_.model),N=qt(),I=T.estimated_input_tokens_max+T.estimated_output_tokens_max>N.remaining_tokens_24h;oe({kind:"synth-preflight",job_id:null,project:_.scope==="project"?_.target_id:null,model:_.model,limit:null,origin:i.req.header("origin")??null,reason:`${_.scope}/${_.mode}/${_.target_id}`});let{token:O,expiresAt:H}=Bi({project:_.target_id,model:_.model,limit:1,force:!1});return Ir.set(O,_),setTimeout(()=>Ir.delete(O),9e4).unref?.(),i.json({preflight_token:O,expires_at:new Date(H).toISOString(),estimated_input_tokens_max:T.estimated_input_tokens_max,estimated_output_tokens_max:T.estimated_output_tokens_max,plan_window_estimate:A,budget:N,would_exceed_budget:I,context_summary:E.context_summary})});let S=$.object({preflight_token:$.string().length(64)});t.post("/api/bug-patterns/synthesize/run",async i=>{let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=S.safeParse(m);if(!g.success)return i.json({error:"invalid request body"},400);let _=Wi(g.data.preflight_token),E=Ir.get(g.data.preflight_token)??null;if(Ir.delete(g.data.preflight_token),!E||!_)return oe({kind:"synth-rejected",job_id:null,project:null,model:null,limit:null,origin:i.req.header("origin")??null,reason:"preflight token invalid, expired, or already used"}),i.json({error:"preflight token invalid, expired, or already used"},400);let T=qt(),w=Er(E);if(!w)return i.json({error:E.scope==="cluster"?`cluster "${E.target_id}" no longer exists`:`project "${E.target_id}" has no findings`},404);let x=Yi({scope:E.scope,mode:E.mode,member_session_count:w.context_summary.session_count,cluster_count:w.context_summary.cluster_count}),A=x.estimated_input_tokens_max+x.estimated_output_tokens_max;if(A>T.remaining_tokens_24h)return oe({kind:"synth-rejected",job_id:null,project:E.scope==="project"?E.target_id:null,model:E.model,limit:null,origin:i.req.header("origin")??null,reason:`projected spend ${A} exceeds remaining 24h budget ${T.remaining_tokens_24h}`}),i.json({error:"daily token budget would be exceeded. Wait for the rolling 24h window to clear, or raise launcher.dailyTokenBudget in ~/.recall/config.json.",budget:T,projected_spend:A},429);let N=mm({intent:E,origin:i.req.header("origin")??null});return"error"in N?i.json({error:N.error},400):i.json({jobId:N.jobId,reused:N.reused},N.reused?409:200)}),t.get("/api/bug-patterns/synthesize/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Vi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return tt(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of gm(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=Vi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/bug-patterns/synthesize/jobs/:jobId",i=>{let l=De(i);return l||(fm(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))}),t.get("/api/bug-signatures",i=>{let l=i.req.query("project")??null,m=Math.min(Math.max(1,Number(i.req.query("limit")??100)),500),g=h(),_=["oi.bug_signatures IS NOT NULL"],E=[];l&&(_.push("p.name = ?"),E.push(l));let w=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1861
1969
  s.started_at, oi.extracted_at, oi.bug_signatures
1862
1970
  FROM session_output_index oi
1863
1971
  JOIN sessions s ON s.id = oi.session_id
1864
1972
  JOIN projects p ON p.id = s.project_id
1865
1973
  WHERE ${_.join(" AND ")}
1866
1974
  ORDER BY oi.extracted_at DESC
1867
- LIMIT ?`).all(...E,p).map(j=>{let v=[];try{let P=JSON.parse(j.bug_signatures);Array.isArray(P)&&(v=P)}catch{v=[]}return{session_id:j.session_id,project:j.project,auto_title:j.auto_title,started_at:j.started_at,extracted_at:j.extracted_at,rawSignatures:v}}),N=w.flatMap(j=>j.rawSignatures.map(v=>v.message_hash).filter(v=>typeof v=="string")),k=Uo(N),C=w.map(j=>({session_id:j.session_id,project:j.project,auto_title:j.auto_title,started_at:j.started_at,extracted_at:j.extracted_at,signatures:j.rawSignatures.map(v=>{let P=v.message_hash?k.get(v.message_hash)??null:null;return{...v,resolved:Bo(P),resolution:P}}),signature_count:j.rawSignatures.length})),I=C.reduce((j,v)=>(j.sessions_total+=1,v.signature_count>0?(j.sessions_with_findings+=1,j.total_findings+=v.signature_count):j.sessions_empty+=1,j),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:C,totals:I})}),t.post("/api/bug-signatures/:hash/resolve",async i=>{let l=i.req.param("hash");if(!l||l.length<4)return i.json({error:"invalid message hash"},400);let p=await i.req.json().catch(()=>({})),g=Qu({messageHash:l,resolvedInSessionId:p.resolved_in_session_id??null,fixSummary:p.fix_summary??null});return i.json({resolution:g})}),t.post("/api/bug-signatures/:hash/unresolve",i=>{let l=i.req.param("hash");return!l||l.length<4?i.json({error:"invalid message hash"},400):(ed(l),i.json({ok:!0}))}),t.get("/api/bug-patterns/graph",i=>{let l=i.req.query("project")??null,p=i.req.query("include_resolved")!=="0",g=f(),_=["oi.bug_signatures IS NOT NULL","oi.bug_signatures != '[]'"],E=[];l&&(_.push("p.name = ?"),E.push(l));let y=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1975
+ LIMIT ?`).all(...E,m).map(I=>{let O=[];try{let H=JSON.parse(I.bug_signatures);Array.isArray(H)&&(O=H)}catch{O=[]}return{session_id:I.session_id,project:I.project,auto_title:I.auto_title,started_at:I.started_at,extracted_at:I.extracted_at,rawSignatures:O}}),x=w.flatMap(I=>I.rawSignatures.map(O=>O.message_hash).filter(O=>typeof O=="string")),A=Pi(x),N=w.map(I=>({session_id:I.session_id,project:I.project,auto_title:I.auto_title,started_at:I.started_at,extracted_at:I.extracted_at,signatures:I.rawSignatures.map(O=>{let H=O.message_hash?A.get(O.message_hash)??null:null;return{...O,resolved:Fi(H),resolution:H}}),signature_count:I.rawSignatures.length})),C=N.reduce((I,O)=>(I.sessions_total+=1,O.signature_count>0?(I.sessions_with_findings+=1,I.total_findings+=O.signature_count):I.sessions_empty+=1,I),{sessions_total:0,sessions_with_findings:0,sessions_empty:0,total_findings:0});return i.json({sessions:N,totals:C})}),t.post("/api/bug-signatures/:hash/resolve",async i=>{let l=i.req.param("hash");if(!l||l.length<4)return i.json({error:"invalid message hash"},400);let m=await i.req.json().catch(()=>({})),g=Fp({messageHash:l,resolvedInSessionId:m.resolved_in_session_id??null,fixSummary:m.fix_summary??null});return i.json({resolution:g})}),t.post("/api/bug-signatures/:hash/unresolve",i=>{let l=i.req.param("hash");return!l||l.length<4?i.json({error:"invalid message hash"},400):($p(l),i.json({ok:!0}))}),t.get("/api/bug-patterns/graph",i=>{let l=i.req.query("project")??null,m=i.req.query("include_resolved")!=="0",g=h(),_=["oi.bug_signatures IS NOT NULL","oi.bug_signatures != '[]'"],E=[];l&&(_.push("p.name = ?"),E.push(l));let T=g.prepare(`SELECT s.id AS session_id, p.name AS project, s.auto_title,
1868
1976
  s.started_at, oi.extracted_at, oi.bug_signatures
1869
1977
  FROM session_output_index oi
1870
1978
  JOIN sessions s ON s.id = oi.session_id
1871
1979
  JOIN projects p ON p.id = s.project_id
1872
1980
  WHERE ${_.join(" AND ")}
1873
- ORDER BY oi.extracted_at DESC`).all(...E),w=[];for(let U of y){let X=[];try{let Y=JSON.parse(U.bug_signatures);Array.isArray(Y)&&(X=Y)}catch{continue}for(let Y of X)w.push({sig:Y,session_id:U.session_id,project:U.project,auto_title:U.auto_title})}let N=new Map;for(let U of w){let X=U.sig.message_hash??`nohash:${(U.sig.snippet??"").slice(0,64)}`,Y=N.get(X);Y?Y.push(U):N.set(X,[U])}let k=Array.from(N.keys()).filter(U=>!U.startsWith("nohash:")),C=Uo(k),I=[],j=new Map,v=[];for(let[U,X]of N){let Y=X[0],Q=Y.sig.message_hash??null,V=Q?C.get(Q)??null:null,J=Bo(V);if(!p&&J)continue;let oe=Array.from(new Set(X.map(me=>me.project))).sort(),ze=Array.from(new Set(X.map(me=>me.session_id))),Me={id:Q??U,message_hash:Q,error_type:Y.sig.error_type??null,snippet:(Y.sig.snippet??"").slice(0,200),file:Y.sig.file??null,occurrence_count:X.length,projects:oe,resolved:J,fix_summary:V?.fix_summary??null,member_session_ids:ze};I.push(Me);for(let me of X)j.has(me.session_id)||j.set(me.session_id,{session_id:me.session_id,project:me.project,auto_title:me.auto_title}),v.push({cluster_id:Me.id,session_id:me.session_id})}let P=[],q=4,K=new Map;function G(U){let X=K.get(U)??0;return X>=q?!1:(K.set(U,X+1),!0)}for(let U=0;U<I.length;U+=1)for(let X=U+1;X<I.length;X+=1){let Y=I[U],Q=I[X],V=null;Y.error_type&&Y.error_type!=="unknown"&&Y.error_type===Q.error_type?V="same_error_type":Y.file&&Q.file&&Y.file===Q.file&&(V="same_file"),V&&(!G(Y.id)||!G(Q.id)||P.push({a:Y.id,b:Q.id,reason:V}))}return i.json({clusters:I,sessions:Array.from(j.values()),member_edges:v,related_edges:P,totals:{cluster_count:I.length,singleton_count:I.filter(U=>U.occurrence_count===1).length,recurring_count:I.filter(U=>U.occurrence_count>1).length,session_count:j.size,resolved_count:I.filter(U=>U.resolved).length}})}),t.get("/api/bug-patterns/:clusterId",i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=la(l);return p?i.json(p):i.json({error:`cluster ${l} not found`},404)}),t.post("/api/bug-patterns/:clusterId/resolve",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let g=p.resolved_in_session_id,_=p.fix_summary;if(typeof g!="string"||g.length===0)return i.json({error:"resolved_in_session_id required"},400);if(typeof _!="string"||_.trim().length===0)return i.json({error:"fix_summary required"},400);try{let E=ua(l,g,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",w=/not found/.test(y)?404:(/not a member/.test(y),400);return i.json({error:y},w)}}),t.post("/api/bug-patterns/:clusterId/split",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);let g=p.member_session_ids;if(!Array.isArray(g)||g.length===0)return i.json({error:"member_session_ids must be a non-empty array of strings"},400);let _=[];for(let E of g){if(typeof E!="string"||E.length===0)return i.json({error:"member_session_ids must contain only non-empty strings"},400);_.push(E)}try{let E=da(l,_);return i.json(E)}catch(E){let y=E instanceof Error?E.message:"unknown error",w=/not found/.test(y)?404:(/cannot split|none of the supplied/.test(y),400);return i.json({error:y},w)}}),t.post("/api/links",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.source_session_id,g=l.target_session_id,_=l.link_type;if(typeof p!="string"||p.length===0)return i.json({error:"source_session_id required"},400);if(typeof g!="string"||g.length===0)return i.json({error:"target_session_id required"},400);if(typeof _!="string")return i.json({error:"link_type required"},400);if(!o.has(_))return i.json({error:`invalid link_type: ${_}`},400);if(_!=="wiki_link")return i.json({error:`link_type '${_}' is not user-writable; only wiki_link is exposed via this endpoint. Other types must go through the suggestions-queue review flow.`},403);if(p===g)return i.json({error:"a session cannot link to itself"},400);let E=f();if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(p))return i.json({error:`source session not found: ${p}`},404);if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(g))return i.json({error:`target session not found: ${g}`},404);let N=Mn({sourceSessionId:g,targetSessionId:p,linkType:"wiki_link"});if(N.length>0)return i.json({link:N[0]});try{let k=Oa({source_session_id:p,target_session_id:g,link_type:"wiki_link",confidence:1,source:"manual",evidence:l.evidence??{created_via:"context_menu"},approved:!0});return i.json({link:k})}catch(k){return i.json({error:k.message},400)}}),t.delete("/api/links/:id",i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let g=La(p);return g.removed===0?i.json({error:`link ${p} not found`},404):i.json(g)}),t.get("/api/sessions/:id/links",i=>{let l=i.req.param("id");if(!l)return i.json({error:"sessionId required"},400);let p=f();if(!p.prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:`session not found: ${l}`},404);let _=i.req.query("type")??"wiki_link";if(!o.has(_))return i.json({error:`invalid type: ${_}`},400);let E=_,y=Jt(l).filter(j=>j.link_type===E),w=Ou(l,y);if(w.length===0)return i.json({links:[]});let N=w.map(j=>j.otherSessionId),k=N.map(()=>"?").join(","),C=p.prepare(`SELECT s.id,
1981
+ ORDER BY oi.extracted_at DESC`).all(...E),w=[];for(let B of T){let X=[];try{let z=JSON.parse(B.bug_signatures);Array.isArray(z)&&(X=z)}catch{continue}for(let z of X)w.push({sig:z,session_id:B.session_id,project:B.project,auto_title:B.auto_title})}let x=new Map;for(let B of w){let X=B.sig.message_hash??`nohash:${(B.sig.snippet??"").slice(0,64)}`,z=x.get(X);z?z.push(B):x.set(X,[B])}let A=Array.from(x.keys()).filter(B=>!B.startsWith("nohash:")),N=Pi(A),C=[],I=new Map,O=[];for(let[B,X]of x){let z=X[0],te=z.sig.message_hash??null,Z=te?N.get(te)??null:null,K=Fi(Z);if(!m&&K)continue;let ie=Array.from(new Set(X.map(ge=>ge.project))).sort(),nt=Array.from(new Set(X.map(ge=>ge.session_id))),$e={id:te??B,message_hash:te,error_type:z.sig.error_type??null,snippet:(z.sig.snippet??"").slice(0,200),file:z.sig.file??null,occurrence_count:X.length,projects:ie,resolved:K,fix_summary:Z?.fix_summary??null,member_session_ids:nt};C.push($e);for(let ge of X)I.has(ge.session_id)||I.set(ge.session_id,{session_id:ge.session_id,project:ge.project,auto_title:ge.auto_title}),O.push({cluster_id:$e.id,session_id:ge.session_id})}let H=[],J=4,Q=new Map;function Y(B){let X=Q.get(B)??0;return X>=J?!1:(Q.set(B,X+1),!0)}for(let B=0;B<C.length;B+=1)for(let X=B+1;X<C.length;X+=1){let z=C[B],te=C[X],Z=null;z.error_type&&z.error_type!=="unknown"&&z.error_type===te.error_type?Z="same_error_type":z.file&&te.file&&z.file===te.file&&(Z="same_file"),Z&&(!Y(z.id)||!Y(te.id)||H.push({a:z.id,b:te.id,reason:Z}))}return i.json({clusters:C,sessions:Array.from(I.values()),member_edges:O,related_edges:H,totals:{cluster_count:C.length,singleton_count:C.filter(B=>B.occurrence_count===1).length,recurring_count:C.filter(B=>B.occurrence_count>1).length,session_count:I.size,resolved_count:C.filter(B=>B.resolved).length}})}),t.get("/api/bug-patterns/:clusterId",i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let m=ec(l);return m?i.json(m):i.json({error:`cluster ${l} not found`},404)}),t.post("/api/bug-patterns/:clusterId/resolve",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);let g=m.resolved_in_session_id,_=m.fix_summary;if(typeof g!="string"||g.length===0)return i.json({error:"resolved_in_session_id required"},400);if(typeof _!="string"||_.trim().length===0)return i.json({error:"fix_summary required"},400);try{let E=tc(l,g,_);return i.json(E)}catch(E){let T=E instanceof Error?E.message:"unknown error",w=/not found/.test(T)?404:(/not a member/.test(T),400);return i.json({error:T},w)}}),t.post("/api/bug-patterns/:clusterId/split",async i=>{let l=i.req.param("clusterId");if(!l)return i.json({error:"clusterId required"},400);let m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);let g=m.member_session_ids;if(!Array.isArray(g)||g.length===0)return i.json({error:"member_session_ids must be a non-empty array of strings"},400);let _=[];for(let E of g){if(typeof E!="string"||E.length===0)return i.json({error:"member_session_ids must contain only non-empty strings"},400);_.push(E)}try{let E=nc(l,_);return i.json(E)}catch(E){let T=E instanceof Error?E.message:"unknown error",w=/not found/.test(T)?404:(/cannot split|none of the supplied/.test(T),400);return i.json({error:T},w)}}),t.post("/api/links",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let m=l.source_session_id,g=l.target_session_id,_=l.link_type;if(typeof m!="string"||m.length===0)return i.json({error:"source_session_id required"},400);if(typeof g!="string"||g.length===0)return i.json({error:"target_session_id required"},400);if(typeof _!="string")return i.json({error:"link_type required"},400);if(!o.has(_))return i.json({error:`invalid link_type: ${_}`},400);if(_!=="wiki_link")return i.json({error:`link_type '${_}' is not user-writable; only wiki_link is exposed via this endpoint. Other types must go through the suggestions-queue review flow.`},403);if(m===g)return i.json({error:"a session cannot link to itself"},400);let E=h();if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(m))return i.json({error:`source session not found: ${m}`},404);if(!E.prepare("SELECT 1 FROM sessions WHERE id = ?").get(g))return i.json({error:`target session not found: ${g}`},404);let x=is({sourceSessionId:g,targetSessionId:m,linkType:"wiki_link"});if(x.length>0)return i.json({link:x[0]});try{let A=Tc({source_session_id:m,target_session_id:g,link_type:"wiki_link",confidence:1,source:"manual",evidence:l.evidence??{created_via:"context_menu"},approved:!0});return i.json({link:A})}catch(A){return i.json({error:A.message},400)}}),t.delete("/api/links/:id",i=>{let l=i.req.param("id"),m=Number(l);if(!Number.isInteger(m)||m<=0)return i.json({error:"id must be a positive integer"},400);let g=wc(m);return g.removed===0?i.json({error:`link ${m} not found`},404):i.json(g)}),t.get("/api/sessions/:id/links",i=>{let l=i.req.param("id");if(!l)return i.json({error:"sessionId required"},400);let m=h();if(!m.prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:`session not found: ${l}`},404);let _=i.req.query("type")??"wiki_link";if(!o.has(_))return i.json({error:`invalid type: ${_}`},400);let E=_,T=un(l).filter(I=>I.link_type===E),w=gp(l,T);if(w.length===0)return i.json({links:[]});let x=w.map(I=>I.otherSessionId),A=x.map(()=>"?").join(","),N=m.prepare(`SELECT s.id,
1874
1982
  NULLIF(sa.alias, '') AS alias,
1875
1983
  s.auto_title,
1876
1984
  s.first_user_message,
@@ -1878,7 +1986,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1878
1986
  FROM sessions s
1879
1987
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1880
1988
  LEFT JOIN projects p ON p.id = s.project_id
1881
- WHERE s.id IN (${k})`).all(...N),I=new Map(C.map(j=>[j.id,j]));return i.json({links:w.map(j=>{let v=I.get(j.otherSessionId),P=v?.alias?.trim()||v?.auto_title?.trim()||(v?.first_user_message?v.first_user_message.slice(0,80):"")||j.otherSessionId.slice(0,8);return{linkId:j.linkId,otherSessionId:j.otherSessionId,direction:j.direction,updatedAt:j.updatedAt,title:P,project:v?.project??null}})})}),t.patch("/api/links/suggestions/:id",async i=>{let l=i.req.param("id"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"id must be a positive integer"},400);let g=await i.req.json().catch(()=>null);if(!g||typeof g.status!="string")return i.json({error:"status required (approved|rejected)"},400);if(g.status!=="approved"&&g.status!=="rejected")return i.json({error:`invalid status: ${g.status}`},400);try{let _=hr(p,g.status);return i.json(_)}catch(_){let E=_.message,y=/already decided/.test(E)?409:/not found/.test(E)?404:400;return i.json({error:E},y)}}),t.post("/api/links/suggestions/bulk-decide",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=Array.isArray(l.ids)?l.ids:null;if(!p||p.length===0)return i.json({error:"ids must be a non-empty array"},400);if(p.length>1e3)return i.json({error:"bulk-decide capped at 1000 ids per call"},400);if(l.status!=="approved"&&l.status!=="rejected")return i.json({error:`invalid status: ${l.status}`},400);let g=l.status,_=0,E=0,y=[];for(let w of p){let N=Number(w);if(!Number.isInteger(N)||N<=0){y.push({id:Number.isFinite(Number(w))?Number(w):-1,error:"invalid id"});continue}try{hr(N,g),_+=1}catch(k){let C=k.message;/already decided/.test(C)?E+=1:y.push({id:N,error:C})}}return i.json({decided:_,skipped:E,errors:y})}),t.get("/api/sessions",i=>{let l=f(),p=i.req.query("project"),g=i.req.query("since"),_=i.req.query("until"),E=i.req.queries("tag")??[],y=i.req.query("collection"),w=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),N=i.req.query("cursor"),k=null;if(N)try{let K=Buffer.from(N,"base64url").toString("utf8"),G=JSON.parse(K);typeof G.ts=="string"&&typeof G.id=="string"&&(k={ts:G.ts,id:G.id})}catch{}let C=i.req.query("system")==="1"||i.req.query("system")==="true",I={limit:w},j="s.message_count > 2"+tr("s",C);if(p&&(j+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",I.proj=`%${An(p)}%`),g&&(j+=" AND s.started_at >= @since",I.since=g),_&&(j+=" AND s.started_at <= @until",/^\d{4}-\d{2}-\d{2}$/.test(_)?I.until=`${_}T23:59:59.999Z`:I.until=_),E.length>0&&E.map(G=>Ze(G)).filter(Boolean).forEach((G,U)=>{j+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${U})`,I[`tag_${U}`]=G}),y){let K=lo(y);if(K.length===0)return i.json({items:[],nextCursor:null});let G=K.map((U,X)=>`@col_${X}`).join(",");j+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${G}))`,K.forEach((U,X)=>{I[`col_${X}`]=U})}k&&(j+=" AND (COALESCE(s.ended_at, s.started_at, '') < @cursor_ts OR (COALESCE(s.ended_at, s.started_at, '') = @cursor_ts AND s.id < @cursor_id))",I.cursor_ts=k.ts,I.cursor_id=k.id);let v=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1989
+ WHERE s.id IN (${A})`).all(...x),C=new Map(N.map(I=>[I.id,I]));return i.json({links:w.map(I=>{let O=C.get(I.otherSessionId),H=O?.alias?.trim()||O?.auto_title?.trim()||(O?.first_user_message?O.first_user_message.slice(0,80):"")||I.otherSessionId.slice(0,8);return{linkId:I.linkId,otherSessionId:I.otherSessionId,direction:I.direction,updatedAt:I.updatedAt,title:H,project:O?.project??null}})})}),t.patch("/api/links/suggestions/:id",async i=>{let l=i.req.param("id"),m=Number(l);if(!Number.isInteger(m)||m<=0)return i.json({error:"id must be a positive integer"},400);let g=await i.req.json().catch(()=>null);if(!g||typeof g.status!="string")return i.json({error:"status required (approved|rejected)"},400);if(g.status!=="approved"&&g.status!=="rejected")return i.json({error:`invalid status: ${g.status}`},400);try{let _=eo(m,g.status);return i.json(_)}catch(_){let E=_.message,T=/already decided/.test(E)?409:/not found/.test(E)?404:400;return i.json({error:E},T)}}),t.post("/api/links/suggestions/bulk-decide",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let m=Array.isArray(l.ids)?l.ids:null;if(!m||m.length===0)return i.json({error:"ids must be a non-empty array"},400);if(m.length>1e3)return i.json({error:"bulk-decide capped at 1000 ids per call"},400);if(l.status!=="approved"&&l.status!=="rejected")return i.json({error:`invalid status: ${l.status}`},400);let g=l.status,_=0,E=0,T=[];for(let w of m){let x=Number(w);if(!Number.isInteger(x)||x<=0){T.push({id:Number.isFinite(Number(w))?Number(w):-1,error:"invalid id"});continue}try{eo(x,g),_+=1}catch(A){let N=A.message;/already decided/.test(N)?E+=1:T.push({id:x,error:N})}}return i.json({decided:_,skipped:E,errors:T})}),t.get("/api/sessions",i=>{let l=h(),m=i.req.query("project"),g=i.req.query("since"),_=i.req.query("until"),E=i.req.queries("tag")??[],T=i.req.query("collection"),w=Math.max(1,Math.min(500,Number(i.req.query("limit")??100))),x=i.req.query("cursor"),A=null;if(x)try{let Q=Buffer.from(x,"base64url").toString("utf8"),Y=JSON.parse(Q);typeof Y.ts=="string"&&typeof Y.id=="string"&&(A={ts:Y.ts,id:Y.id})}catch{}let N=i.req.query("system")==="1"||i.req.query("system")==="true",C={limit:w},I="s.message_count > 2"+Mr("s",N);if(m&&(I+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",C.proj=`%${Gn(m)}%`),g&&(I+=" AND s.started_at >= @since",C.since=g),_&&(I+=" AND s.started_at <= @until",/^\d{4}-\d{2}-\d{2}$/.test(_)?C.until=`${_}T23:59:59.999Z`:C.until=_),E.length>0&&E.map(Y=>it(Y)).filter(Boolean).forEach((Y,B)=>{I+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${B})`,C[`tag_${B}`]=Y}),T){let Q=Go(T);if(Q.length===0)return i.json({items:[],nextCursor:null});let Y=Q.map((B,X)=>`@col_${X}`).join(",");I+=` AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id IN (${Y}))`,Q.forEach((B,X)=>{C[`col_${X}`]=B})}A&&(I+=" AND (COALESCE(s.ended_at, s.started_at, '') < @cursor_ts OR (COALESCE(s.ended_at, s.started_at, '') = @cursor_ts AND s.id < @cursor_id))",C.cursor_ts=A.ts,C.cursor_id=A.id);let O=l.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1882
1990
  s.message_count, s.first_user_message, s.git_branch,
1883
1991
  s.auto_title, s.auto_title_source, s.verification_status,
1884
1992
  COALESCE(s.ended_at, s.started_at, '') AS _cursor_ts,
@@ -1897,17 +2005,17 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1897
2005
  JOIN projects p ON p.id = s.project_id
1898
2006
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1899
2007
  LEFT JOIN session_notes sn ON sn.session_id = s.id
1900
- WHERE ${j}
2008
+ WHERE ${I}
1901
2009
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC, s.id DESC
1902
- LIMIT @limit`).all(I),P=v.map(({tags_csv:K,_cursor_ts:G,...U})=>{let X=U.id,Y=M.getOrigin(X),Q=U.alias,V=Q==null?null:M.isSessionAutoLinked(X)?"auto":"manual",J=tn({auto_title:U.auto_title,auto_title_source:U.auto_title_source??null,has_alias:Q!=null&&V==="manual"});return{...U,tags:K?K.split(","):[],origin:Y?{editor:Y.editor,label:Y.label}:null,alias_source:V,title_quality:J}}),q=null;if(v.length===w&&v.length>0){let K=v[v.length-1],G=JSON.stringify({ts:K._cursor_ts??"",id:K.id});q=Buffer.from(G,"utf8").toString("base64url")}return i.json({items:P,nextCursor:q})}),t.get("/api/sessions/:id",i=>{let l=f(),p=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
2010
+ LIMIT @limit`).all(C),H=O.map(({tags_csv:Q,_cursor_ts:Y,...B})=>{let X=B.id,z=j.getOrigin(X),te=B.alias,Z=te==null?null:j.isSessionAutoLinked(X)?"auto":"manual",K=bn({auto_title:B.auto_title,auto_title_source:B.auto_title_source??null,has_alias:te!=null&&Z==="manual"});return{...B,tags:Q?Q.split(","):[],origin:z?{editor:z.editor,label:z.label}:null,alias_source:Z,title_quality:K}}),J=null;if(O.length===w&&O.length>0){let Q=O[O.length-1],Y=JSON.stringify({ts:Q._cursor_ts??"",id:Q.id});J=Buffer.from(Y,"utf8").toString("base64url")}return i.json({items:H,nextCursor:J})}),t.get("/api/sessions/:id",i=>{let l=h(),m=i.req.param("id"),g=l.prepare(`SELECT s.*, p.name AS project_name, p.decoded_path,
1903
2011
  NULLIF(sa.alias, '') AS alias
1904
2012
  FROM sessions s
1905
2013
  JOIN projects p ON p.id = s.project_id
1906
2014
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1907
- WHERE s.id = ?`).get(p);if(!g)return i.json({error:"not found"},404);let _=zt(p),E=M.getOrigin(p),y=E?{editor:E.editor,label:E.label}:null,w=g.alias==null?null:M.isSessionAutoLinked(p)?"auto":"manual",N=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2015
+ WHERE s.id = ?`).get(m);if(!g)return i.json({error:"not found"},404);let _=mn(m),E=j.getOrigin(m),T=E?{editor:E.editor,label:E.label}:null,w=g.alias==null?null:j.isSessionAutoLinked(m)?"auto":"manual",x=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1908
2016
  FROM messages
1909
2017
  WHERE session_id = ?
1910
- ORDER BY COALESCE(timestamp, ''), rowid`).all(p);return i.json({session:{...g,tags:_,origin:y,alias_source:w},messages:N})}),t.get("/api/tags",i=>i.json(dt())),t.get("/api/sessions/:id/tags",i=>i.json({tags:zt(i.req.param("id"))})),t.post("/api/sessions/:id/tags",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.tag!="string")return i.json({error:"tag required"},400);try{let g=ut(l,p.tag);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/tags/:tag",i=>{let l=i.req.param("id"),p=i.req.param("tag");return i.json(Ua(l,p))}),t.get("/api/config/auto-tag",i=>i.json(Po(Fe()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),p=Ds.partial().safeParse(l);if(!p.success)return i.json({error:"invalid config",issues:p.error.issues},400);let g=p.data;g.apiKey===void 0&&delete g.apiKey;let _=zu(g);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&Gs(),i.json(Po(_))}),t.get("/api/onboarding",i=>{let p=f().prepare(`SELECT s.id,
2018
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(m);return i.json({session:{...g,tags:_,origin:T,alias_source:w},messages:x})}),t.get("/api/tags",i=>i.json(Tt())),t.get("/api/sessions/:id/tags",i=>i.json({tags:mn(i.req.param("id"))})),t.post("/api/sessions/:id/tags",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.tag!="string")return i.json({error:"tag required"},400);try{let g=yt(l,m.tag);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/tags/:tag",i=>{let l=i.req.param("id"),m=i.req.param("tag");return i.json(Ic(l,m))}),t.get("/api/config/auto-tag",i=>i.json(ji(Be()))),t.put("/api/config/auto-tag",async i=>{let l=await i.req.json().catch(()=>({})),m=fr.partial().safeParse(l);if(!m.success)return i.json({error:"invalid config",issues:m.error.issues},400);let g=m.data;g.apiKey===void 0&&delete g.apiKey;let _=Mp(g);return _.autopilot&&_.enabled&&_.backend==="api"&&_.apiKey&&Ar(),i.json(ji(_))}),t.get("/api/onboarding",i=>{let m=h().prepare(`SELECT s.id,
1911
2019
  p.name AS project,
1912
2020
  s.started_at,
1913
2021
  s.ended_at,
@@ -1919,7 +2027,7 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1919
2027
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1920
2028
  WHERE s.message_count > 2
1921
2029
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1922
- LIMIT 1`).get();return i.json({state:zs(),mostRecentSession:p??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),p=Ys.partial().safeParse(l);return p.success?i.json(Ep(p.data)):i.json({error:"invalid onboarding state",issues:p.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(bp())),t.get("/api/config/mcp-install",i=>i.json({...qe(),claudeCliAvailable:pe()})),t.post("/api/config/mcp-install",i=>i.json({...dp(),claudeCliAvailable:pe()})),t.delete("/api/config/mcp-install",i=>i.json({...pp(),claudeCliAvailable:pe()}));let S=D.object({scope:D.object({untaggedOnly:D.boolean().optional(),project:D.string().optional(),collectionId:D.string().optional(),sessionIds:D.array(D.string()).optional(),limit:D.number().int().min(1).max(500).optional()}).default({}),model:D.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:D.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(Ai)return i.json({error:"a scan is already running"},409);if(!pe())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!qe().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),g=S.safeParse(p);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=g.data.scope,E=Fe(),y=g.data.model??E.model,w=f(),N=()=>w.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,k=N();Ai=!0;let C;try{let I=g.data.scanId;C=await Ar(_,{model:y,scanId:I});let j=N(),v=Math.max(0,j-k);return I&&Pn(I,{type:"done",result:{success:C.success,exitCode:C.exitCode,tagsAdded:v}}),i.json({success:C.success,exitCode:C.exitCode,tagsAdded:v,model:y,stdout:Se(C.stdout.slice(0,2e3)).redacted,stderrTail:Se(C.stderr.slice(-2e3)).redacted})}finally{Ai=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return Ye(i,async p=>{let g=[],_={resolve:()=>{}},E=new Promise(k=>{_.resolve=k}),y=Wa(l,k=>{g.push(k);let C=_.resolve;E=new Promise(I=>{_.resolve=I}),C()}),w=!1,N=setInterval(()=>{w||p.writeSSE({event:"heartbeat",data:""}).catch(()=>{w=!0})},15e3);try{for(;!w;){g.length===0&&await E;let k=g.shift();if(k&&(await p.writeSSE({event:k.type,data:JSON.stringify(k)}),k.type==="done"))break}}finally{w=!0,clearInterval(N),y()}})}),t.get("/api/prompts",i=>i.json({prompts:wr.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:pe()})),t.post("/api/prompts/run",async i=>{if(!pe())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!qe().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let p=await i.req.json().catch(()=>({})),_=D.object({name:D.string(),args:D.record(D.string(),D.unknown()).optional(),model:D.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).safeParse(p);if(!_.success)return i.json({error:"invalid request",issues:_.error.issues},400);let E=Ha(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let y=E.build(_.data.args??{}),w=Fe(),N=_.data.model??w.model,k=await Qe(y,E.allowedTools,{model:N});return i.json({success:k.success,exitCode:k.exitCode,promptName:E.name,model:N,stdout:k.stdout,stderrTail:k.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(Tn())),t.get("/api/autopilot/events",i=>Ye(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(Tn())});let p=[],g=()=>{},_=new Promise(y=>g=y),E=ap(y=>{p.push(y);let w=g;_=new Promise(N=>g=N),w()});try{for(;;){if(p.length===0){let w=new Promise(k=>setTimeout(()=>k("tick"),3e4));if(await Promise.race([_.then(()=>"event"),w])==="tick"){await l.writeSSE({event:"heartbeat",data:"1"});continue}}let y=p.shift();y&&await l.writeSSE({event:"state",data:JSON.stringify(y)})}}finally{E()}})),t.post("/api/autopilot/kick",i=>(Gs(),i.json({ok:!0,snapshot:Tn()})));let T=D.object({scope:D.object({untaggedOnly:D.boolean().optional(),project:D.string().optional(),collectionId:D.string().optional(),sessionIds:D.array(D.string()).optional(),limit:D.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=Fe();if(!l.enabled)return i.json({error:"auto-tagging is disabled"},403);if(l.backend!=="api")return i.json({error:"api-backend scan requires backend=api in config"},400);if(!l.apiKey)return i.json({error:"no api key configured"},400);let p=await i.req.json().catch(()=>({})),g=T.safeParse(p);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=mt(g.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=ep(_.length);return op(E,{apiKey:l.apiKey,model:l.model,minTags:l.minTagsPerSession,maxTags:l.maxTagsPerSession,sessions:_}),i.json({scanId:E.id,total:E.total})}),t.get("/api/tags/scan/:id",i=>{let l=Hs(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let{controller:p,listeners:g,..._}=l;return i.json(_)}),t.get("/api/tags/scan/:id/events",i=>{let l=Hs(i.req.param("id"));return l?Ye(i,async p=>{await p.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let w of l.results)await p.writeSSE({event:"result",data:JSON.stringify(w)});let g=[],_={resolve:()=>{}},E=new Promise(w=>{_.resolve=w}),y=tp(l,w=>{g.push(w);let N=_.resolve;E=new Promise(k=>{_.resolve=k}),N()});try{for(;l.status==="running"||l.status==="pending";){g.length===0&&await E;let w=g.shift();if(w&&(await p.writeSSE({event:w.type,data:JSON.stringify(w)}),w.type==="done"||w.type==="status"&&(w.status==="cancelled"||w.status==="failed")))break}}finally{y()}}):i.json({error:"scan not found"},404)});let R=D.object({selection:D.array(D.object({sessionId:D.string(),tags:D.array(D.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=Hs(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let p=await i.req.json().catch(()=>({})),g=R.safeParse(p);if(!g.success)return i.json({error:"invalid selection"},400);let _=ip(l,g.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return np(l),sp(l),i.json({ok:!0})}),t.put("/api/sessions/:id/alias",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.alias!="string")return i.json({error:"alias required"},400);try{let g=he(l,p.alias);if(p.pin===!0)M.unlinkSession(l);else{let _=f().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=_?.cwd?_.cwd.replace(/\/+$/,""):null,y=!1;if(E&&_?.started_at){let w=Date.parse(_.started_at),N=_.started_at,k=M.all().filter(C=>C.cwd&&C.cwd.replace(/\/+$/,"")===E&&on({sessionStartedAt:N,terminalOpenedAt:C.opened_at??null}).allowed);if(Number.isFinite(w)&&k.length>0){let I=k.map(j=>({t:j,gap:w-Date.parse(j.opened_at??"")})).filter(j=>Number.isFinite(j.gap)).sort((j,v)=>j.gap-v.gap)[0];I&&(M.linkSession(l,I.t.shell_pid),y=!0)}}y||M.unlinkSession(l)}return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return es(l),M.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:Te(l)})}),t.get("/api/config/auto-title",i=>i.json(st())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),p=ds.partial().safeParse(l);return p.success?i.json(ul(p.data)):i.json({error:"invalid config",issues:p.error.issues},400)}),t.get("/api/sessions/:id/auto-title",i=>{let l=i.req.param("id"),p=Le(l);return p?i.json(p):i.json({error:"session not found"},404)}),t.post("/api/sessions/:id/auto-title",async i=>{let l=i.req.param("id");if(!st().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!f().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await el(l);return Ee(l,E,"agent"),i.json(Le(l))}catch(E){return i.json({error:E.message,code:"agent-title-failed"},500)}}),t.post("/api/sessions/:id/auto-title/revert",i=>{let l=i.req.param("id"),p=Le(l);if(!p)return i.json({error:"session not found"},404);let g=p.auto_title_history;if(!g||g.length===0)return i.json({error:"no prior title to revert to",code:"no-history"},422);let _=g[g.length-1];return Ee(l,_.title,"agent"),i.json(Le(l))}),t.post("/api/sessions/:id/regenerate-title",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),g=p.model??Fs;try{let _=await $o(l,{model:g,force:p.force===!0,budget:typeof p.budget=="number"?p.budget:void 0,signal:i.req.raw.signal}),E=Le(l),y=E?.auto_title_history&&E.auto_title_history.length>0?E.auto_title_history[E.auto_title_history.length-1].title:null;return i.json({..._,previous_title:y})}catch(_){if(_ instanceof At)return i.json({error:_.message,code:"no-context-available",session_id:_.sessionId},422);let E=_ instanceof Error?_.message:"unknown error",y=/not found|unknown/i.test(E)?404:500;return i.json({error:E,code:"regenerate-failed"},y)}}),t.post("/api/sessions/regenerate-titles-batch",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let p=l.project;if(typeof p!="string"||p.length===0)return i.json({error:"project (string) required"},400);let g=l.quality_filter;if(!Array.isArray(g)||g.length===0)return i.json({error:"quality_filter (non-empty array) required"},400);let _=new Set(["low_signal","recursive_meta","programmatic"]),E=[];for(let v of g){if(typeof v!="string")return i.json({error:`invalid quality_filter entry: ${v}`},400);if(!_.has(v))return i.json({error:`quality_filter must be a subset of ${[..._].join(",")}; got ${v}`},400);E.push(v)}let y=typeof l.model=="string"&&l.model.length>0?l.model:Fs,w=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,N=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,C=f().prepare(`SELECT s.id,
2030
+ LIMIT 1`).get();return i.json({state:Or(),mostRecentSession:m??null})}),t.put("/api/onboarding",async i=>{let l=await i.req.json().catch(()=>({})),m=Nr.partial().safeParse(l);return m.success?i.json(sg(m.data)):i.json({error:"invalid onboarding state",issues:m.error.issues},400)}),t.post("/api/onboarding/reset",i=>i.json(rg())),t.get("/api/config/mcp-install",i=>i.json({...qe(),claudeCliAvailable:me()})),t.post("/api/config/mcp-install",i=>i.json({...oa(),claudeCliAvailable:me()})),t.delete("/api/config/mcp-install",i=>i.json({...Vm(),claudeCliAvailable:me()}));let y=$.object({scope:$.object({untaggedOnly:$.boolean().optional(),project:$.string().optional(),collectionId:$.string().optional(),sessionIds:$.array($.string()).optional(),limit:$.number().int().min(1).max(500).optional()}).default({}),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),scanId:$.string().min(1).max(100).optional()});t.post("/api/tags/scan/claude-cli",async i=>{if(ya)return i.json({error:"a scan is already running"},409);if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!qe().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let m=await i.req.json().catch(()=>({})),g=y.safeParse(m);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=g.data.scope,E=Be(),T=g.data.model??E.model,w=h(),x=()=>w.prepare("SELECT COUNT(*) AS n FROM session_tags").get().n,A=x();ya=!0;let N;try{let C=g.data.scanId;N=await lo(_,{model:T,scanId:C});let I=x(),O=Math.max(0,I-A);return C&&ls(C,{type:"done",result:{success:N.success,exitCode:N.exitCode,tagsAdded:O}}),i.json({success:N.success,exitCode:N.exitCode,tagsAdded:O,model:T,stdout:Se(N.stdout.slice(0,2e3)).redacted,stderrTail:Se(N.stderr.slice(-2e3)).redacted})}finally{ya=!1}}),t.get("/api/claude-cli/scan/:scanId/progress",i=>{let l=i.req.param("scanId");return tt(i,async m=>{let g=[],_={resolve:()=>{}},E=new Promise(A=>{_.resolve=A}),T=Dc(l,A=>{g.push(A);let N=_.resolve;E=new Promise(C=>{_.resolve=C}),N()}),w=!1,x=setInterval(()=>{w||m.writeSSE({event:"heartbeat",data:""}).catch(()=>{w=!0})},15e3);try{for(;!w;){g.length===0&&await E;let A=g.shift();if(A&&(await m.writeSSE({event:A.type,data:JSON.stringify(A)}),A.type==="done"))break}}finally{w=!0,clearInterval(x),T()}})}),t.get("/api/prompts",i=>i.json({prompts:io.map(l=>({name:l.name,title:l.title,description:l.description})),claudeCliAvailable:me()})),t.post("/api/prompts/run",async i=>{if(!me())return i.json({error:"claude CLI not found on PATH. Install Claude Code locally, then reload."},400);if(!qe().installed)return i.json({error:"Recall MCP is not installed in Claude Code yet, run the one-click install first."},400);let m=await i.req.json().catch(()=>({})),_=$.object({name:$.string(),args:$.record($.string(),$.unknown()).optional(),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).safeParse(m);if(!_.success)return i.json({error:"invalid request",issues:_.error.issues},400);let E=jc(_.data.name);if(!E)return i.json({error:`unknown prompt: ${_.data.name}`},404);let T=E.build(_.data.args??{}),w=Be(),x=_.data.model??w.model,A=await at(T,E.allowedTools,{model:x});return i.json({success:A.success,exitCode:A.exitCode,promptName:E.name,model:x,stdout:A.stdout,stderrTail:A.stderr.slice(-4e3)})}),t.get("/api/autopilot/status",i=>i.json(Xn())),t.get("/api/autopilot/events",i=>tt(i,async l=>{await l.writeSSE({event:"state",data:JSON.stringify(Xn())});let m=[],g=()=>{},_=new Promise(T=>g=T),E=Jm(T=>{m.push(T);let w=g;_=new Promise(x=>g=x),w()});try{for(;;){if(m.length===0){let w=new Promise(A=>setTimeout(()=>A("tick"),3e4));if(await Promise.race([_.then(()=>"event"),w])==="tick"){await l.writeSSE({event:"heartbeat",data:"1"});continue}}let T=m.shift();T&&await l.writeSSE({event:"state",data:JSON.stringify(T)})}}finally{E()}})),t.post("/api/autopilot/kick",i=>(Ar(),i.json({ok:!0,snapshot:Xn()})));let b=$.object({scope:$.object({untaggedOnly:$.boolean().optional(),project:$.string().optional(),collectionId:$.string().optional(),sessionIds:$.array($.string()).optional(),limit:$.number().int().min(1).max(500).optional()}).default({})});t.post("/api/tags/scan",async i=>{let l=Be();if(!l.enabled)return i.json({error:"auto-tagging is disabled"},403);if(l.backend!=="api")return i.json({error:"api-backend scan requires backend=api in config"},400);if(!l.apiKey)return i.json({error:"no api key configured"},400);let m=await i.req.json().catch(()=>({})),g=b.safeParse(m);if(!g.success)return i.json({error:"invalid scope",issues:g.error.issues},400);let _=Rt(g.data.scope);if(_.length===0)return i.json({error:"no sessions match scope"},400);let E=$m(_.length);return qm(E,{apiKey:l.apiKey,model:l.model,minTags:l.minTagsPerSession,maxTags:l.maxTagsPerSession,sessions:_}),i.json({scanId:E.id,total:E.total})}),t.get("/api/tags/scan/:id",i=>{let l=yr(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let{controller:m,listeners:g,..._}=l;return i.json(_)}),t.get("/api/tags/scan/:id/events",i=>{let l=yr(i.req.param("id"));return l?tt(i,async m=>{await m.writeSSE({event:"state",data:JSON.stringify({completed:l.completed,total:l.total,status:l.status})});for(let w of l.results)await m.writeSSE({event:"result",data:JSON.stringify(w)});let g=[],_={resolve:()=>{}},E=new Promise(w=>{_.resolve=w}),T=Um(l,w=>{g.push(w);let x=_.resolve;E=new Promise(A=>{_.resolve=A}),x()});try{for(;l.status==="running"||l.status==="pending";){g.length===0&&await E;let w=g.shift();if(w&&(await m.writeSSE({event:w.type,data:JSON.stringify(w)}),w.type==="done"||w.type==="status"&&(w.status==="cancelled"||w.status==="failed")))break}}finally{T()}}):i.json({error:"scan not found"},404)});let R=$.object({selection:$.array($.object({sessionId:$.string(),tags:$.array($.string()).min(1)}))});t.post("/api/tags/scan/:id/apply",async i=>{let l=yr(i.req.param("id"));if(!l)return i.json({error:"scan not found"},404);let m=await i.req.json().catch(()=>({})),g=R.safeParse(m);if(!g.success)return i.json({error:"invalid selection"},400);let _=Xm(l,g.data.selection);return i.json(_)}),t.delete("/api/tags/scan/:id",i=>{let l=i.req.param("id");return Hm(l),Bm(l),i.json({ok:!0})}),t.put("/api/sessions/:id/alias",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.alias!="string")return i.json({error:"alias required"},400);try{let g=he(l,m.alias);if(m.pin===!0)j.unlinkSession(l);else{let _=h().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l),E=_?.cwd?_.cwd.replace(/\/+$/,""):null,T=!1;if(E&&_?.started_at){let w=Date.parse(_.started_at),x=_.started_at,A=j.all().filter(N=>N.cwd&&N.cwd.replace(/\/+$/,"")===E&&Cn({sessionStartedAt:x,terminalOpenedAt:N.opened_at??null}).allowed);if(Number.isFinite(w)&&A.length>0){let C=A.map(I=>({t:I,gap:w-Date.parse(I.opened_at??"")})).filter(I=>Number.isFinite(I.gap)).sort((I,O)=>I.gap-O.gap)[0];C&&(j.linkSession(l,C.t.shell_pid),T=!0)}}T||j.unlinkSession(l)}return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return ks(l),j.unlinkSession(l),i.json({ok:!0})}),t.get("/api/sessions/:id/alias",i=>{let l=i.req.param("id");return i.json({alias:ye(l)})}),t.get("/api/config/auto-title",i=>i.json(dt())),t.put("/api/config/auto-title",async i=>{let l=await i.req.json().catch(()=>({})),m=Ds.partial().safeParse(l);return m.success?i.json(ru(m.data)):i.json({error:"invalid config",issues:m.error.issues},400)}),t.get("/api/sessions/:id/auto-title",i=>{let l=i.req.param("id"),m=ve(l);return m?i.json(m):i.json({error:"session not found"},404)}),t.post("/api/sessions/:id/auto-title",async i=>{let l=i.req.param("id");if(!dt().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);if(!h().prepare("SELECT 1 FROM sessions WHERE id = ?").get(l))return i.json({error:"session not found"},404);try{let E=await Yl(l);return Ee(l,E,"agent"),i.json(ve(l))}catch(E){return i.json({error:E.message,code:"agent-title-failed"},500)}}),t.post("/api/sessions/:id/auto-title/revert",i=>{let l=i.req.param("id"),m=ve(l);if(!m)return i.json({error:"session not found"},404);let g=m.auto_title_history;if(!g||g.length===0)return i.json({error:"no prior title to revert to",code:"no-history"},422);let _=g[g.length-1];return Ee(l,_.title,"agent"),i.json(ve(l))}),t.post("/api/sessions/:id/regenerate-title",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({})),g=m.model??_r;try{let _=await Di(l,{model:g,force:m.force===!0,budget:typeof m.budget=="number"?m.budget:void 0,signal:i.req.raw.signal}),E=ve(l),T=E?.auto_title_history&&E.auto_title_history.length>0?E.auto_title_history[E.auto_title_history.length-1].title:null;return i.json({..._,previous_title:T})}catch(_){if(_ instanceof Bt)return i.json({error:_.message,code:"no-context-available",session_id:_.sessionId},422);let E=_ instanceof Error?_.message:"unknown error",T=/not found|unknown/i.test(E)?404:500;return i.json({error:E,code:"regenerate-failed"},T)}}),t.post("/api/sessions/regenerate-titles-batch",async i=>{let l=await i.req.json().catch(()=>null);if(!l)return i.json({error:"body required"},400);let m=l.project;if(typeof m!="string"||m.length===0)return i.json({error:"project (string) required"},400);let g=l.quality_filter;if(!Array.isArray(g)||g.length===0)return i.json({error:"quality_filter (non-empty array) required"},400);let _=new Set(["low_signal","recursive_meta","programmatic"]),E=[];for(let O of g){if(typeof O!="string")return i.json({error:`invalid quality_filter entry: ${O}`},400);if(!_.has(O))return i.json({error:`quality_filter must be a subset of ${[..._].join(",")}; got ${O}`},400);E.push(O)}let T=typeof l.model=="string"&&l.model.length>0?l.model:_r,w=typeof l.limit=="number"&&l.limit>0?Math.min(2e3,Math.floor(l.limit)):500,x=typeof l.budget=="number"&&l.budget>=100?Math.floor(l.budget):void 0,N=h().prepare(`SELECT s.id,
1923
2031
  s.auto_title,
1924
2032
  s.auto_title_source,
1925
2033
  NULLIF(sa.alias, '') AS alias
@@ -1928,14 +2036,16 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1928
2036
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1929
2037
  WHERE p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\'
1930
2038
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1931
- LIMIT @limit`).all({proj:`%${An(p)}%`,limit:w}),I=new Set(E),j=C.filter(v=>{let P=v.alias==null?null:M.isSessionAutoLinked(v.id)?"auto":"manual",q=tn({auto_title:v.auto_title,auto_title_source:v.auto_title_source??null,has_alias:v.alias!=null&&P==="manual"});return I.has(q)});return Ye(i,async v=>{let P=j.length,q=[],K=[],G=[],U=0,X=async(Q,V)=>{U+=1;try{await v.writeSSE({id:String(U),event:Q,data:JSON.stringify(V)})}catch{}};await X("start",{total:P,model:y});let Y=0;for(let Q of j){if(i.req.raw.signal.aborted)break;Y+=1;try{let V=await $o(Q.id,{model:y,budget:N,signal:i.req.raw.signal});V.written?(q.push(Q.id),await X("progress",{sessionId:Q.id,title:V.title,evidence:V.evidence,confidence:V.confidence,current:Y,total:P})):(K.push({sessionId:Q.id,reason:V.skipped??"unknown"}),await X("skipped",{sessionId:Q.id,reason:V.skipped??"unknown",current:Y,total:P}))}catch(V){let J=V instanceof Error?V.message:String(V),oe=V instanceof At?"no-context-available":"failed";G.push({sessionId:Q.id,error:J}),await X("error",{sessionId:Q.id,error:J,code:oe,current:Y,total:P})}}await X("done",{generated:q,skipped:K,failed:G,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),p=ws(l);return p?i.json(p):i.body(null,204)}),t.put("/api/sessions/:id/notes",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.content!="string")return i.json({error:"content required (string)"},400);try{let g=Gl(l,p.content);return i.json(g)}catch(g){return console.error("[notes] failed to save note for session",l,g),i.json({error:"failed to save note"},500)}}),t.post("/api/sessions/:id/generate-note",async i=>{let l=i.req.param("id");if(!st().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await Yl(l),_=zl(l,g);return i.json(_)}catch(g){let _=g.message,E=/no messages available/i.test(_)?404:500;return i.json({error:_},E)}}),t.get("/api/semantic/status",i=>i.json(Or())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),p=Hn.partial().safeParse(l);return p.success?(Wn(p.data),i.json(Or())):i.json({error:"invalid semantic config",issues:p.error.issues},400)}),t.get("/api/semantic/config",i=>i.json(ce())),t.post("/api/semantic/backfill",je,async i=>{if(Ni)return i.json({error:"a scan is already running"},409);if(!ce().enabled)return i.json({error:"semantic search is disabled"},400);let p=await i.req.json().catch(()=>({})),g=Math.max(1,Math.min(5e3,Number(p.limit??200)));return Ni=!0,Jn({limit:g,force:!!p.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{Ni=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",je,async i=>{if(!ce().enabled)return i.json({error:"semantic search is disabled"},400);let p=i.req.param("id");if(!p)return i.json({error:"session id required"},400);let g=await Xn(p);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=_e(),p=Ie(),g=Ge(),_=(i.req.query("project")??"").trim(),E=_.length>512?"":_,y=null,w=0;if(E){let I=f(),j=I.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(E,E);j?y=I.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(j.id).n:y=0}let N=Ti(),k=N?.chunksPerSec??0,C=k>0?Math.ceil(p.queueDepth/k):0;return k>0&&y!==null&&(w=Math.ceil(y/k)),i.json({embedder:l,worker:p,modelInstalled:g,project:E||null,queueDepthForProject:y,etaForProject:w,throughput:{chunksPerSec:k,samples:N?.samples??0,source:N?"local-measured":"no-samples-yet"},etaSeconds:C})}),t.post("/api/semantic/install",je,async i=>{if(Ge())return i.json({ok:!0,status:"already_installed"});if(xi)return i.json({error:"a scan is already running"},409);xi=!0;try{return await tm(),await Ue(),kn(),i.json({ok:!0,status:"installed"})}catch(l){let p=l instanceof Error?l.message:"unknown error";return i.json({ok:!1,error:p},500)}finally{xi=!1}}),t.get("/api/semantic/reindex-preview",je,async i=>{let l=(i.req.query("project")??"").trim();if(!l)return i.json({error:"project name required"},400);let p=f(),g=p.prepare("SELECT id, name FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(l,l);if(!g)return i.json({error:`project not found: ${l}`},404);let _=p.prepare(`SELECT
2039
+ LIMIT @limit`).all({proj:`%${Gn(m)}%`,limit:w}),C=new Set(E),I=N.filter(O=>{let H=O.alias==null?null:j.isSessionAutoLinked(O.id)?"auto":"manual",J=bn({auto_title:O.auto_title,auto_title_source:O.auto_title_source??null,has_alias:O.alias!=null&&H==="manual"});return C.has(J)});return tt(i,async O=>{let H=I.length,J=[],Q=[],Y=[],B=0,X=async(te,Z)=>{B+=1;try{await O.writeSSE({id:String(B),event:te,data:JSON.stringify(Z)})}catch{}};await X("start",{total:H,model:T});let z=0;for(let te of I){if(i.req.raw.signal.aborted)break;z+=1;try{let Z=await Di(te.id,{model:T,budget:x,signal:i.req.raw.signal});Z.written?(J.push(te.id),await X("progress",{sessionId:te.id,title:Z.title,evidence:Z.evidence,confidence:Z.confidence,current:z,total:H})):(Q.push({sessionId:te.id,reason:Z.skipped??"unknown"}),await X("skipped",{sessionId:te.id,reason:Z.skipped??"unknown",current:z,total:H}))}catch(Z){let K=Z instanceof Error?Z.message:String(Z),ie=Z instanceof Bt?"no-context-available":"failed";Y.push({sessionId:te.id,error:K}),await X("error",{sessionId:te.id,error:K,code:ie,current:z,total:H})}}await X("done",{generated:J,skipped:Q,failed:Y,cancelled:i.req.raw.signal.aborted})})}),t.get("/api/sessions/:id/notes",i=>{let l=i.req.param("id"),m=nr(l);return m?i.json(m):i.body(null,204)}),t.put("/api/sessions/:id/notes",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.content!="string")return i.json({error:"content required (string)"},400);try{let g=vd(l,m.content);return i.json(g)}catch(g){return console.error("[notes] failed to save note for session",l,g),i.json({error:"failed to save note"},500)}}),t.post("/api/sessions/:id/generate-note",async i=>{let l=i.req.param("id");if(!dt().agentEnabled)return i.json({error:"autoTitle.agentEnabled is false"},403);try{let g=await Id(l),_=Md(l,g);return i.json(_)}catch(g){let _=g.message,E=/no messages available/i.test(_)?404:500;return i.json({error:_},E)}}),t.get("/api/semantic/status",i=>i.json(mo())),t.put("/api/semantic/config",async i=>{let l=await i.req.json().catch(()=>({})),m=ms.partial().safeParse(l);return m.success?(gs(m.data,"api"),i.json(mo())):i.json({error:"invalid semantic config",issues:m.error.issues},400)}),t.get("/api/semantic/config",i=>i.json(ae())),t.post("/api/semantic/backfill",Fe,async i=>{if(wa)return i.json({error:"a scan is already running"},409);if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let m=await i.req.json().catch(()=>({})),g=Math.max(1,Math.min(5e3,Number(m.limit??200)));return wa=!0,hs({limit:g,force:!!m.force}).catch(_=>console.error("[semantic.backfill] error:",_)).finally(()=>{wa=!1}),i.json({ok:!0,limit:g})}),t.post("/api/semantic/sessions/:id",Fe,async i=>{if(!ae().enabled)return i.json({error:"semantic search is disabled"},400);let m=i.req.param("id");if(!m)return i.json({error:"session id required"},400);let g=await _s(m);return i.json(g)}),t.get("/api/semantic/vector-status",i=>{let l=pe(),m=Me(),g=et(),_=(i.req.query("project")??"").trim(),E=_.length>512?"":_,T=null,w=0,x=h();if(E){let X=x.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(E,E);X?T=x.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(X.id).n:T=0}let A=x.prepare(`SELECT COALESCE(SUM(s.message_count), 0) AS n
2040
+ FROM chunk_queue cq
2041
+ JOIN sessions s ON s.id = cq.session_id`).get(),N=Number(A.n)||0,C=ii(),I=C?.chunksPerSec??0,O=C?.samples??0,H=Kk(N),Q=I>0?Math.ceil(N/I):0,Y=null;if(O>=5&&I>0){let X=I-H.deltaPerSec;X>0&&H.deltaPerSec>0?Y=Math.ceil(N/X):(H.deltaPerSec<=0,Y=null)}let B="no-samples";return O>=5&&I>0&&(B=H.deltaPerSec>0?"live":"stable"),I>0&&T!==null&&(w=Math.ceil(T/I)),i.json({embedder:l,worker:m,modelInstalled:g,project:E||null,queueDepthForProject:T,etaForProject:w,remainingChunks:N,throughput:{chunksPerSec:I,samples:O,source:C?"local-measured":"no-samples-yet"},etaSeconds:Q,etaSecondsDetail:{currentQueue:Q,withGrowth:Y,source:B},queueGrowthChunksPerSec:H.deltaPerSec})}),t.post("/api/semantic/install",Fe,async i=>{if(et())return i.json({ok:!0,status:"already_installed"});if(Ta)return i.json({error:"a scan is already running"},409);Ta=!0;try{return await Ag(),await Ge(),Nn(),i.json({ok:!0,status:"installed"})}catch(l){let m=l instanceof Error?l.message:"unknown error";return i.json({ok:!1,error:m},500)}finally{Ta=!1}}),t.get("/api/semantic/reindex-preview",Fe,async i=>{let l=(i.req.query("project")??"").trim();if(!l)return i.json({error:"project name required"},400);let m=h(),g=m.prepare("SELECT id, name FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(l,l);if(!g)return i.json({error:`project not found: ${l}`},404);let _=m.prepare(`SELECT
1932
2042
  COUNT(*) AS total,
1933
2043
  SUM(CASE WHEN s.message_count >= 3 THEN 1 ELSE 0 END) AS eligible,
1934
2044
  SUM(CASE WHEN s.message_count >= 3 AND EXISTS
1935
2045
  (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id) THEN 1 ELSE 0 END)
1936
2046
  AS indexed
1937
- FROM sessions s WHERE s.project_id = ?`).get(g.id),E=_.eligible??0,y=_.indexed??0,w=Math.max(0,E-y),N=p.prepare(`SELECT COUNT(*) AS n FROM chunk_queue
1938
- WHERE session_id NOT IN (SELECT id FROM sessions WHERE project_id = ?)`).get(g.id).n,k=Ti(),C=2,I=30,j=C,v=I,P=C*I,q="fallback-baseline",K=0;if(k)j=k.sessionsPerSec,v=k.avgChunksPerSession,P=k.chunksPerSec,q="local-measured",K=k.samples;else if(Ge()){if(!_e().loaded)try{await Ue()}catch{}try{let Y=["[user] benchmark probe one \u2014 typical session opening turn","[assistant] benchmark probe two \u2014 typical assistant response with code reference","[user] benchmark probe three \u2014 typical follow-up clarification"],Q=Date.now();await at(Y);let V=Date.now()-Q;V>0&&(P=Y.length*1e3/V,j=P/I,q="live-benchmark")}catch{}}let G=Y=>p.prepare(`SELECT
2047
+ FROM sessions s WHERE s.project_id = ?`).get(g.id),E=_.eligible??0,T=_.indexed??0,w=Math.max(0,E-T),x=m.prepare(`SELECT COUNT(*) AS n FROM chunk_queue
2048
+ WHERE session_id NOT IN (SELECT id FROM sessions WHERE project_id = ?)`).get(g.id).n,A=ii(),N=2,C=30,I=N,O=C,H=N*C,J="fallback-baseline",Q=0;if(A)I=A.sessionsPerSec,O=A.avgChunksPerSession,H=A.chunksPerSec,J="local-measured",Q=A.samples;else if(et()){if(!pe().loaded)try{await Ge()}catch{}try{let z=["[user] benchmark probe one \u2014 typical session opening turn","[assistant] benchmark probe two \u2014 typical assistant response with code reference","[user] benchmark probe three \u2014 typical follow-up clarification"],te=Date.now();await Et(z);let Z=Date.now()-te;Z>0&&(H=z.length*1e3/Z,I=H/C,J="live-benchmark")}catch{}}let Y=z=>m.prepare(`SELECT
1939
2049
  COALESCE(SUM(CASE WHEN ec > 5 THEN 5 ELSE ec END), 0) AS total_quick,
1940
2050
  COALESCE(SUM(CASE WHEN ec > 80 THEN 80 ELSE ec END), 0) AS total_standard,
1941
2051
  COALESCE(SUM(CASE WHEN ec > 200 THEN 200 ELSE ec END), 0) AS total_full,
@@ -1947,10 +2057,10 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1947
2057
  ELSE (s.message_count + 3) / 4
1948
2058
  END AS ec
1949
2059
  FROM sessions s
1950
- WHERE s.project_id = ? AND s.message_count >= 3 ${Y}
1951
- )`).get(g.id),U=G("AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)"),X=G("");return i.json({project:g.name,total:_.total??0,eligible:E,indexed:y,pendingNew:w,pendingForce:E,modelInstalled:Ge(),modelName:"BAAI/bge-base-en-v1.5",embedderLoaded:_e().loaded,workerRunning:Ie().running,queueDepthOther:N,sessionsPerSec:j,avgChunksPerSession:v,chunksPerSec:P,throughputSource:q,throughputSamples:K,estimatedChunksByDepth:{new:{quick:U.total_quick,standard:U.total_standard,full:U.total_full,uncapped:U.total_uncapped},force:{quick:X.total_quick,standard:X.total_standard,full:X.total_full,uncapped:X.total_uncapped}}})}),t.post("/api/semantic/cancel-reindex",je,async i=>{let l={};try{l=await i.req.json()}catch{}let p=(l.project??"").trim(),g=f(),_=0,E=null;if(p){let y=g.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(p,p);if(!y)return i.json({error:`project not found: ${p}`},404);let w=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(y.id).n;g.prepare("DELETE FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").run(y.id),_=w,E=y.id}else{let y=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n;g.prepare("DELETE FROM chunk_queue").run(),_=y}return E!==null?Jp(E):Gp(),Ie().queueDepth===0&&zp(),i.json({cleared:_,project:p||null,queueDepth:Ie().queueDepth})}),t.post("/api/semantic/reindex-project",je,async i=>{if(!Ge())return i.json({error:"embedder not installed \u2014 run `recall semantic install` first"},503);let l={};try{l=await i.req.json()}catch{}let p=(l.project??"").trim();if(!p)return i.json({error:"project name required"},400);let g=!!l.force,_=Math.max(0,Math.floor(Number(l.maxChunks??0)));qp(_);let E=f(),y=E.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(p,p);if(!y)return i.json({error:`project not found: ${p}`},404);let w=g?"SELECT id FROM sessions WHERE project_id = ? AND message_count >= 3":`SELECT s.id FROM sessions s
2060
+ WHERE s.project_id = ? AND s.message_count >= 3 ${z}
2061
+ )`).get(g.id),B=Y("AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)"),X=Y("");return i.json({project:g.name,total:_.total??0,eligible:E,indexed:T,pendingNew:w,pendingForce:E,modelInstalled:et(),modelName:"BAAI/bge-base-en-v1.5",embedderLoaded:pe().loaded,workerRunning:Me().running,queueDepthOther:x,sessionsPerSec:I,avgChunksPerSession:O,chunksPerSec:H,throughputSource:J,throughputSamples:Q,estimatedChunksByDepth:{new:{quick:B.total_quick,standard:B.total_standard,full:B.total_full,uncapped:B.total_uncapped},force:{quick:X.total_quick,standard:X.total_standard,full:X.total_full,uncapped:X.total_uncapped}}})}),t.post("/api/semantic/cancel-reindex",Fe,async i=>{let l={};try{l=await i.req.json()}catch{}let m=(l.project??"").trim(),g=h(),_=0,E=null;if(m){let T=g.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(m,m);if(!T)return i.json({error:`project not found: ${m}`},404);let w=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").get(T.id).n;g.prepare("DELETE FROM chunk_queue WHERE session_id IN (SELECT id FROM sessions WHERE project_id = ?)").run(T.id),_=w,E=T.id}else{let T=g.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n;g.prepare("DELETE FROM chunk_queue").run(),_=T}return E!==null?Pu(E):Fu(),Me().queueDepth===0&&Uu(),i.json({cleared:_,project:m||null,queueDepth:Me().queueDepth})}),t.post("/api/semantic/reindex-project",Fe,async i=>{if(!et())return i.json({error:"embedder not installed \u2014 run `recall semantic install` first"},503);let l={};try{l=await i.req.json()}catch{}let m=(l.project??"").trim();if(!m)return i.json({error:"project name required"},400);let g=!!l.force,_=Math.max(0,Math.floor(Number(l.maxChunks??0)));ju(_);let E=h(),T=E.prepare("SELECT id FROM projects WHERE name = ? OR decoded_path = ? LIMIT 1").get(m,m);if(!T)return i.json({error:`project not found: ${m}`},404);let w=g?"SELECT id FROM sessions WHERE project_id = ? AND message_count >= 3":`SELECT s.id FROM sessions s
1952
2062
  WHERE s.project_id = ? AND s.message_count >= 3
1953
- AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)`,N=E.prepare(w).all(y.id);if(N.length===0)return i.json({enqueued:0,queueDepth:Ie().queueDepth,message:"nothing to do \u2014 every session in this project is already vectorized (use force:true to re-embed)"});let k=E.prepare("INSERT INTO chunk_queue(session_id, action) VALUES (?, 'embed')");if(E.transaction(()=>{for(let I of N)k.run(I.id)})(),!_e().loaded)try{await Ue()}catch(I){let j=I instanceof Error?I.message:"unknown error";return i.json({error:`embedder load failed: ${j}`},500)}return Ie().running||kn(),i.json({enqueued:N.length,queueDepth:Ie().queueDepth,project:p,appliedMaxChunks:_})}),t.get("/api/sessions/:id/similar",je,async i=>{if(!_e().loaded)return i.json({error:"vector model not loaded"},503);let l=i.req.param("id"),p=Math.max(1,Math.min(50,Number(i.req.query("limit")??10)));try{let g=await jp(l,p);return i.json({sessionId:l,similar:g})}catch(g){let _=g instanceof Error?g.message:"unknown error";return i.json({error:_},500)}}),t.get("/api/search",je,async i=>{let l=f(),p=i.req.query("q")?.trim();if(!p)return i.json({query:"",hits:[],tags:[]});if(p.length>500)return i.json({error:"query too long (max 500 chars)"},400);let g=i.req.query("project"),_=p.split(/\s+/).filter(J=>J.length>0),E=_.filter(J=>J.startsWith("#")).map(J=>Ze(J)).filter(Boolean),y=_.filter(J=>!J.startsWith("#")),w=y.length>20,k=(w?y.slice(0,20):y).map(J=>`"${J.replace(/"/g,"")}"`),C=k.join(" "),I=Math.max(1,Math.min(200,Number(i.req.query("limit")??30))),j=i.req.query("system")==="1"||i.req.query("system")==="true",v=tr("s",j);if(k.length===0&&E.length>0){let J=`
2063
+ AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)`,x=E.prepare(w).all(T.id);if(x.length===0)return i.json({enqueued:0,queueDepth:Me().queueDepth,message:"nothing to do \u2014 every session in this project is already vectorized (use force:true to re-embed)"});let A=E.prepare("INSERT INTO chunk_queue(session_id, action) VALUES (?, 'embed')");if(E.transaction(()=>{for(let C of x)A.run(C.id)})(),!pe().loaded)try{await Ge()}catch(C){let I=C instanceof Error?C.message:"unknown error";return i.json({error:`embedder load failed: ${I}`},500)}return Me().running||Nn(),i.json({enqueued:x.length,queueDepth:Me().queueDepth,project:m,appliedMaxChunks:_})}),t.get("/api/sessions/:id/similar",Fe,async i=>{if(!pe().loaded)return i.json({error:"vector model not loaded"},503);let l=i.req.param("id"),m=Math.max(1,Math.min(50,Number(i.req.query("limit")??10)));try{let g=await bg(l,m);return i.json({sessionId:l,similar:g})}catch(g){let _=g instanceof Error?g.message:"unknown error";return i.json({error:_},500)}}),t.get("/api/search",Fe,async i=>{let l=h(),m=i.req.query("q")?.trim();if(!m)return i.json({query:"",hits:[],tags:[]});if(m.length>500)return i.json({error:"query too long (max 500 chars)"},400);let g=i.req.query("project"),_=m.split(/\s+/).filter(K=>K.length>0),E=_.filter(K=>K.startsWith("#")).map(K=>it(K)).filter(Boolean),T=_.filter(K=>!K.startsWith("#")),w=T.length>20,A=(w?T.slice(0,20):T).map(K=>`"${K.replace(/"/g,"")}"`),N=A.join(" "),C=Math.max(1,Math.min(200,Number(i.req.query("limit")??30))),I=i.req.query("system")==="1"||i.req.query("system")==="true",O=Mr("s",I);if(A.length===0&&E.length>0){let K=`
1954
2064
  SELECT s.id AS session_id,
1955
2065
  s.id AS message_uuid,
1956
2066
  p.name AS project,
@@ -1962,8 +2072,8 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1962
2072
  FROM sessions s
1963
2073
  JOIN projects p ON p.id = s.project_id
1964
2074
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1965
- WHERE 1=1${v}
1966
- `,oe={limit:I};g&&(J+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",oe.proj=`%${An(g)}%`),E.forEach((Me,me)=>{J+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${me})`,oe[`tag_${me}`]=Me}),J+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let ze=l.prepare(J).all(oe);return i.json({query:p,hits:ze,tags:E,truncated:w})}let P=`
2075
+ WHERE 1=1${O}
2076
+ `,ie={limit:C};g&&(K+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Gn(g)}%`),E.forEach(($e,ge)=>{K+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ge})`,ie[`tag_${ge}`]=$e}),K+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit";let nt=l.prepare(K).all(ie);return i.json({query:m,hits:nt,tags:E,truncated:w})}let H=`
1967
2077
  SELECT m.session_id AS session_id,
1968
2078
  m.uuid AS message_uuid,
1969
2079
  p.name AS project,
@@ -1977,8 +2087,8 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1977
2087
  JOIN sessions s ON s.id = m.session_id
1978
2088
  JOIN projects p ON p.id = s.project_id
1979
2089
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1980
- WHERE messages_fts MATCH @fts${v}
1981
- `,q={fts:C,limit:I};g&&(P+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",q.proj=`%${An(g)}%`),E.forEach((J,oe)=>{P+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${oe})`,q[`tag_${oe}`]=J}),P+=" ORDER BY bm25(messages_fts) LIMIT @limit";let G=l.prepare(P).all(q).map(J=>({...J,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:p,hits:G,tags:E,truncated:w});let X=[];try{let J=`
2090
+ WHERE messages_fts MATCH @fts${O}
2091
+ `,J={fts:N,limit:C};g&&(H+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",J.proj=`%${Gn(g)}%`),E.forEach((K,ie)=>{H+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${ie})`,J[`tag_${ie}`]=K}),H+=" ORDER BY bm25(messages_fts) LIMIT @limit";let Y=l.prepare(H).all(J).map(K=>({...K,matched_via:"fts"}));if(i.req.query("mode")!=="semantic")return i.json({query:m,hits:Y,tags:E,truncated:w});let X=[];try{let K=`
1982
2092
  SELECT s.id AS session_id,
1983
2093
  s.id AS message_uuid,
1984
2094
  p.name AS project,
@@ -1993,21 +2103,30 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
1993
2103
  JOIN sessions s ON s.id = ss.session_id
1994
2104
  JOIN projects p ON p.id = s.project_id
1995
2105
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1996
- WHERE sessions_fts MATCH @fts${v}
1997
- `,oe={fts:C,limit:I};g&&(J+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",oe.proj=`%${An(g)}%`),E.forEach((ze,Me)=>{J+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${Me})`,oe[`tag_${Me}`]=ze}),J+=" ORDER BY rank LIMIT @limit",X=l.prepare(J).all(oe)}catch(J){console.error("[search.semantic] failed:",J)}if(_e().loaded)try{let J=await Ip(p,I),oe=G.map(ue=>({id:String(ue.session_id),data:ue,lane:"bm25"})),ze=X.map(ue=>({id:String(ue.session_id),data:ue,lane:"summary"})),Me=J.map(ue=>({id:ue.sessionId,data:{session_id:ue.sessionId,snippet:ue.text,matched_via:"vector"},lane:"vector"})),Am=Mp([oe,ze,Me]).slice(0,I).map(ue=>({...ue.data,session_id:ue.id,rrf_score:ue.score,lanes:ue.lanes,matched_via:ue.lanes.length>1?"fused":ue.lanes[0]}));return i.json({query:p,hits:Am,tags:E,mode:"semantic",fusion:"rrf",truncated:w})}catch(J){console.error("[search.vector] failed, falling back:",J)}let Y=new Set(G.map(J=>String(J.session_id))),Q=X.filter(J=>!Y.has(String(J.session_id))).map(({rank:J,...oe})=>({...oe,matched_via:"semantic"})),V=[...G,...Q].slice(0,I);return i.json({query:p,hits:V,tags:E,mode:"semantic",truncated:w})}),t.get("/api/sessions/:id/context",je,i=>{let l=f(),p=i.req.param("id"),g=i.req.query("mode")==="full"?"full":"condensed",_=i.req.query("subagents")==="1",E=i.req.query("prelude")??null,y=l.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
2106
+ WHERE sessions_fts MATCH @fts${O}
2107
+ `,ie={fts:N,limit:C};g&&(K+=" AND (p.name LIKE @proj ESCAPE '\\' OR p.decoded_path LIKE @proj ESCAPE '\\')",ie.proj=`%${Gn(g)}%`),E.forEach((nt,$e)=>{K+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${$e})`,ie[`tag_${$e}`]=nt}),K+=" ORDER BY rank LIMIT @limit",X=l.prepare(K).all(ie)}catch(K){console.error("[search.semantic] failed:",K)}if(pe().loaded)try{let K=await Eg(m,C),ie=Y.map(ue=>({id:String(ue.session_id),data:ue,lane:"bm25"})),nt=X.map(ue=>({id:String(ue.session_id),data:ue,lane:"summary"})),$e=K.map(ue=>({id:ue.sessionId,data:{session_id:ue.sessionId,snippet:ue.text,matched_via:"vector"},lane:"vector"})),zg=Sg([ie,nt,$e]).slice(0,C).map(ue=>({...ue.data,session_id:ue.id,rrf_score:ue.score,lanes:ue.lanes,matched_via:ue.lanes.length>1?"fused":ue.lanes[0]}));return i.json({query:m,hits:zg,tags:E,mode:"semantic",fusion:"rrf",truncated:w})}catch(K){console.error("[search.vector] failed, falling back:",K)}let z=new Set(Y.map(K=>String(K.session_id))),te=X.filter(K=>!z.has(String(K.session_id))).map(({rank:K,...ie})=>({...ie,matched_via:"semantic"})),Z=[...Y,...te].slice(0,C);return i.json({query:m,hits:Z,tags:E,mode:"semantic",truncated:w})}),t.get("/api/sessions/:id/context",Fe,i=>{let l=h(),m=i.req.param("id"),g=i.req.query("mode")==="full"?"full":"condensed",_=i.req.query("subagents")==="1",E=i.req.query("prelude")??null,T=l.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
1998
2108
  s.started_at, s.ended_at, s.message_count, s.git_branch
1999
2109
  FROM sessions s JOIN projects p ON p.id = s.project_id
2000
- WHERE s.id = ?`).get(p);if(!y)return i.json({error:"not found"},404);let w=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2110
+ WHERE s.id = ?`).get(m);if(!T)return i.json({error:"not found"},404);let w=l.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
2001
2111
  FROM messages
2002
2112
  WHERE session_id = ?
2003
- ORDER BY COALESCE(timestamp, ''), rowid`).all(p),N=Hl(y,w,{mode:g,includeSidechain:_,prelude:E});return i.text(N)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:pl(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),p=Be(l);if(!p)return i.json({error:"not found"},404);let g=ml(l,!0);return i.json({collection:p,members:g})}),t.post("/api/collections",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=sn({name:l.name,description:l.description??null,icon:l.icon??null,color:l.color??null,parent_id:l.parent_id??null,sort_key:l.sort_key});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/collections/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let g=_l(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.post("/api/collections/:id/archive",i=>{let l=i.req.param("id");try{let p=fl(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/restore",i=>{let l=i.req.param("id");try{let p=hl(l);return i.json(p)}catch(p){return i.json({error:p.message},404)}}),t.post("/api/collections/:id/members",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p||typeof p.session_id!="string")return i.json({error:"session_id required"},400);try{let g=rn(l,p.session_id,p.note??null);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/collections/:id/members/:sid",i=>{let l=i.req.param("id"),p=i.req.param("sid");try{let g=El(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/collections",i=>{let l=i.req.param("id");return i.json({collections:gl(l)})});let O=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:wl()})),t.post("/api/auto-collections/rules",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string"||typeof l.pattern!="string"||!l.type||!O.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let p=fo({name:l.name,type:l.type,pattern:l.pattern,collection_id:l.collection_id,parent_collection_id:l.parent_collection_id,priority:l.priority,enabled:l.enabled});return i.json(p,201)}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/auto-collections/rules/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(!p)return i.json({error:"body required"},400);try{let g=Rl(l,p);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/auto-collections/rules/:id",i=>{let l=i.req.param("id");try{let p=kl(l);return i.json(p)}catch(p){return i.json({error:p.message},400)}}),t.get("/api/auto-collections/suggestions",i=>{let l=i.req.query("dismissed")==="1";return i.json({suggestions:bs({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let p=xl(l);return i.json({rule:p})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/suggestions/:id/dismiss",i=>{let l=i.req.param("id");try{return Al(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=Ss();return i.json({suggestions:l})}),t.get("/api/auto-collections/suggestions/:id/preview",i=>{let l=i.req.param("id"),p=Math.max(1,Math.min(20,Number(i.req.query("limit"))||3)),_=bs({includeDismissed:!1}).find(y=>y.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=Tl(_.type,_.pattern,p);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(Nl());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:vo({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),p=ie(l);if(!p)return i.json({error:"thread not found"},404);let g=p.edges.map(_=>({..._,alias_source:_.alias==null?null:M.isSessionAutoLinked(_.session_id)?"auto":"manual"}));return i.json({thread:{...p,edges:g}})}),t.post("/api/threads",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name)return i.json({error:"name required"},400);try{let p=ks({name:l.name,summary:l.summary??null,originSessionId:l.originSessionId});return i.json({thread:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/threads/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{p.name&&ou(l,p.name),p.close&&iu(l),p.reopen&&au(l),p.archive&&cu(l),"folder_id"in p&&Nu(l,p.folder_id??null);let g=ie(l);return g?i.json({thread:g}):i.json({error:"thread not found"},404)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/thread-folders",i=>i.json({folders:Do()})),t.post("/api/thread-folders",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name||typeof l.name!="string")return i.json({error:"name required"},400);try{let p=yu({name:l.name,parentFolderId:l.parent_folder_id??null,projectScope:l.project_scope??null});return i.json({folder:p})}catch(p){return i.json({error:p.message},400)}}),t.patch("/api/thread-folders/:id",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));try{let g;return p.name&&(g=Ru(l,p.name)),"parent_folder_id"in p&&(g=ku(l,p.parent_folder_id??null)),g?i.json({folder:g}):i.json({error:"no patch fields"},400)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/thread-folders/:id",i=>{let l=i.req.param("id");try{return xu(l),i.json({ok:!0})}catch(p){return i.json({error:p.message},400)}}),t.post("/api/thread-folders/reorder",async i=>{let l=await i.req.json().catch(()=>({})),p=l.ordered_ids;if(!Array.isArray(p))return i.json({error:"ordered_ids must be an array"},400);try{return Au(l.parent_folder_id??null,l.project_scope??null,p),i.json({ok:!0})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/sessions",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionId)return i.json({error:"sessionId required"},400);try{let g=As({threadId:l,sessionId:p.sessionId,parentSessionId:p.parentSessionId??null,role:p.role});return i.json({edge:g})}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/threads/:id/sessions/:sessionId",i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),g=ru(l,p);return i.json(g)}),t.patch("/api/threads/:id/sessions/:sessionId",async i=>{let l=i.req.param("id"),p=i.req.param("sessionId"),g=await i.req.json().catch(()=>({}));try{let _=an(l,p,g.parentSessionId??null);return i.json({edge:_})}catch(_){return i.json({error:_.message},400)}}),t.post("/api/threads/:id/merge",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sourceId)return i.json({error:"sourceId required"},400);try{let g=lu(p.sourceId,l);return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/split",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({}));if(!p.sessionIds?.length||!p.newThreadName)return i.json({error:"sessionIds and newThreadName required"},400);try{let g=uu({threadId:l,sessionIds:p.sessionIds,newThreadName:p.newThreadName});return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/threads",i=>{let l=i.req.param("id");return i.json({threads:su(l)})});let L=D.object({enabled:D.boolean(),band_lo:D.number().min(0).max(1).optional(),band_hi:D.number().min(0).max(1).optional(),model:D.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),x=D.object({project:D.string().min(1),threshold:D.number().min(0).max(1).optional(),model:D.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:L});t.post("/api/threads/scan/preflight",async i=>{let l=Ce(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=x.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=zd({project:g.data.project,threshold:g.data.threshold,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json(_)});let F=D.object({project:D.string().min(1),threshold:D.number().min(0).max(1).optional(),llm_names:D.boolean().optional(),model:D.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:L});t.post("/api/threads/scan/apply",async i=>{let l=Ce(i);if(l)return l;let p=await i.req.json().catch(()=>null),g=F.safeParse(p);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Vd({project:g.data.project,threshold:g.data.threshold,llm_names:g.data.llm_names,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json({jobId:_.jobId,reused:_.reused},_.reused?409:200)}),t.get("/api/threads/scan/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!oi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ye(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of Zd(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=oi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/threads/scan/jobs/:jobId",i=>{let l=Ce(i);return l||(Qd(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let A=D.object({project_id:D.number().int().positive(),mode:D.enum(["preflight","apply"]),window_hours:D.number().min(.5).max(168).optional(),score_threshold:D.number().min(0).max(1).optional(),use_live_pids:D.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),p=A.safeParse(l);if(!p.success)return i.json({error:"invalid request body",details:p.error.format()},400);try{let g=await bu(p.data.project_id,{windowHours:p.data.window_hours,scoreThreshold:p.data.score_threshold,useLivePids:p.data.use_live_pids});if(p.data.mode==="preflight")return i.json({plan:g});let _=Su(g);return i.json({plan:g,result:_})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/threads/:id/titles/preflight",i=>{let l=i.req.param("id"),p=ie(l);if(!p)return i.json({error:"thread not found"},404);let g=f(),_=0;for(let E of p.edges)g.prepare("SELECT auto_title_source FROM sessions WHERE id = ?").get(E.session_id)?.auto_title_source==="agent"&&(_+=1);return i.json({total:p.edges.length,alreadyTitled:_,untitled:p.edges.length-_})}),t.post("/api/threads/:id/titles/generate",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>({})),g=ie(l);if(!g)return i.json({error:"thread not found"},404);if(g.edges.length===0)return i.json({error:"thread has no sessions"},400);let _=Fe(),E=p.model??_.model,y=Bu({threadId:l,force:p.force??!1,model:E});return i.json({jobId:y})}),t.get("/api/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Fo(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return Ye(i,async _=>{let E=!1,y=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of Hu(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(y)}})}),t.get("/api/jobs/:jobId",i=>{let l=Fo(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>Wu(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404)),t.post("/api/terminal/opened",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=M.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance",count:M.size()});let g=M.upsert({shell_pid:l.shell_pid,tab_name:l.tab_name,cwd:l.cwd??null,opened_at:l.opened_at??new Date().toISOString()});return i.json({ok:!0,ownership:p,count:M.size(),entry:g})}),t.post("/api/terminal/renamed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let p=M.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(p==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance"});let g=M.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let _=lm(l.shell_pid,l.tab_name);return i.json({ok:!0,ownership:p,entry:g,propagated:_})}),t.post("/api/terminal/closed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number")return i.json({error:"shell_pid required"},400);let p=M.remove(l.shell_pid);return i.json({ok:!0,removed:p,count:M.size()})}),t.post("/api/terminal/claude-started",async i=>{let l=await i.req.json().catch(()=>null);return!l||typeof l.shell_pid!="number"?i.json({error:"shell_pid required"},400):(M.pushPending({shell_pid:l.shell_pid,tab_name:typeof l.tab_name=="string"?l.tab_name:"",cwd:typeof l.cwd=="string"?l.cwd:null,started_at:typeof l.started_at=="string"?l.started_at:new Date().toISOString()}),i.json({ok:!0,pending:M.pendingSize()}))}),t.post("/api/terminal/output",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.text!="string")return i.json({error:"shell_pid and text required"},400);let p=l.text.length>8192?l.text.slice(-8192):l.text,g=typeof l.captured_at=="string"?l.captured_at:new Date().toISOString();return M.setOutputTail(l.shell_pid,p,g),i.json({ok:!0})}),t.post("/api/terminal/sync",async i=>{let l=await i.req.json().catch(()=>null);if(!l||!Array.isArray(l.terminals))return i.json({error:"terminals array required"},400);let p=new Map;for(let v of M.all())p.set(v.shell_pid,v.tab_name);let g=l.terminals.filter(v=>!!v&&typeof v.shell_pid=="number"&&typeof v.tab_name=="string").map(v=>({shell_pid:v.shell_pid,tab_name:v.tab_name,cwd:v.cwd??null,opened_at:v.opened_at??new Date().toISOString()})),_=l.extension_instance_id??null,E=[],y=g.filter(v=>{let P=M.claimPidOwnership(v.shell_pid,_);return E.push({shell_pid:v.shell_pid,ownership:P}),P!=="rejected"}),w=M.sync(y),N=0;for(let v of y){let P=p.get(v.shell_pid),q=M.get(v.shell_pid)?.tab_name??v.tab_name,G=!!q&&!de(q)&&!le(q)?q:v.tab_name;P!==void 0&&P!==G&&(N+=lm(v.shell_pid,G))}let k=E.filter(v=>v.ownership==="rejected").length;k>0&&console.log(`[terminal/sync] dropped ${k} tab_name update(s), pid(s) owned by a different extension instance`);let C=await Bw(),I={resolved:0,expired:0};try{I=Pc()}catch{}let j={rebound:0,ghosts:0,ambiguous:0};try{j=Fc()}catch{}return Qw(),i.json({ok:!0,count:M.size(),diff:w,propagated:N,live_sweep:C,deferred_resolved:I,rebound:j})}),t.get("/api/terminal/registry",i=>i.json({terminals:M.all(),count:M.size()})),t.get("/api/terminal/sessions/:shellPid",i=>{let l=i.req.param("shellPid"),p=Number(l);if(!Number.isInteger(p)||p<=0)return i.json({error:"shellPid must be a positive integer"},400);let g=M.sessionsFor(p);return i.json({shell_pid:p,sessions:g})}),t.get("/api/sessions/:id/linked-terminal",i=>{let l=i.req.param("id"),p=M.all().find(_=>M.sessionsFor(_.shell_pid).includes(l)),g=[];if(!p){let _=f().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l);if(_?.cwd&&_.started_at){let E=Date.parse(_.started_at);if(Number.isFinite(E)){let y=_.cwd.replace(/\/+$/,""),w=300*1e3;for(let N of M.all()){if(!N.cwd||N.cwd.replace(/\/+$/,"")!==y||de(N.tab_name))continue;let k=Date.parse(N.opened_at),C=Date.parse(N.last_seen_at);!Number.isFinite(k)||!Number.isFinite(C)||k>E||C+w<E||g.push({shell_pid:N.shell_pid,tab_name:N.tab_name,cwd:N.cwd,opened_at:N.opened_at,last_seen_at:N.last_seen_at,reason:"time-overlap"})}g.sort((N,k)=>Date.parse(k.last_seen_at)-Date.parse(N.last_seen_at))}}}return p?i.json({linked:{shell_pid:p.shell_pid,tab_name:p.tab_name,cwd:p.cwd},suggested:[]}):i.json({linked:null,suggested:g})}),t.post("/api/sessions/:id/auto-relink",async i=>{let l=i.req.param("id");if(Te(l))return i.json({applied:!1,reason:"has-alias"});if(M.all().some(y=>M.sessionsFor(y.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=f().prepare("SELECT cwd, git_branch, started_at FROM sessions WHERE id = ?").get(l);if(!g?.cwd)return i.json({applied:!1,reason:"no-cwd"});let _=g.cwd.replace(/\/+$/,""),E=M.all().filter(y=>y.cwd&&y.cwd.replace(/\/+$/,"")===_&&!de(y.tab_name));if(E.length===1){let y=E[0],w=on({sessionStartedAt:g.started_at??null,terminalOpenedAt:y.opened_at??null});if(!w.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:w.reason});let N=M.getOrigin(l),k=St({tabName:y.tab_name,origin:N??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return k?(he(l,k),M.linkSession(l,y.shell_pid),i.json({applied:!0,alias:k,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,method:"cwd-singleton"})):i.json({applied:!1,reason:"no-usable-name"})}if(E.length>1){let y=await Dc(l);if(y){let N=M.get(y.shell_pid),k=on({sessionStartedAt:g.started_at??null,terminalOpenedAt:N?.opened_at??null});if(!k.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:k.reason});let C=M.getOrigin(l),I=St({tabName:y.tab_name,origin:C??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(I)return he(l,I),M.linkSession(l,y.shell_pid),i.json({applied:!0,alias:I,linked_pid:y.shell_pid,linked_tab_name:y.tab_name,matched_fingerprints:y.matched_fingerprints,method:"content-match"})}let w=6e4;if(g.started_at){let N=Date.parse(g.started_at);if(Number.isFinite(N)){let k=E.filter(I=>on({sessionStartedAt:g.started_at,terminalOpenedAt:I.opened_at??null}).allowed).map(I=>({t:I,gap:N-Date.parse(I.opened_at??"")})).filter(I=>Number.isFinite(I.gap)&&I.gap>=0&&I.gap<=w);if(k.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:k.length});let C=k[0];if(C){let I=M.getOrigin(l),j=St({tabName:C.t.tab_name,origin:I??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(j)return he(l,j),M.linkSession(l,C.t.shell_pid),i.json({applied:!0,alias:j,linked_pid:C.t.shell_pid,linked_tab_name:C.t.tab_name,method:"closest-before-temporal",gap_ms:C.gap})}}}return i.json({applied:!1,reason:"ambiguous",candidate_count:E.length})}return i.json({applied:!1,reason:"no-candidates"})}),t.post("/api/sessions/:id/relink",async i=>{let l=i.req.param("id"),p=await i.req.json().catch(()=>null);if(p?.clear)return M.unlinkSession(l),es(l),i.json({ok:!0,alias:null,linked_pid:null});if(!p||typeof p.shell_pid!="number")return i.json({error:"shell_pid required"},400);let g=M.get(p.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let _=M.getOrigin(l),E=f().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),y=null,w=g.tab_name?.trim()??"";if(w&&!de(w)&&!le(w))y=w;else if(w&&le(w)){let N=bt(w);N&&!de(N)&&(y=N)}return y?(M.unlinkSession(l),he(l,y),i.json({ok:!0,alias:y,linked_pid:p.shell_pid,linked_tab_name:g.tab_name})):i.json({error:"terminal has no usable name, name the tab in your editor first, then retry the relink"},422)}),t.post("/api/sessions/:id/recorrelate",async i=>{let l=i.req.param("id"),p=f().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!p?.file_path)return i.json({error:"session not found"},404);M.unlinkSession(l),es(l),await ss(p.file_path);let g=Te(l);return i.json({ok:!0,alias:g,linked_pid:M.all().find(_=>M.sessionsFor(_.shell_pid).includes(l))?.shell_pid??null})}),t.get("/api/paste-expand",async i=>{let l=i.req.query("session"),p=i.req.query("message"),g=i.req.query("path");if(!l||!p||!g)return i.json({error:"session, message and path are required"},400);let _=f(),E=_.prepare("SELECT rowid, content_text FROM messages WHERE uuid = ? AND session_id = ?").get(p,l);if(!E)return i.json({error:"message not found in session"},404);let y=g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(!new RegExp(`\\[Pasted text #\\d+ \\+\\d+ lines\\]\\s*${y}`).test(E.content_text??""))return i.json({error:"path not referenced by this message"},403);let N=_.prepare(`SELECT content_text FROM messages
2113
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(m),x=xd(T,w,{mode:g,includeSidechain:_,prelude:E});return i.text(x)}),t.get("/api/collections",i=>{let l=i.req.query("archived")==="1";return i.json({collections:iu(l)})}),t.get("/api/collections/:id",i=>{let l=i.req.param("id"),m=Ye(l);if(!m)return i.json({error:"not found"},404);let g=au(l,!0);return i.json({collection:m,members:g})}),t.post("/api/collections",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string")return i.json({error:"name required"},400);try{let m=yn({name:l.name,description:l.description??null,icon:l.icon??null,color:l.color??null,parent_id:l.parent_id??null,sort_key:l.sort_key});return i.json(m,201)}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/collections/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);try{let g=lu(l,m);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.post("/api/collections/:id/archive",i=>{let l=i.req.param("id");try{let m=uu(l);return i.json(m)}catch(m){return i.json({error:m.message},404)}}),t.post("/api/collections/:id/restore",i=>{let l=i.req.param("id");try{let m=du(l);return i.json(m)}catch(m){return i.json({error:m.message},404)}}),t.post("/api/collections/:id/members",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m||typeof m.session_id!="string")return i.json({error:"session_id required"},400);try{let g=Tn(l,m.session_id,m.note??null);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/collections/:id/members/:sid",i=>{let l=i.req.param("id"),m=i.req.param("sid");try{let g=pu(l,m);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/collections",i=>{let l=i.req.param("id");return i.json({collections:cu(l)})});let k=["cwd-prefix","project-id","tag","plan-file","git-branch-prefix"];t.get("/api/auto-collections/rules",i=>i.json({rules:hu()})),t.post("/api/auto-collections/rules",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.name!="string"||typeof l.pattern!="string"||!l.type||!k.includes(l.type))return i.json({error:"name, type, pattern required (type must be a known matcher)"},400);try{let m=Zo({name:l.name,type:l.type,pattern:l.pattern,collection_id:l.collection_id,parent_collection_id:l.parent_collection_id,priority:l.priority,enabled:l.enabled});return i.json(m,201)}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/auto-collections/rules/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(!m)return i.json({error:"body required"},400);try{let g=Eu(l,m);return i.json(g)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/auto-collections/rules/:id",i=>{let l=i.req.param("id");try{let m=bu(l);return i.json(m)}catch(m){return i.json({error:m.message},400)}}),t.get("/api/auto-collections/suggestions",i=>{let l=i.req.query("dismissed")==="1";return i.json({suggestions:qs({includeDismissed:l})})}),t.post("/api/auto-collections/suggestions/:id/accept",i=>{let l=i.req.param("id");try{let m=yu(l);return i.json({rule:m})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/auto-collections/suggestions/:id/dismiss",i=>{let l=i.req.param("id");try{return Su(l),i.json({ok:!0})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/auto-collections/detect",i=>{let l=Xs();return i.json({suggestions:l})}),t.get("/api/auto-collections/suggestions/:id/preview",i=>{let l=i.req.param("id"),m=Math.max(1,Math.min(20,Number(i.req.query("limit"))||3)),_=qs({includeDismissed:!1}).find(T=>T.id===l);if(!_)return i.json({error:"suggestion not found"},404);let E=fu(_.type,_.pattern,m);return i.json({sessions:E})}),t.get("/api/auto-collections/parents",i=>{let l=Array.from(Tu());return i.json({auto_collection_ids:l})}),t.get("/api/threads",i=>{let l=i.req.query("archived")==="1";return i.json({threads:Oi({includeArchived:l})})}),t.get("/api/threads/:id",i=>{let l=i.req.param("id"),m=ce(l);if(!m)return i.json({error:"thread not found"},404);let g=m.edges.map(_=>({..._,alias_source:_.alias==null?null:j.isSessionAutoLinked(_.session_id)?"auto":"manual"}));return i.json({thread:{...m,edges:g}})}),t.post("/api/threads",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name)return i.json({error:"name required"},400);try{let m=rr({name:l.name,summary:l.summary??null,originSessionId:l.originSessionId});return i.json({thread:m})}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/threads/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));try{m.name&&qd(l,m.name),m.close&&Xd(l),m.reopen&&Jd(l),m.archive&&Gd(l),"folder_id"in m&&mp(l,m.folder_id??null);let g=ce(l);return g?i.json({thread:g}):i.json({error:"thread not found"},404)}catch(g){return i.json({error:g.message},400)}}),t.get("/api/thread-folders",i=>i.json({folders:Ii()})),t.post("/api/thread-folders",async i=>{let l=await i.req.json().catch(()=>({}));if(!l.name||typeof l.name!="string")return i.json({error:"name required"},400);try{let m=ap({name:l.name,parentFolderId:l.parent_folder_id??null,projectScope:l.project_scope??null});return i.json({folder:m})}catch(m){return i.json({error:m.message},400)}}),t.patch("/api/thread-folders/:id",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));try{let g;return m.name&&(g=lp(l,m.name)),"parent_folder_id"in m&&(g=up(l,m.parent_folder_id??null)),g?i.json({folder:g}):i.json({error:"no patch fields"},400)}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/thread-folders/:id",i=>{let l=i.req.param("id");try{return pp(l),i.json({ok:!0})}catch(m){return i.json({error:m.message},400)}}),t.post("/api/thread-folders/reorder",async i=>{let l=await i.req.json().catch(()=>({})),m=l.ordered_ids;if(!Array.isArray(m))return i.json({error:"ordered_ids must be an array"},400);try{return dp(l.parent_folder_id??null,l.project_scope??null,m),i.json({ok:!0})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/sessions",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));if(!m.sessionId)return i.json({error:"sessionId required"},400);try{let g=or({threadId:l,sessionId:m.sessionId,parentSessionId:m.parentSessionId??null,role:m.role});return i.json({edge:g})}catch(g){return i.json({error:g.message},400)}}),t.delete("/api/threads/:id/sessions/:sessionId",i=>{let l=i.req.param("id"),m=i.req.param("sessionId"),g=Wd(l,m);return i.json(g)}),t.patch("/api/threads/:id/sessions/:sessionId",async i=>{let l=i.req.param("id"),m=i.req.param("sessionId"),g=await i.req.json().catch(()=>({}));try{let _=Ln(l,m,g.parentSessionId??null);return i.json({edge:_})}catch(_){return i.json({error:_.message},400)}}),t.post("/api/threads/:id/merge",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));if(!m.sourceId)return i.json({error:"sourceId required"},400);try{let g=Yd(m.sourceId,l);return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.post("/api/threads/:id/split",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({}));if(!m.sessionIds?.length||!m.newThreadName)return i.json({error:"sessionIds and newThreadName required"},400);try{let g=Kd({threadId:l,sessionIds:m.sessionIds,newThreadName:m.newThreadName});return i.json({thread:g})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/sessions/:id/threads",i=>{let l=i.req.param("id");return i.json({threads:Bd(l)})});let M=$.object({enabled:$.boolean(),band_lo:$.number().min(0).max(1).optional(),band_hi:$.number().min(0).max(1).optional(),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional()}).optional(),P=$.object({project:$.string().min(1),threshold:$.number().min(0).max(1).optional(),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:M});t.post("/api/threads/scan/preflight",async i=>{let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=P.safeParse(m);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Mm({project:g.data.project,threshold:g.data.threshold,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json(_)});let G=$.object({project:$.string().min(1),threshold:$.number().min(0).max(1).optional(),llm_names:$.boolean().optional(),model:$.string().regex(/^[a-zA-Z0-9._-]{1,100}$/).optional(),llm_rescore:M});t.post("/api/threads/scan/apply",async i=>{let l=De(i);if(l)return l;let m=await i.req.json().catch(()=>null),g=G.safeParse(m);if(!g.success)return i.json({error:"invalid request body",details:g.error.format()},400);let _=Dm({project:g.data.project,threshold:g.data.threshold,llm_names:g.data.llm_names,model:g.data.model,llm_rescore:g.data.llm_rescore});return"error"in _?i.json({error:_.error},400):i.json({jobId:_.jobId,reused:_.reused},_.reused?409:200)}),t.get("/api/threads/scan/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!na(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return tt(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of Pm(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/threads/scan/jobs/:jobId",i=>{let l=na(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/threads/scan/jobs/:jobId",i=>{let l=De(i);return l||(Fm(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404))});let D=$.object({project_id:$.number().int().positive(),mode:$.enum(["preflight","apply"]),window_hours:$.number().min(.5).max(168).optional(),score_threshold:$.number().min(0).max(1).optional(),use_live_pids:$.boolean().optional()});t.post("/api/threads/sync-active",async i=>{let l=await i.req.json().catch(()=>null),m=D.safeParse(l);if(!m.success)return i.json({error:"invalid request body",details:m.error.format()},400);try{let g=await rp(m.data.project_id,{windowHours:m.data.window_hours,scoreThreshold:m.data.score_threshold,useLivePids:m.data.use_live_pids});if(m.data.mode==="preflight")return i.json({plan:g});let _=op(g);return i.json({plan:g,result:_})}catch(g){return i.json({error:g.message},400)}}),t.get("/api/threads/:id/titles/preflight",i=>{let l=i.req.param("id"),m=ce(l);if(!m)return i.json({error:"thread not found"},404);let g=h(),_=0;for(let E of m.edges)g.prepare("SELECT auto_title_source FROM sessions WHERE id = ?").get(E.session_id)?.auto_title_source==="agent"&&(_+=1);return i.json({total:m.edges.length,alreadyTitled:_,untitled:m.edges.length-_})}),t.post("/api/threads/:id/titles/generate",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>({})),g=ce(l);if(!g)return i.json({error:"thread not found"},404);if(g.edges.length===0)return i.json({error:"thread has no sessions"},400);let _=Be(),E=m.model??_.model,T=Ap({threadId:l,force:m.force??!1,model:E});return i.json({jobId:T})}),t.get("/api/jobs/:jobId/stream",i=>{let l=i.req.param("jobId");if(!Mi(l))return i.json({error:"job not found"},404);let g=Number(i.req.header("Last-Event-ID")??0);return tt(i,async _=>{let E=!1,T=setInterval(()=>{E||_.writeSSE({event:"heartbeat",data:""}).catch(()=>{E=!0})},15e3);try{for await(let w of xp(l,g))if(E||(await _.writeSSE({id:String(w.id),event:w.kind,data:JSON.stringify(w.data)}),w.kind==="done"))break}finally{E=!0,clearInterval(T)}})}),t.get("/api/jobs/:jobId",i=>{let l=Mi(i.req.param("jobId"));return l?i.json(l):i.json({error:"job not found"},404)}),t.delete("/api/jobs/:jobId",i=>Np(i.req.param("jobId"))?i.json({ok:!0}):i.json({error:"job not found or already done"},404)),t.post("/api/terminal/opened",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let m=j.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(m==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance",count:j.size()});let g=j.upsert({shell_pid:l.shell_pid,tab_name:l.tab_name,cwd:l.cwd??null,opened_at:l.opened_at??new Date().toISOString()});return i.json({ok:!0,ownership:m,count:j.size(),entry:g})}),t.post("/api/terminal/renamed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.tab_name!="string")return i.json({error:"shell_pid and tab_name required"},400);let m=j.claimPidOwnership(l.shell_pid,l.extension_instance_id??null);if(m==="rejected")return i.json({ok:!0,rejected:!0,reason:"pid-owned-by-other-extension-instance"});let g=j.rename(l.shell_pid,l.tab_name);if(!g)return i.json({error:"unknown shell_pid"},404);let _=Mg(l.shell_pid,l.tab_name);return i.json({ok:!0,ownership:m,entry:g,propagated:_})}),t.post("/api/terminal/closed",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number")return i.json({error:"shell_pid required"},400);let m=j.remove(l.shell_pid);return i.json({ok:!0,removed:m,count:j.size()})}),t.post("/api/terminal/claude-started",async i=>{let l=await i.req.json().catch(()=>null);return!l||typeof l.shell_pid!="number"?i.json({error:"shell_pid required"},400):(j.pushPending({shell_pid:l.shell_pid,tab_name:typeof l.tab_name=="string"?l.tab_name:"",cwd:typeof l.cwd=="string"?l.cwd:null,started_at:typeof l.started_at=="string"?l.started_at:new Date().toISOString()}),i.json({ok:!0,pending:j.pendingSize()}))}),t.post("/api/terminal/output",async i=>{let l=await i.req.json().catch(()=>null);if(!l||typeof l.shell_pid!="number"||typeof l.text!="string")return i.json({error:"shell_pid and text required"},400);let m=l.text.length>8192?l.text.slice(-8192):l.text,g=typeof l.captured_at=="string"?l.captured_at:new Date().toISOString();return j.setOutputTail(l.shell_pid,m,g),i.json({ok:!0})}),t.post("/api/terminal/sync",async i=>{let l=await i.req.json().catch(()=>null);if(!l||!Array.isArray(l.terminals))return i.json({error:"terminals array required"},400);let m=new Map;for(let O of j.all())m.set(O.shell_pid,O.tab_name);let g=l.terminals.filter(O=>!!O&&typeof O.shell_pid=="number"&&typeof O.tab_name=="string").map(O=>({shell_pid:O.shell_pid,tab_name:O.tab_name,cwd:O.cwd??null,opened_at:O.opened_at??new Date().toISOString()})),_=l.extension_instance_id??null,E=[],T=g.filter(O=>{let H=j.claimPidOwnership(O.shell_pid,_);return E.push({shell_pid:O.shell_pid,ownership:H}),H!=="rejected"}),w=j.sync(T),x=0;for(let O of T){let H=m.get(O.shell_pid),J=j.get(O.shell_pid)?.tab_name??O.tab_name,Y=!!J&&!de(J)&&!le(J)?J:O.tab_name;H!==void 0&&H!==Y&&(x+=Mg(O.shell_pid,Y))}let A=E.filter(O=>O.ownership==="rejected").length;A>0&&console.log(`[terminal/sync] dropped ${A} tab_name update(s), pid(s) owned by a different extension instance`);let N=await Mk(),C={resolved:0,expired:0};try{C=vl()}catch{}let I={rebound:0,ghosts:0,ambiguous:0};try{I=Ll()}catch{}return Jk(),i.json({ok:!0,count:j.size(),diff:w,propagated:x,live_sweep:N,deferred_resolved:C,rebound:I})}),t.get("/api/terminal/registry",i=>i.json({terminals:j.all(),count:j.size()})),t.get("/api/terminal/sessions/:shellPid",i=>{let l=i.req.param("shellPid"),m=Number(l);if(!Number.isInteger(m)||m<=0)return i.json({error:"shellPid must be a positive integer"},400);let g=j.sessionsFor(m);return i.json({shell_pid:m,sessions:g})}),t.get("/api/sessions/:id/linked-terminal",i=>{let l=i.req.param("id"),m=j.all().find(_=>j.sessionsFor(_.shell_pid).includes(l)),g=[];if(!m){let _=h().prepare("SELECT cwd, started_at FROM sessions WHERE id = ?").get(l);if(_?.cwd&&_.started_at){let E=Date.parse(_.started_at);if(Number.isFinite(E)){let T=_.cwd.replace(/\/+$/,""),w=300*1e3;for(let x of j.all()){if(!x.cwd||x.cwd.replace(/\/+$/,"")!==T||de(x.tab_name))continue;let A=Date.parse(x.opened_at),N=Date.parse(x.last_seen_at);!Number.isFinite(A)||!Number.isFinite(N)||A>E||N+w<E||g.push({shell_pid:x.shell_pid,tab_name:x.tab_name,cwd:x.cwd,opened_at:x.opened_at,last_seen_at:x.last_seen_at,reason:"time-overlap"})}g.sort((x,A)=>Date.parse(A.last_seen_at)-Date.parse(x.last_seen_at))}}}return m?i.json({linked:{shell_pid:m.shell_pid,tab_name:m.tab_name,cwd:m.cwd},suggested:[]}):i.json({linked:null,suggested:g})}),t.post("/api/sessions/:id/auto-relink",async i=>{let l=i.req.param("id");if(ye(l))return i.json({applied:!1,reason:"has-alias"});if(j.all().some(T=>j.sessionsFor(T.shell_pid).includes(l)))return i.json({applied:!1,reason:"already-linked"});let g=h().prepare("SELECT cwd, git_branch, started_at FROM sessions WHERE id = ?").get(l);if(!g?.cwd)return i.json({applied:!1,reason:"no-cwd"});let _=g.cwd.replace(/\/+$/,""),E=j.all().filter(T=>T.cwd&&T.cwd.replace(/\/+$/,"")===_&&!de(T.tab_name));if(E.length===1){let T=E[0],w=Cn({sessionStartedAt:g.started_at??null,terminalOpenedAt:T.opened_at??null});if(!w.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:w.reason});let x=j.getOrigin(l),A=Lt({tabName:T.tab_name,origin:x??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});return A?(he(l,A),j.linkSession(l,T.shell_pid),i.json({applied:!0,alias:A,linked_pid:T.shell_pid,linked_tab_name:T.tab_name,method:"cwd-singleton"})):i.json({applied:!1,reason:"no-usable-name"})}if(E.length>1){let T=await Cl(l);if(T){let x=j.get(T.shell_pid),A=Cn({sessionStartedAt:g.started_at??null,terminalOpenedAt:x?.opened_at??null});if(!A.allowed)return i.json({applied:!1,reason:"temporal-guard",guard_reason:A.reason});let N=j.getOrigin(l),C=Lt({tabName:T.tab_name,origin:N??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(C)return he(l,C),j.linkSession(l,T.shell_pid),i.json({applied:!0,alias:C,linked_pid:T.shell_pid,linked_tab_name:T.tab_name,matched_fingerprints:T.matched_fingerprints,method:"content-match"})}let w=6e4;if(g.started_at){let x=Date.parse(g.started_at);if(Number.isFinite(x)){let A=E.filter(C=>Cn({sessionStartedAt:g.started_at,terminalOpenedAt:C.opened_at??null}).allowed).map(C=>({t:C,gap:x-Date.parse(C.opened_at??"")})).filter(C=>Number.isFinite(C.gap)&&C.gap>=0&&C.gap<=w);if(A.length>=2)return i.json({applied:!1,reason:"ambiguous-temporal",candidate_count:A.length});let N=A[0];if(N){let C=j.getOrigin(l),I=Lt({tabName:N.t.tab_name,origin:C??null,cwd:g.cwd??null,gitBranch:g.git_branch??null});if(I)return he(l,I),j.linkSession(l,N.t.shell_pid),i.json({applied:!0,alias:I,linked_pid:N.t.shell_pid,linked_tab_name:N.t.tab_name,method:"closest-before-temporal",gap_ms:N.gap})}}}return i.json({applied:!1,reason:"ambiguous",candidate_count:E.length})}return i.json({applied:!1,reason:"no-candidates"})}),t.post("/api/sessions/:id/relink",async i=>{let l=i.req.param("id"),m=await i.req.json().catch(()=>null);if(m?.clear)return j.unlinkSession(l),ks(l),i.json({ok:!0,alias:null,linked_pid:null});if(!m||typeof m.shell_pid!="number")return i.json({error:"shell_pid required"},400);let g=j.get(m.shell_pid);if(!g)return i.json({error:"terminal not registered"},404);let _=j.getOrigin(l),E=h().prepare("SELECT cwd, git_branch FROM sessions WHERE id = ?").get(l),T=null,w=g.tab_name?.trim()??"";if(w&&!de(w)&&!le(w))T=w;else if(w&&le(w)){let x=Ct(w);x&&!de(x)&&(T=x)}return T?(j.unlinkSession(l),he(l,T),i.json({ok:!0,alias:T,linked_pid:m.shell_pid,linked_tab_name:g.tab_name})):i.json({error:"terminal has no usable name, name the tab in your editor first, then retry the relink"},422)}),t.post("/api/sessions/:id/recorrelate",async i=>{let l=i.req.param("id"),m=h().prepare("SELECT file_path FROM sessions WHERE id = ?").get(l);if(!m?.file_path)return i.json({error:"session not found"},404);j.unlinkSession(l),ks(l),await Ns(m.file_path);let g=ye(l);return i.json({ok:!0,alias:g,linked_pid:j.all().find(_=>j.sessionsFor(_.shell_pid).includes(l))?.shell_pid??null})}),t.get("/api/paste-expand",async i=>{let l=i.req.query("session"),m=i.req.query("message"),g=i.req.query("path");if(!l||!m||!g)return i.json({error:"session, message and path are required"},400);let _=h(),E=_.prepare("SELECT rowid, content_text FROM messages WHERE uuid = ? AND session_id = ?").get(m,l);if(!E)return i.json({error:"message not found in session"},404);let T=g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(!new RegExp(`\\[Pasted text #\\d+ \\+\\d+ lines\\]\\s*${T}`).test(E.content_text??""))return i.json({error:"path not referenced by this message"},403);let x=_.prepare(`SELECT content_text FROM messages
2004
2114
  WHERE session_id = ? AND rowid > ?
2005
- ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let k of N){let C=k.content_text??"";if(C.includes("**Tool result**")&&C.includes(g))return i.json({source:"tool-result",content:C});if(/^\s*1\t/.test(C)&&C.length>200)return i.json({source:"tool-result",content:C})}try{let k=await Fw(g),C=$w();if(!k.startsWith(C+"/")&&!k.startsWith(C+"\\"))return i.json({error:"path outside allowed root"},403);let I=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],v=k.slice(C.length+1).split("/")[0].split("\\")[0];if(I.includes(v))return i.json({error:"path inside sensitive directory"},403);let P=await Mw(k),q=2*1024*1024;if(P.size>q)return i.json({error:"file too large",size:P.size,max:q},413);let K=await Dw(k,"utf8");return i.json({source:"disk",content:K})}catch(k){return i.json({source:"missing",error:k.message})}}),t.get("/api/projects/:name/stats",i=>{let l=f(),p=i.req.param("name"),g=l.prepare(`SELECT
2115
+ ORDER BY rowid ASC LIMIT 10`).all(l,E.rowid);for(let A of x){let N=A.content_text??"";if(N.includes("**Tool result**")&&N.includes(g))return i.json({source:"tool-result",content:N});if(/^\s*1\t/.test(N)&&N.length>200)return i.json({source:"tool-result",content:N})}try{let A=await Ck(g),N=vk();if(!A.startsWith(N+"/")&&!A.startsWith(N+"\\"))return i.json({error:"path outside allowed root"},403);let C=[".ssh",".gnupg",".gpg",".aws",".kube",".docker",".password-store"],O=A.slice(N.length+1).split("/")[0].split("\\")[0];if(C.includes(O))return i.json({error:"path inside sensitive directory"},403);let H=await Nk(A),J=2*1024*1024;if(H.size>J)return i.json({error:"file too large",size:H.size,max:J},413);let Q=await Ok(A,"utf8");return i.json({source:"disk",content:Q})}catch(A){return i.json({source:"missing",error:A.message})}}),t.get("/api/projects/:name/stats",i=>{let l=h(),m=i.req.param("name"),g=l.prepare(`SELECT
2006
2116
  (SELECT COUNT(*) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=? AND s.message_count > 2) AS sessions,
2007
2117
  (SELECT COALESCE(SUM(s.message_count), 0) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=?) AS messages,
2008
2118
  (SELECT MIN(s.started_at) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE p.name=? AND s.started_at IS NOT NULL) AS earliest,
2009
- (SELECT MAX(COALESCE(s.ended_at, s.started_at)) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE (s.ended_at IS NOT NULL OR s.started_at IS NOT NULL) AND p.name=?) AS latest`).get(p,p,p,p),_=l.prepare(`SELECT DISTINCT s.git_branch FROM sessions s
2119
+ (SELECT MAX(COALESCE(s.ended_at, s.started_at)) FROM sessions s JOIN projects p ON p.id=s.project_id WHERE (s.ended_at IS NOT NULL OR s.started_at IS NOT NULL) AND p.name=?) AS latest`).get(m,m,m,m),_=l.prepare(`SELECT DISTINCT s.git_branch FROM sessions s
2010
2120
  JOIN projects p ON p.id = s.project_id
2011
2121
  WHERE p.name = ? AND s.git_branch IS NOT NULL
2012
2122
  ORDER BY s.git_branch
2013
- LIMIT 20`).all(p).map(E=>E.git_branch);return i.json({...g,branches:_})});function $(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function te(i){if(!e)return i;let l=`<meta name="recall-token" content="${$(e)}" />`,p=i.indexOf("</head>");return p!==-1?i.slice(0,p)+l+i.slice(p):l+i}function H(i){let l=dm();return i.html(Bl({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:um}))}function se(){try{return Oi(mm,"utf8")}catch{return null}}return Yw?(t.use("/assets/*",cm({root:Ci})),t.get("/favicon.svg",cm({root:Ci})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=se();return l===null?H(i):i.html(te(l))}),t.get("*",i=>{if(i.req.path.startsWith("/api/"))return i.notFound();i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=se();return l===null?H(i):i.html(te(l))})):t.get("/",i=>H(i)),t}function n0(){if(Gs(),!!st().heuristicEnabled){try{let{updated:e}=tl();e>0&&console.log(`[auto-title] backfilled heuristic title on ${e} sessions`)}catch(e){console.error("[auto-title] backfill failed:",e)}try{let{scanned:e,updated:t}=nl();t>0&&console.log(`[auto-title] refreshed templated heuristic title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] templated-title refresh failed:",e)}try{let{scanned:e,updated:t}=sl();t>0&&console.log(`[auto-title] refreshed recursive_meta title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] recursive_meta refresh failed:",e)}try{let{scanned:e,updated:t}=rl();t>0&&console.log(`[auto-title] canonicalized brand on ${t}/${e} session titles`)}catch(e){console.error("[auto-title] brand canonicalization failed:",e)}}}async function fm(e,t){let n=t0(t);return new Promise((s,r)=>{try{let o=Iw({fetch:n.fetch,port:e,hostname:"127.0.0.1"},()=>{s(o),setImmediate(()=>{try{n0()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}import{createServer as Em}from"node:net";function hm(e){return new Promise(t=>{let n=Em();n.once("error",()=>t(!1)),n.once("listening",()=>{n.close(()=>t(!0))}),n.listen(e,"127.0.0.1")})}async function bm(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await hm(t))return t;for(let n=0;n<50;n++){let s=49152+Math.floor(Math.random()*16383);if(!e.has(s)&&await hm(s))return s}return new Promise((n,s)=>{let r=Em();r.once("error",s),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>n(a))}else r.close(),s(new Error("could not determine a free port"))})})}ee();import{existsSync as i0,readFileSync as yI,writeFileSync as Tm,unlinkSync as a0}from"node:fs";import{join as Mi}from"node:path";ee();import{randomBytes as s0}from"node:crypto";import{writeFileSync as r0,readFileSync as fI,existsSync as hI}from"node:fs";import{join as o0}from"node:path";var ji=o0(W,"daemon.token");function Sm(){z();let e=s0(32).toString("hex");return r0(ji,e,{encoding:"utf8",mode:384}),e}var ym=Mi(W,"daemon.pid"),wm=Mi(W,"daemon.port"),AI=Mi(W,"daemon.log");function Rm(e){z(),Tm(ym,JSON.stringify(e),{encoding:"utf8",mode:384}),Tm(wm,String(e.port),{encoding:"utf8",mode:384})}function Di(){for(let e of[ym,wm,ji])if(i0(e))try{a0(e)}catch{}}Fr();B();ee();ct();var l0=Math.max(1,c0().length),km=String(Math.max(2,Math.floor(l0/2)));process.env.OMP_NUM_THREADS||(process.env.OMP_NUM_THREADS=km);process.env.ORT_NUM_THREADS||(process.env.ORT_NUM_THREADS=km);var u0=360*60*1e3,d0=60*1e3,p0=1440*60*1e3,m0=300*1e3,g0=300*1e3,_0=10*1e3,f0=1440*60*1e3,h0=30*1e3,E0=500,b0=1500,S0=6e4;async function T0(){let e=await bm(),t=Sm();(!t||t.length<32)&&(console.error("[daemon] FATAL: daemon token init returned empty or undersized \u2014 refusing to start"),process.exit(1));let n=await fm(e,t);Rm({pid:process.pid,port:e,startedAt:new Date().toISOString()}),xr(ce().enabled);try{Qt();let A=dc();A>0&&console.log(`[daemon] archive: migrated ${A} hot row(s) into archive.sqlite`)}catch(A){let $=A instanceof Error?A.message:String(A);console.error(`[daemon] archive migration failed: ${$}`)}let s=Ul({db:f(),walPath:`${Bt}-wal`}),r=Ml(),o=setInterval(()=>{Dl()},S0),a=()=>{try{Ss()}catch(A){console.error("[daemon] suggestion scan failed:",A)}},c=setTimeout(a,d0),u=setInterval(a,u0),d=()=>{try{let A=M.reapStaleLinks();(A.pruned_pids||A.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${A.pruned_pids} pid${A.pruned_pids===1?"":"s"}, ${A.pruned_sessions} session link${A.pruned_sessions===1?"":"s"}`)}catch(A){console.error("[daemon] stale-link reaper failed:",A)}},m=setTimeout(d,m0),h=setInterval(d,p0),b=()=>{try{let A=M.gcDeadPids();(A.pruned_pids||A.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${A.pruned_pids} pid${A.pruned_pids===1?"":"s"}, ${A.pruned_sessions} session link${A.pruned_sessions===1?"":"s"}`)}catch(A){console.error("[daemon] dead-pid gc failed:",A)}},S=setTimeout(b,_0),T=setInterval(b,g0),R=()=>{Yi().then(A=>{A.ran&&A.revoked&&console.log(`[daemon] license check: REVOKED${A.reason?` (${A.reason})`:""}`)}).catch(A=>{console.error("[daemon] license check failed:",A)})},O=setTimeout(R,h0),L=setInterval(R,f0),x=Rc();(async()=>{try{if(!Ge())return;let A=f().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get();if(A.n===0)return;if(!ce().autoResumeWorker){console.log(`[daemon] vector-worker dormant: ${A.n} chunk(s) pending in queue. Run \`recall vectorize\` or click Start in the web UI to drain. To restore auto-resume, set semantic.autoResumeWorker=true in ~/.recall/config.json.`);return}let te=await Ke();if(te.tier!=="pro"){console.log(`[daemon] vector-worker auto-resume skipped: ${A.n} chunk(s) pending but license tier is ${te.tier}`);return}_e().loaded||await Ue(),Ie().running||(kn(),console.log(`[daemon] vector-worker auto-resumed: ${A.n} chunk(s) pending`))}catch(A){let $=A instanceof Error?A.message:String(A);console.error(`[daemon] vector-worker auto-resume failed: ${$}`)}})();let F=A=>{console.log(`[daemon] received ${A}, shutting down`),clearTimeout(c),clearInterval(u),clearTimeout(m),clearInterval(h),clearTimeout(S),clearInterval(T),clearTimeout(O),clearInterval(L),clearInterval(o),x.stop(),s.stop(),r.close(),n.close(),Di(),process.exit(0)};process.on("SIGTERM",()=>F("SIGTERM")),process.on("SIGINT",()=>F("SIGINT")),process.on("SIGHUP",()=>F("SIGHUP")),console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{rs().then(A=>{console.log(`[daemon] boot sweep: scanned ${A.scanned} live claude(s), linked ${A.linked}, renamed ${A.renamed}, ambiguous_cwd ${A.ambiguous_cwd}`)}).catch(A=>{console.error("[daemon] boot sweep failed:",A)})},E0),setTimeout(()=>{To().then(A=>{console.log(`[daemon] ingestion sweep: scanned=${A.scanned} reindexed=${A.reindexed} up-to-date=${A.upToDate} skipped=${A.skipped} errors=${A.errors} (${A.durationMs}ms)`)}).catch(A=>{console.error("[daemon] ingestion sweep failed:",A)})},b0)}T0().catch(e=>{console.error("[daemon] fatal:",e),Di(),process.exit(1)});
2123
+ LIMIT 20`).all(m).map(E=>E.git_branch);return i.json({...g,branches:_})});function v(i){return i.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function F(i){if(!e)return i;let l=`<meta name="recall-token" content="${v(e)}" />`,m=i.indexOf("</head>");return m!==-1?i.slice(0,m)+l+i.slice(m):l+i}function U(i){let l=Dg();return i.html(Ad({projects:l.projects,sessions:l.sessions,messages:l.messages,port:Number(i.req.raw.headers.get("host")?.split(":")[1]??0),version:jg}))}function L(){try{return Ra(Fg,"utf8")}catch{return null}}return Hk?(t.use("/assets/*",Ig({root:Aa})),t.get("/favicon.svg",Ig({root:Aa})),t.get("/",i=>{i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=L();return l===null?U(i):i.html(F(l))}),t.get("*",i=>{if(i.req.path.startsWith("/api/"))return i.notFound();i.header("cache-control","no-cache, no-store, must-revalidate"),i.header("pragma","no-cache"),i.header("expires","0");let l=L();return l===null?U(i):i.html(F(l))})):t.get("/",i=>U(i)),t}function Qk(){if(Ar(),!!dt().heuristicEnabled){try{let{updated:e}=Kl();e>0&&console.log(`[auto-title] backfilled heuristic title on ${e} sessions`)}catch(e){console.error("[auto-title] backfill failed:",e)}try{let{scanned:e,updated:t}=zl();t>0&&console.log(`[auto-title] refreshed templated heuristic title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] templated-title refresh failed:",e)}try{let{scanned:e,updated:t}=Vl();t>0&&console.log(`[auto-title] refreshed recursive_meta title on ${t}/${e} sessions`)}catch(e){console.error("[auto-title] recursive_meta refresh failed:",e)}try{let{scanned:e,updated:t}=Ql();t>0&&console.log(`[auto-title] canonicalized brand on ${t}/${e} session titles`)}catch(e){console.error("[auto-title] brand canonicalization failed:",e)}}}async function Hg(e,t){let n=Vk(t);return new Promise((s,r)=>{try{let o=Ak({fetch:n.fetch,port:e,hostname:"127.0.0.1"},()=>{s(o),setImmediate(()=>{try{Qk()}catch(a){console.error("[daemon] startup maintenance crashed:",a)}})})}catch(o){r(o)}})}import{createServer as Wg}from"node:net";function Bg(e){return new Promise(t=>{let n=Wg();n.once("error",()=>t(!1)),n.once("listening",()=>{n.close(()=>t(!0))}),n.listen(e,"127.0.0.1")})}async function qg(){let e=new Set([3e3,3001,4200,5e3,5173,8e3,8080,8888,9e3]),t=51370;if(!e.has(t)&&await Bg(t))return t;for(let n=0;n<50;n++){let s=49152+Math.floor(Math.random()*16383);if(!e.has(s)&&await Bg(s))return s}return new Promise((n,s)=>{let r=Wg();r.once("error",s),r.listen(0,"127.0.0.1",()=>{let o=r.address();if(o&&typeof o=="object"){let a=o.port;r.close(()=>n(a))}else r.close(),s(new Error("could not determine a free port"))})})}function Xg(e){let t=Date.now(),n=e.prepare("SELECT id, decoded_path FROM projects WHERE repo_root IS NULL AND is_repo = 0").all();if(n.length===0)return{total:0,ephemeral:0,walked:0,folded:0,deferred:0,durationMs:Date.now()-t};let s=new Set,r=e.prepare("SELECT DISTINCT repo_root FROM projects WHERE repo_root IS NOT NULL AND repo_root != ?").all(jt);for(let y of r)s.add(y.repo_root);let o=[],a=e.prepare(`UPDATE projects
2124
+ SET repo_root = ?, main_repo = NULL, is_repo = 0, is_worktree = 0
2125
+ WHERE id = ?`),c=e.prepare(`UPDATE projects
2126
+ SET repo_root = ?, main_repo = ?, is_repo = 1, is_worktree = ?
2127
+ WHERE id = ?`),u=e.prepare("UPDATE projects SET repo_root = ?, is_repo = 1 WHERE id = ?"),d=0,p=0,f=0;e.transaction(()=>{for(let y of n)Rn(y.decoded_path)&&(a.run(jt,y.id),d++);for(let y of n){if(Rn(y.decoded_path))continue;let b=Gs(y.decoded_path);b.isRepo?(c.run(b.root,b.mainRepo,b.isWorktree?1:0,y.id),s.add(b.root),p++):o.push(y)}for(let y of o){let b=ku(y.decoded_path,s);b&&(u.run(b,y.id),f++)}})();let S=o.length-f;return{total:n.length,ephemeral:d,walked:p,folded:f,deferred:S,durationMs:Date.now()-t}}yo();q();ne();bt();function Oa(){let e=[];return tn("stale-claude-json-mcp-paths",()=>{for(let t of Zk())e.push(t)}),tn("zombie-mcp",()=>{for(let t of tA())e.push(t)}),tn("chunk-queue-growth",()=>{for(let t of nA())e.push(t)}),tn("watcher-reflag-loop",()=>{for(let t of sA())e.push(t)}),tn("disk-pressure-backups",()=>{for(let t of rA())e.push(t)}),tn("daemon-state-files",()=>{for(let t of iA())e.push(t)}),e}function tn(e,t){try{t()}catch(n){let s=n instanceof Error?n.message:String(n);process.stderr.write(`[doctor-tick] ${e} check failed: ${s}
2128
+ `)}}function Zk(){let e=kd();if(!e.configExists)return[];let t=[];for(let n of e.findings)t.push(eA(n));return t}function eA(e){return{check:"stale_claude_json_mcp_path",severity:e.severity==="HIGH"?"critical":"high",keyFacts:{name:e.name,stalePath:e.stalePath},message:`~/.claude.json mcpServers.${e.name} points at a deleted path: ${e.stalePath}`,remediation:e.remediation}}function tA(){let e=pi();return!e.flagged||!e.message?[]:[{check:"zombie_mcp_threshold",severity:"critical",keyFacts:{countBucket:Math.floor(e.count/5)*5,aggregateGbBucket:Math.floor(e.aggregateRssKb/(1024*1024))},message:e.message,remediation:"`recall mcp-prune --all` reaps. If that returns no children on a stale build, `pkill -f mcp-server.js` is the nuclear option."}]}function nA(){let e=bi();return e.status==="ok"?[]:[{check:"chunk_queue_growth",severity:{critical:"critical",high:"high",medium:"medium"}[e.status],keyFacts:{sizeBucket:Math.floor(e.currentSize/1e3)*1e3,semanticEnabled:e.semanticEnabled},message:e.message,remediation:e.remediation??"Re-run `recall doctor` for context."}]}function sA(){let e=Ks(20),t=[];for(let n of e){if(n.count<=Td)continue;let s=n.path.replace(/'/g,"''");t.push({check:"watcher_reflag_loop",severity:"critical",keyFacts:{path:n.path},message:`Watcher reindexed ${n.path} ${n.count.toLocaleString()} times in the last hour \u2014 reflag loop.`,remediation:`Mark it skipped with a SQL one-liner:
2129
+ sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${s}';"`})}return t}function rA(){let e=wd();if(e.severity==="ok"||e.severity==="low")return[];let t=e.severity==="high"?"critical":"medium",n=e.severity==="high"?`Disk pressure HIGH \u2014 ${ft(e.freeBytes)} free of ${ft(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${ft(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`:`Disk pressure MEDIUM \u2014 ${ft(e.freeBytes)} free of ${ft(e.totalBytes)} (${e.freePercent.toFixed(1)}%), backups ${ft(e.backupTotalBytes)} across ${e.backupFileCount} file(s).`;return[{check:"disk_pressure_backups",severity:t,keyFacts:{freePercentBucket:Math.floor(e.freePercent/5)*5,backupGbBucket:Math.floor(e.backupTotalBytes/(1024*1024*1024)),severity:e.severity},message:n,remediation:oA(e.oldFiles)}]}function oA(e){return e.length===0?"No snapshots older than 30 days \u2014 recent ones may still be rollback-critical, leave them alone unless you know what you are doing.":`Review snapshots older than 30 days then delete: ${e.slice(0,3).map(n=>`${n.name} (${ft(n.sizeBytes)}, ${n.ageDays}d old)`).join("; ")}. See memory \`partial_corpus_swap_bug_20260519\` \u2014 keep most recent .pre-swap for rollback.`}function iA(){let e=Rd();return!e.flagged||!e.message?[]:[{check:"daemon_state_files_missing",severity:"critical",keyFacts:{missing:[...e.missing].sort().join(",")},message:e.message,remediation:e.remediation??"recall stop && recall start"}]}function ft(e){return!Number.isFinite(e)||e<=0?"0 B":e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}var Jg=300*1e3;function aA(){let e=process.env.RECALL_DOCTOR_TICK_MS;if(!e)return Jg;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<100?Jg:t}function Gg(e=Oa,t=new Date){let n=[];try{n=e()}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] check pipeline failed: ${a}
2130
+ `),n=[]}let s=yi(),r=Ed(s,n,t);Ti(r);try{gd().catch(o=>{let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] auto-prune failed: ${a}
2131
+ `)})}catch(o){let a=o instanceof Error?o.message:String(o);process.stderr.write(`[doctor-tick] auto-prune failed (sync): ${a}
2132
+ `)}return r}function Yg(e=Oa){let t=aA(),n=setInterval(()=>{Gg(e)},t);return typeof n.unref=="function"&&n.unref(),{stop:()=>clearInterval(n),runOnce:()=>{Gg(e)}}}var lA=Math.max(1,cA().length),Kg=String(Math.max(2,Math.floor(lA/2)));process.env.OMP_NUM_THREADS||(process.env.OMP_NUM_THREADS=Kg);process.env.ORT_NUM_THREADS||(process.env.ORT_NUM_THREADS=Kg);var uA=360*60*1e3,dA=60*1e3,pA=1440*60*1e3,mA=300*1e3,gA=300*1e3,fA=10*1e3,_A=1440*60*1e3,hA=30*1e3,EA=500,bA=1500,SA=6e4;async function yA(){let e=await qg(),t=Zu();(!t||t.length<32)&&(console.error("[daemon] FATAL: daemon token mint returned empty or undersized \u2014 refusing to start"),process.exit(1));try{zm().repointed&&console.log("[daemon] mcp self-heal: ~/.claude.json repointed (was stale)")}catch(L){console.error("[daemon] mcp self-heal failed (non-fatal, daemon continues):",L)}let n=await Hg(e,t),s={pid:process.pid,port:e,startedAt:new Date().toISOString()};ed({...s,token:t}),uo(ae().enabled,"boot");try{hn();let L=rl();L>0&&console.log(`[daemon] archive: migrated ${L} hot row(s) into archive.sqlite`)}catch(L){let i=L instanceof Error?L.message:String(L);console.error(`[daemon] archive migration failed: ${i}`)}let r=rd({db:h(),walPath:`${st}-wal`}),o=Ju(),a=setInterval(()=>{Gu()},SA),c=()=>{try{Xs()}catch(L){console.error("[daemon] suggestion scan failed:",L)}},u=setTimeout(c,dA),d=setInterval(c,uA),p=()=>{try{let L=j.reapStaleLinks();(L.pruned_pids||L.pruned_sessions)&&console.log(`[daemon] reaper: pruned ${L.pruned_pids} pid${L.pruned_pids===1?"":"s"}, ${L.pruned_sessions} session link${L.pruned_sessions===1?"":"s"}`)}catch(L){console.error("[daemon] stale-link reaper failed:",L)}},f=setTimeout(p,mA),S=setInterval(p,pA),y=()=>{try{let L=j.gcDeadPids();(L.pruned_pids||L.pruned_sessions)&&console.log(`[daemon] dead-pid gc: pruned ${L.pruned_pids} pid${L.pruned_pids===1?"":"s"}, ${L.pruned_sessions} session link${L.pruned_sessions===1?"":"s"}`)}catch(L){console.error("[daemon] dead-pid gc failed:",L)}},b=setTimeout(y,fA),R=setInterval(y,gA),k=()=>{Ha().then(L=>{L.ran&&L.revoked&&console.log(`[daemon] license check: REVOKED${L.reason?` (${L.reason})`:""}`)}).catch(L=>{console.error("[daemon] license check failed:",L)})},M=setTimeout(k,hA),P=setInterval(k,_A),G=hl();(async()=>{try{if(!et())return;let L=h().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get();if(L.n===0)return;if(!ae().autoResumeWorker){console.log(`[daemon] vector-worker dormant: ${L.n} chunk(s) pending in queue. Run \`recall vectorize\` or click Start in the web UI to drain. To restore auto-resume, set semantic.autoResumeWorker=true in ~/.recall/config.json.`);return}let l=await rt();if(l.tier!=="pro"){console.log(`[daemon] vector-worker auto-resume skipped: ${L.n} chunk(s) pending but license tier is ${l.tier}`);return}pe().loaded||await Ge(),Me().running||(Nn(),console.log(`[daemon] vector-worker auto-resumed: ${L.n} chunk(s) pending`))}catch(L){let i=L instanceof Error?L.message:String(L);console.error(`[daemon] vector-worker auto-resume failed: ${i}`)}})();let D=Yg(),v=setInterval(()=>{try{let{restored:L}=nd({...s,token:t});L.length>0&&console.log(`[daemon] state-files heal: restored ${L.join(",")}`)}catch(L){let i=L instanceof Error?L.message:String(L);console.error(`[daemon] state-files heal failed: ${i}`)}},3e4);typeof v.unref=="function"&&v.unref();let F=tr();console.log(F==="enabled"?"[auto-prune] mode=enabled \u2014 orphans >10min and runaway-CPU MCPs will be killed":F==="off"?"[auto-prune] mode=off \u2014 auto-prune fully disabled":"[auto-prune] mode=dry-run (set RECALL_AUTO_PRUNE=enabled to enforce)");let U=L=>{console.log(`[daemon] received ${L}, shutting down`),clearTimeout(u),clearInterval(d),clearTimeout(f),clearInterval(S),clearTimeout(b),clearInterval(R),clearTimeout(M),clearInterval(P),clearInterval(a),clearInterval(v),G.stop(),r.stop(),D.stop(),o.close(),n.close(),fi(),process.exit(0)};process.on("SIGTERM",()=>U("SIGTERM")),process.on("SIGINT",()=>U("SIGINT")),process.on("SIGHUP",()=>U("SIGHUP"));try{let L=Xg(h());L.total>0&&console.log(`[daemon] projects backfill: ephemeral=${L.ephemeral} walked=${L.walked} folded=${L.folded} deferred=${L.deferred} (total=${L.total}, ${L.durationMs}ms)`)}catch(L){console.error("[daemon] projects backfill failed:",L)}console.log(`[daemon] ready on http://127.0.0.1:${e} pid=${process.pid}`),setTimeout(()=>{Os().then(L=>{console.log(`[daemon] boot sweep: scanned ${L.scanned} live claude(s), linked ${L.linked}, renamed ${L.renamed}, ambiguous_cwd ${L.ambiguous_cwd}`)}).catch(L=>{console.error("[daemon] boot sweep failed:",L)})},EA),setTimeout(()=>{ui().then(L=>{console.log(`[daemon] ingestion sweep: scanned=${L.scanned} reindexed=${L.reindexed} up-to-date=${L.upToDate} skipped=${L.skipped} errors=${L.errors} (${L.durationMs}ms)`)}).catch(L=>{console.error("[daemon] ingestion sweep failed:",L)})},bA)}yA().catch(e=>{console.error("[daemon] fatal:",e),fi(),process.exit(1)});