@vpxa/aikit 0.1.166 → 0.1.168

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.
@@ -205,6 +205,13 @@ interface ISqliteAdapter {
205
205
  * be loaded — typically because the platform has no prebuild and the native
206
206
  * compile failed during install. In that case the consumer should disable
207
207
  * vector-dependent code paths via `adapter.vectorCapable`.
208
+ *
209
+ * ## Auto-healing (ABI mismatch)
210
+ *
211
+ * When the Node.js version changes (e.g., Node 24 → Node 22), the native
212
+ * .node binary is incompatible. This adapter detects the mismatch BEFORE
213
+ * loading the module in-process (via a child process probe) so the file
214
+ * remains unlocked and can be deleted + rebuilt cleanly.
208
215
  */
209
216
  declare function createSqliteAdapter(dbPath: string): Promise<ISqliteAdapter>;
210
217
  /**
@@ -1,4 +1,4 @@
1
- import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as n,readFileSync as r,renameSync as i,unlinkSync as a,writeFileSync as o}from"node:fs";import{dirname as s,join as c}from"node:path";import{AIKIT_PATHS as l,EMBEDDING_DEFAULTS as u,SEARCH_DEFAULTS as d,STORE_DEFAULTS as f,createLogger as p,serializeError as m,sourceTypeContentTypes as h}from"../../core/dist/index.js";var g=e(import.meta.url);const _=p(`sqlite-adapter`),v=e(import.meta.url);var y=class{type=`better-sqlite3`;vectorCapable=!1;db=null;stmtCache=new Map;dbPath=``;DatabaseCtor=null;recovering=!1;async open(e){let t;try{t=v(`better-sqlite3`)}catch(e){throw Error(`better-sqlite3 native binding unavailable: ${e instanceof Error?e.message:String(e)}`)}this.db=new t(e),this.dbPath=e,this.DatabaseCtor=t,this.db.pragma(`journal_mode = WAL`),this.db.pragma(`foreign_keys = ON`),this.db.pragma(`synchronous = NORMAL`),this.runIntegrityCheck(e,t)||(this.db?.pragma(`journal_mode = WAL`),this.db?.pragma(`foreign_keys = ON`),this.db?.pragma(`synchronous = NORMAL`));try{v(`sqlite-vec`).load(this.db),this.vectorCapable=!0,_.info(`sqlite-vec extension loaded`)}catch(e){this.vectorCapable=!1,_.warn(`sqlite-vec extension failed to load; vector search disabled`,m(e))}}exec(e){try{this.getDb().exec(e)}catch(t){if(this.isCorruptionError(t)){this.recover(),this.getDb().exec(e);return}throw t}}pragma(e){this.getDb().pragma(e)}queryAll(e,t=[]){try{let n=this.prepareCached(e);return t.length>0?n.all(...t):n.all()}catch(n){if(this.isCorruptionError(n))return this.recover(),this.queryAll(e,t);throw n}}run(e,t=[]){try{let n=this.prepareCached(e);t.length>0?n.run(...t):n.run()}catch(n){if(this.isCorruptionError(n)){this.recover(),this.run(e,t);return}throw n}}flush(){}close(){this.db&&=(this.stmtCache.clear(),this.db.close(),null)}runIntegrityCheck(e,t){try{let t=this.db?.pragma(`integrity_check`);if(t.length===1&&t[0]?.integrity_check===`ok`)return!0;let n=t.map(e=>e.integrity_check).slice(0,5).join(`; `);_.warn(`Database integrity check failed — recreating`,{dbPath:e,issues:n})}catch(t){_.warn(`Integrity check query failed — recreating database`,{dbPath:e,error:m(t)})}try{this.db?.close()}catch{}this.db=null,this.stmtCache.clear();try{a(e)}catch{}try{a(`${e}-wal`)}catch{}try{a(`${e}-shm`)}catch{}return this.db=new t(e),_.info(`Database recreated successfully — full reindex required`,{dbPath:e}),!1}isCorruptionError(e){if(this.recovering)return!1;let t=e instanceof Error?e.message:String(e);return/database disk image is malformed|file is not a database|database or disk is full/.test(t)}recover(){if(this.recovering)throw Error(`BetterSqlite3Adapter: recovery already in progress`);this.recovering=!0;try{_.warn(`Runtime corruption detected — recovering database`,{dbPath:this.dbPath});try{this.db?.close()}catch{}this.db=null,this.stmtCache.clear();try{a(this.dbPath)}catch{}try{a(`${this.dbPath}-wal`)}catch{}try{a(`${this.dbPath}-shm`)}catch{}if(!this.DatabaseCtor)throw Error(`DatabaseCtor is not initialized`);this.db=this.DatabaseCtor(this.dbPath),this.db.pragma(`journal_mode = WAL`),this.db.pragma(`foreign_keys = ON`),this.db.pragma(`synchronous = NORMAL`);try{v(`sqlite-vec`).load(this.db),this.vectorCapable=!0}catch{this.vectorCapable=!1}_.info(`Database recovered — full reindex required`,{dbPath:this.dbPath})}finally{this.recovering=!1}}getDb(){if(!this.db)throw Error(`BetterSqlite3Adapter: database not opened`);return this.db}prepareCached(e){let t=this.stmtCache.get(e);if(t)return t;let n=this.getDb().prepare(e);return this.stmtCache.set(e,n),n}};function b(e){return v.resolve(`sql.js/dist/${e}`)}var x=class{type=`sql.js`;vectorCapable=!1;db=null;dbPath=``;dirty=!1;inTransaction=!1;async open(e){this.dbPath=e;let n=(await import(`sql.js`)).default,i=await n({locateFile:e=>b(e)});if(t(e)){let t=r(e);this.db=new i.Database(t)}else this.db=new i.Database}exec(e){let t=e.trimStart().toUpperCase();this.getDb().run(e),t.startsWith(`BEGIN`)?this.inTransaction=!0:(t.startsWith(`COMMIT`)||t.startsWith(`ROLLBACK`))&&(this.inTransaction=!1),this.dirty=!0}pragma(e){this.getDb().exec(`PRAGMA ${e}`)}queryAll(e,t=[]){let n=this.getDb().prepare(e);try{t.length>0&&n.bind(t);let e=[];for(;n.step();)e.push(n.getAsObject());return e}finally{n.free()}}run(e,t=[]){let n=this.getDb(),r=e.trimStart().toUpperCase(),i=!this.inTransaction&&(r.startsWith(`INSERT`)||r.startsWith(`UPDATE`));i&&n.run(`SAVEPOINT fk_check`);try{if(t.length>0){let r=n.prepare(e);try{r.bind(t),r.step()}finally{r.free()}}else n.run(e);if(i){if(n.exec(`PRAGMA foreign_key_check`).length>0)throw n.run(`ROLLBACK TO fk_check`),n.run(`RELEASE fk_check`),Error(`FOREIGN KEY constraint failed`);n.run(`RELEASE fk_check`)}}catch(e){if(i)try{n.run(`ROLLBACK TO fk_check`),n.run(`RELEASE fk_check`)}catch{}throw e}this.dirty=!0}flush(){if(!this.dirty||!this.db)return;let e=this.db.export(),r=`${this.dbPath}.tmp`,a=s(this.dbPath);a&&!t(a)&&n(a,{recursive:!0}),o(r,Buffer.from(e)),i(r,this.dbPath),this.dirty=!1}close(){if(this.db){let e=this.db,t;try{this.flush()}catch(e){t=e;try{a(`${this.dbPath}.tmp`)}catch{}}try{e.close()}finally{this.db=null}if(t)throw t}}getDb(){if(!this.db)throw Error(`SqlJsAdapter: database not opened`);return this.db}};async function S(){try{let{execSync:e}=await import(`node:child_process`),{createRequire:t}=await import(`node:module`),n=t(import.meta.url).resolve(`better-sqlite3/package.json`).replace(/[\\/]package\.json$/,``).replace(/[\\/]node_modules[\\/]better-sqlite3$/,``);return _.info(`Attempting native module rebuild for better-sqlite3`,{cwd:n}),e(`npm rebuild better-sqlite3`,{cwd:n,stdio:`pipe`,timeout:6e4}),_.info(`Native module rebuild completed successfully`),!0}catch(e){return _.warn(`Native module rebuild failed — continuing with sql.js fallback`,m(e)),!1}}let C=!1;async function w(e){let t=new y;try{return await t.open(e),t}catch(t){let n=t instanceof Error?t.message:String(t);if(/NODE_MODULE_VERSION|Could not locate the bindings file|no native build was found/.test(n)&&await S()){let t=new y;try{return await t.open(e),_.info(`better-sqlite3 recovered after native module rebuild`),t}catch{}}C||(C=!0,_.warn(`[aikit] better-sqlite3 unavailable — falling back to sql.js (vector search disabled). Reinstall with prebuild support or rebuild from source to enable vector search.`,m(t)))}let n=new x;try{return await n.open(e),n}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`[aikit] SQLite adapter "sql.js" failed to load: ${t}`)}}async function T(e){let t=new x;return await t.open(e),t}var E=class{adapter=null;reopenPromise=null;dbPath;externalAdapter;constructor(e={}){if(e.adapter)this.adapter=e.adapter,this.externalAdapter=!0,this.dbPath=``;else{let t=e.path??l.data;this.dbPath=c(t,`graph.db`),this.externalAdapter=!1}}async initialize(){if(this.externalAdapter){let e=this.getAdapter();this.createTables(e),this.migrateSchema(e),e.flush();return}let e=s(this.dbPath);t(e)||n(e,{recursive:!0}),this.adapter=await w(this.dbPath),this.configureAdapter(this.adapter),this.createTables(this.adapter),this.migrateSchema(this.adapter),this.adapter.flush()}configureAdapter(e){e.pragma(`journal_mode = WAL`),e.pragma(`foreign_keys = ON`)}createTables(e){e.exec(`
1
+ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as n,readFileSync as r,renameSync as i,unlinkSync as a,writeFileSync as o}from"node:fs";import{dirname as s,join as c}from"node:path";import{AIKIT_PATHS as l,EMBEDDING_DEFAULTS as u,SEARCH_DEFAULTS as d,STORE_DEFAULTS as f,createLogger as p,serializeError as m,sourceTypeContentTypes as h}from"../../core/dist/index.js";var g=e(import.meta.url);const _=p(`sqlite-adapter`),v=e(import.meta.url);var y=class{type=`better-sqlite3`;vectorCapable=!1;db=null;stmtCache=new Map;dbPath=``;DatabaseCtor=null;recovering=!1;async open(e){let t;try{t=v(`better-sqlite3`)}catch(e){throw Error(`better-sqlite3 native binding unavailable: ${e instanceof Error?e.message:String(e)}`)}this.db=new t(e),this.dbPath=e,this.DatabaseCtor=t,this.db.pragma(`journal_mode = WAL`),this.db.pragma(`foreign_keys = ON`),this.db.pragma(`synchronous = NORMAL`),this.runIntegrityCheck(e,t)||(this.db?.pragma(`journal_mode = WAL`),this.db?.pragma(`foreign_keys = ON`),this.db?.pragma(`synchronous = NORMAL`));try{v(`sqlite-vec`).load(this.db),this.vectorCapable=!0,_.info(`sqlite-vec extension loaded`)}catch(e){this.vectorCapable=!1,_.warn(`sqlite-vec extension failed to load; vector search disabled`,m(e))}}exec(e){try{this.getDb().exec(e)}catch(t){if(this.isCorruptionError(t)){this.recover(),this.getDb().exec(e);return}throw t}}pragma(e){this.getDb().pragma(e)}queryAll(e,t=[]){try{let n=this.prepareCached(e);return t.length>0?n.all(...t):n.all()}catch(n){if(this.isCorruptionError(n))return this.recover(),this.queryAll(e,t);throw n}}run(e,t=[]){try{let n=this.prepareCached(e);t.length>0?n.run(...t):n.run()}catch(n){if(this.isCorruptionError(n)){this.recover(),this.run(e,t);return}throw n}}flush(){}close(){this.db&&=(this.stmtCache.clear(),this.db.close(),null)}runIntegrityCheck(e,t){try{let t=this.db?.pragma(`integrity_check`);if(t.length===1&&t[0]?.integrity_check===`ok`)return!0;let n=t.map(e=>e.integrity_check).slice(0,5).join(`; `);_.warn(`Database integrity check failed — recreating`,{dbPath:e,issues:n})}catch(t){_.warn(`Integrity check query failed — recreating database`,{dbPath:e,error:m(t)})}try{this.db?.close()}catch{}this.db=null,this.stmtCache.clear();try{a(e)}catch{}try{a(`${e}-wal`)}catch{}try{a(`${e}-shm`)}catch{}return this.db=new t(e),_.info(`Database recreated successfully — full reindex required`,{dbPath:e}),!1}isCorruptionError(e){if(this.recovering)return!1;let t=e instanceof Error?e.message:String(e);return/database disk image is malformed|file is not a database|database or disk is full/.test(t)}recover(){if(this.recovering)throw Error(`BetterSqlite3Adapter: recovery already in progress`);this.recovering=!0;try{_.warn(`Runtime corruption detected — recovering database`,{dbPath:this.dbPath});try{this.db?.close()}catch{}this.db=null,this.stmtCache.clear();try{a(this.dbPath)}catch{}try{a(`${this.dbPath}-wal`)}catch{}try{a(`${this.dbPath}-shm`)}catch{}if(!this.DatabaseCtor)throw Error(`DatabaseCtor is not initialized`);this.db=this.DatabaseCtor(this.dbPath),this.db.pragma(`journal_mode = WAL`),this.db.pragma(`foreign_keys = ON`),this.db.pragma(`synchronous = NORMAL`);try{v(`sqlite-vec`).load(this.db),this.vectorCapable=!0}catch{this.vectorCapable=!1}_.info(`Database recovered — full reindex required`,{dbPath:this.dbPath})}finally{this.recovering=!1}}getDb(){if(!this.db)throw Error(`BetterSqlite3Adapter: database not opened`);return this.db}prepareCached(e){let t=this.stmtCache.get(e);if(t)return t;let n=this.getDb().prepare(e);return this.stmtCache.set(e,n),n}};function b(e){return v.resolve(`sql.js/dist/${e}`)}var x=class{type=`sql.js`;vectorCapable=!1;db=null;dbPath=``;dirty=!1;inTransaction=!1;async open(e){this.dbPath=e;let n=(await import(`sql.js`)).default,i=await n({locateFile:e=>b(e)});if(t(e)){let t=r(e);this.db=new i.Database(t)}else this.db=new i.Database}exec(e){let t=e.trimStart().toUpperCase();this.getDb().run(e),t.startsWith(`BEGIN`)?this.inTransaction=!0:(t.startsWith(`COMMIT`)||t.startsWith(`ROLLBACK`))&&(this.inTransaction=!1),this.dirty=!0}pragma(e){this.getDb().exec(`PRAGMA ${e}`)}queryAll(e,t=[]){let n=this.getDb().prepare(e);try{t.length>0&&n.bind(t);let e=[];for(;n.step();)e.push(n.getAsObject());return e}finally{n.free()}}run(e,t=[]){let n=this.getDb(),r=e.trimStart().toUpperCase(),i=!this.inTransaction&&(r.startsWith(`INSERT`)||r.startsWith(`UPDATE`));i&&n.run(`SAVEPOINT fk_check`);try{if(t.length>0){let r=n.prepare(e);try{r.bind(t),r.step()}finally{r.free()}}else n.run(e);if(i){if(n.exec(`PRAGMA foreign_key_check`).length>0)throw n.run(`ROLLBACK TO fk_check`),n.run(`RELEASE fk_check`),Error(`FOREIGN KEY constraint failed`);n.run(`RELEASE fk_check`)}}catch(e){if(i)try{n.run(`ROLLBACK TO fk_check`),n.run(`RELEASE fk_check`)}catch{}throw e}this.dirty=!0}flush(){if(!this.dirty||!this.db)return;let e=this.db.export(),r=`${this.dbPath}.tmp`,a=s(this.dbPath);a&&!t(a)&&n(a,{recursive:!0}),o(r,Buffer.from(e)),i(r,this.dbPath),this.dirty=!1}close(){if(this.db){let e=this.db,t;try{this.flush()}catch(e){t=e;try{a(`${this.dbPath}.tmp`)}catch{}}try{e.close()}finally{this.db=null}if(t)throw t}}getDb(){if(!this.db)throw Error(`SqlJsAdapter: database not opened`);return this.db}};function S(){try{let e=c(v.resolve(`better-sqlite3/package.json`).replace(/[\\/]package\.json$/,``),`build`,`Release`,`better_sqlite3.node`);return t(e)?e:null}catch{return null}}async function C(){if(!S())return`missing`;try{let{execSync:e}=await import(`node:child_process`);return e(`"${process.execPath}" -e "require('better-sqlite3')"`,{stdio:`pipe`,timeout:15e3}),`ok`}catch(e){let t=e instanceof Error&&`stderr`in e?String(e.stderr):``;return/NODE_MODULE_VERSION/.test(t)?`abi-mismatch`:/Could not locate the bindings file|no native build was found/.test(t)?`missing`:`error`}}async function w(e=!1){try{let{execSync:n}=await import(`node:child_process`),r=v.resolve(`better-sqlite3/package.json`).replace(/[\\/]package\.json$/,``),i=r.replace(/[\\/]node_modules[\\/]better-sqlite3$/,``);if(e){let e=c(r,`build`,`Release`,`better_sqlite3.node`);if(t(e))try{a(e),_.info(`Deleted stale native binding before rebuild`,{path:e})}catch(t){_.warn(`Cannot delete stale native binding — file may be locked by another process`,{path:e,error:t instanceof Error?t.message:String(t)})}}return _.info(`Attempting native module rebuild for better-sqlite3`,{cwd:i}),n(`npm rebuild better-sqlite3`,{cwd:i,stdio:`pipe`,timeout:6e4}),_.info(`Native module rebuild completed successfully`),!0}catch(e){return _.warn(`Native module rebuild failed — continuing with sql.js fallback`,m(e)),!1}}let T=!1;function E(){return!!process.env.VITEST||process.argv.some(e=>e.includes(`vitest`))}async function D(e){if(!E()){let e=await C();e===`abi-mismatch`?(_.info(`Detected NODE_MODULE_VERSION mismatch via pre-flight probe — rebuilding before load`),await w(!0)&&_.info(`Pre-flight rebuild succeeded — proceeding with native adapter`)):e===`missing`&&(_.info(`No native binding found — attempting rebuild before load`),await w(!1))}let t=new y;try{return await t.open(e),t}catch(t){let n=t instanceof Error?t.message:String(t);if(!E()&&/NODE_MODULE_VERSION|Could not locate the bindings file|no native build was found/.test(n)&&await w(!1)){let t=new y;try{return await t.open(e),_.info(`better-sqlite3 recovered after native module rebuild`),t}catch{}}T||(T=!0,_.warn(`[aikit] better-sqlite3 unavailable — falling back to sql.js (vector search disabled). Reinstall with prebuild support or rebuild from source to enable vector search.`,m(t)))}let n=new x;try{return await n.open(e),n}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`[aikit] SQLite adapter "sql.js" failed to load: ${t}`)}}async function O(e){let t=new x;return await t.open(e),t}var k=class{adapter=null;reopenPromise=null;dbPath;externalAdapter;constructor(e={}){if(e.adapter)this.adapter=e.adapter,this.externalAdapter=!0,this.dbPath=``;else{let t=e.path??l.data;this.dbPath=c(t,`graph.db`),this.externalAdapter=!1}}async initialize(){if(this.externalAdapter){let e=this.getAdapter();this.createTables(e),this.migrateSchema(e),e.flush();return}let e=s(this.dbPath);t(e)||n(e,{recursive:!0}),this.adapter=await D(this.dbPath),this.configureAdapter(this.adapter),this.createTables(this.adapter),this.migrateSchema(this.adapter),this.adapter.flush()}configureAdapter(e){e.pragma(`journal_mode = WAL`),e.pragma(`foreign_keys = ON`)}createTables(e){e.exec(`
2
2
  CREATE TABLE IF NOT EXISTS nodes (
3
3
  id TEXT PRIMARY KEY,
4
4
  type TEXT NOT NULL,
@@ -39,7 +39,7 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
39
39
  FOREIGN KEY (process_id) REFERENCES processes(id) ON DELETE CASCADE,
40
40
  FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE
41
41
  )
42
- `),e.exec(`CREATE INDEX IF NOT EXISTS idx_process_steps_node ON process_steps(node_id)`)}migrateSchema(e){for(let t of[`ALTER TABLE edges ADD COLUMN confidence REAL DEFAULT 1.0`,`ALTER TABLE nodes ADD COLUMN community TEXT`])try{e.exec(t)}catch{}e.exec(`CREATE INDEX IF NOT EXISTS idx_nodes_community ON nodes(community)`)}getAdapter(){if(!this.adapter)throw Error(`SqliteGraphStore not initialized — call initialize() first`);return this.adapter}async ensureOpen(){if(this.adapter)return;if(this.externalAdapter)throw Error(`SqliteGraphStore: external adapter has been closed by its owner; cannot reopen`);if(this.reopenPromise)return this.reopenPromise;let e=this.reopenAdapter();this.reopenPromise=e;try{await e}finally{this.reopenPromise===e&&(this.reopenPromise=null)}}async reopenAdapter(){let e=await w(this.dbPath);this.configureAdapter(e),this.adapter=e}query(e,t=[]){return this.getAdapter().queryAll(e,t)}run(e,t=[]){this.getAdapter().run(e,t)}async upsertNode(e){await this.ensureOpen(),this.run(`INSERT INTO nodes (id, type, name, properties, source_record_id, source_path, created_at, community)
42
+ `),e.exec(`CREATE INDEX IF NOT EXISTS idx_process_steps_node ON process_steps(node_id)`)}migrateSchema(e){for(let t of[`ALTER TABLE edges ADD COLUMN confidence REAL DEFAULT 1.0`,`ALTER TABLE nodes ADD COLUMN community TEXT`])try{e.exec(t)}catch{}e.exec(`CREATE INDEX IF NOT EXISTS idx_nodes_community ON nodes(community)`)}getAdapter(){if(!this.adapter)throw Error(`SqliteGraphStore not initialized — call initialize() first`);return this.adapter}async ensureOpen(){if(this.adapter)return;if(this.externalAdapter)throw Error(`SqliteGraphStore: external adapter has been closed by its owner; cannot reopen`);if(this.reopenPromise)return this.reopenPromise;let e=this.reopenAdapter();this.reopenPromise=e;try{await e}finally{this.reopenPromise===e&&(this.reopenPromise=null)}}async reopenAdapter(){let e=await D(this.dbPath);this.configureAdapter(e),this.adapter=e}query(e,t=[]){return this.getAdapter().queryAll(e,t)}run(e,t=[]){this.getAdapter().run(e,t)}async upsertNode(e){await this.ensureOpen(),this.run(`INSERT INTO nodes (id, type, name, properties, source_record_id, source_path, created_at, community)
43
43
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
44
44
  ON CONFLICT(id) DO UPDATE SET
45
45
  type = excluded.type, name = excluded.name, properties = excluded.properties,
@@ -57,19 +57,19 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
57
57
  VALUES (?, ?, ?, ?, ?, ?, ?)
58
58
  ON CONFLICT(id) DO UPDATE SET
59
59
  from_id = excluded.from_id, to_id = excluded.to_id,
60
- type = excluded.type, weight = excluded.weight, confidence = excluded.confidence, properties = excluded.properties`,[t.id,t.fromId,t.toId,t.type,t.weight??1,t.confidence??1,JSON.stringify(t.properties??{})]);t.exec(`COMMIT`),t.flush()}catch(e){throw t.exec(`ROLLBACK`),e}finally{t.pragma(`foreign_keys = ON`)}}async getNode(e){await this.ensureOpen();let t=this.query(`SELECT * FROM nodes WHERE id = ?`,[e]);return t.length>0?O(t[0]):null}async getNeighbors(e,t){await this.ensureOpen();let n=t?.direction??`both`,r=t?.edgeType,i=t?.limit??50,a=[],o=[],s=new Set;if(n===`outgoing`||n===`both`){let t=`
60
+ type = excluded.type, weight = excluded.weight, confidence = excluded.confidence, properties = excluded.properties`,[t.id,t.fromId,t.toId,t.type,t.weight??1,t.confidence??1,JSON.stringify(t.properties??{})]);t.exec(`COMMIT`),t.flush()}catch(e){throw t.exec(`ROLLBACK`),e}finally{t.pragma(`foreign_keys = ON`)}}async getNode(e){await this.ensureOpen();let t=this.query(`SELECT * FROM nodes WHERE id = ?`,[e]);return t.length>0?j(t[0]):null}async getNeighbors(e,t){await this.ensureOpen();let n=t?.direction??`both`,r=t?.edgeType,i=t?.limit??50,a=[],o=[],s=new Set;if(n===`outgoing`||n===`both`){let t=`
61
61
  SELECT e.id AS edge_id, e.from_id, e.to_id, e.type AS edge_type, e.weight,
62
62
  e.confidence AS edge_confidence, e.properties AS edge_props,
63
63
  n.id AS node_id, n.type AS node_type, n.name AS node_name, n.properties AS node_props,
64
64
  n.source_record_id AS node_src_rec, n.source_path AS node_src_path,
65
65
  n.created_at AS node_created, n.community AS node_community
66
- FROM edges e JOIN nodes n ON e.to_id = n.id WHERE e.from_id = ?`,n=[e];r&&(t+=` AND e.type = ?`,n.push(r)),t+=` LIMIT ?`,n.push(i);let c=this.query(t,n);for(let e of c)o.push(A(e)),s.has(e.node_id)||(s.add(e.node_id),a.push(j(e)))}if(n===`incoming`||n===`both`){let t=`
66
+ FROM edges e JOIN nodes n ON e.to_id = n.id WHERE e.from_id = ?`,n=[e];r&&(t+=` AND e.type = ?`,n.push(r)),t+=` LIMIT ?`,n.push(i);let c=this.query(t,n);for(let e of c)o.push(N(e)),s.has(e.node_id)||(s.add(e.node_id),a.push(P(e)))}if(n===`incoming`||n===`both`){let t=`
67
67
  SELECT e.id AS edge_id, e.from_id, e.to_id, e.type AS edge_type, e.weight,
68
68
  e.confidence AS edge_confidence, e.properties AS edge_props,
69
69
  n.id AS node_id, n.type AS node_type, n.name AS node_name, n.properties AS node_props,
70
70
  n.source_record_id AS node_src_rec, n.source_path AS node_src_path,
71
71
  n.created_at AS node_created, n.community AS node_community
72
- FROM edges e JOIN nodes n ON e.from_id = n.id WHERE e.to_id = ?`,n=[e];r&&(t+=` AND e.type = ?`,n.push(r)),t+=` LIMIT ?`,n.push(i);let c=this.query(t,n);for(let e of c)o.push(A(e)),s.has(e.node_id)||(s.add(e.node_id),a.push(j(e)))}return{nodes:a,edges:o}}async traverse(e,t){await this.ensureOpen();let n=t?.maxDepth??2,r=t?.direction??`both`,i=t?.edgeType,a=t?.limit??50,o=new Map,s=new Map,c=new Set,l=[{nodeId:e,depth:0}];for(;l.length>0&&o.size<a;){let e=l.shift();if(!e||c.has(e.nodeId)||e.depth>n)continue;c.add(e.nodeId);let t=await this.getNeighbors(e.nodeId,{direction:r,edgeType:i,limit:a-o.size});for(let r of t.nodes)o.has(r.id)||(o.set(r.id,r),e.depth+1<n&&l.push({nodeId:r.id,depth:e.depth+1}));for(let e of t.edges)s.set(e.id,e)}return{nodes:[...o.values()],edges:[...s.values()]}}async findNodes(e){await this.ensureOpen();let t=[],n=[];e.type&&(t.push(`type = ?`),n.push(e.type)),e.namePattern&&(t.push(`name LIKE ?`),n.push(`%${e.namePattern}%`)),e.sourcePath&&(t.push(`source_path = ?`),n.push(e.sourcePath));let r=t.length>0?`WHERE ${t.join(` AND `)}`:``,i=e.limit??100;return this.query(`SELECT * FROM nodes ${r} LIMIT ?`,[...n,i]).map(e=>O(e))}async findEdges(e){await this.ensureOpen();let t=[],n=[];e.type&&(t.push(`type = ?`),n.push(e.type)),e.fromId&&(t.push(`from_id = ?`),n.push(e.fromId)),e.toId&&(t.push(`to_id = ?`),n.push(e.toId));let r=t.length>0?`WHERE ${t.join(` AND `)}`:``,i=e.limit??100;return this.query(`SELECT * FROM edges ${r} LIMIT ?`,[...n,i]).map(e=>k(e))}async deleteNode(e){await this.ensureOpen();let t=this.getAdapter();t.exec(`BEGIN TRANSACTION`);try{this.run(`DELETE FROM edges WHERE from_id = ? OR to_id = ?`,[e,e]),this.run(`DELETE FROM nodes WHERE id = ?`,[e]),t.exec(`COMMIT`),t.flush()}catch(e){throw t.exec(`ROLLBACK`),e}}async deleteBySourcePath(e){await this.ensureOpen();let t=this.query(`SELECT id FROM nodes WHERE source_path = ?`,[e]);if(t.length===0)return 0;let n=this.getAdapter();n.exec(`BEGIN TRANSACTION`);try{for(let e of t)this.run(`DELETE FROM edges WHERE from_id = ? OR to_id = ?`,[e.id,e.id]);this.run(`DELETE FROM nodes WHERE source_path = ?`,[e]),n.exec(`COMMIT`),n.flush()}catch(e){throw n.exec(`ROLLBACK`),e}return t.length}async clear(){await this.ensureOpen();let e=this.getAdapter();e.exec(`BEGIN TRANSACTION`);try{this.run(`DELETE FROM process_steps`),this.run(`DELETE FROM processes`),this.run(`DELETE FROM edges`),this.run(`DELETE FROM nodes`),e.exec(`COMMIT`)}catch(t){throw e.exec(`ROLLBACK`),t}e.flush()}async getStats(){await this.ensureOpen();let e=this.query(`SELECT COUNT(*) as count FROM nodes`)[0]?.count??0,t=this.query(`SELECT COUNT(*) as count FROM edges`)[0]?.count??0,n=this.query(`SELECT type, COUNT(*) as count FROM nodes GROUP BY type`),r={};for(let e of n)r[e.type]=e.count;let i=this.query(`SELECT type, COUNT(*) as count FROM edges GROUP BY type`),a={};for(let e of i)a[e.type]=e.count;return{nodeCount:e,edgeCount:t,nodeTypes:r,edgeTypes:a}}async validate(){await this.ensureOpen();let e=await this.getStats(),t=this.query(`SELECT e.id AS edgeId,
72
+ FROM edges e JOIN nodes n ON e.from_id = n.id WHERE e.to_id = ?`,n=[e];r&&(t+=` AND e.type = ?`,n.push(r)),t+=` LIMIT ?`,n.push(i);let c=this.query(t,n);for(let e of c)o.push(N(e)),s.has(e.node_id)||(s.add(e.node_id),a.push(P(e)))}return{nodes:a,edges:o}}async traverse(e,t){await this.ensureOpen();let n=t?.maxDepth??2,r=t?.direction??`both`,i=t?.edgeType,a=t?.limit??50,o=new Map,s=new Map,c=new Set,l=[{nodeId:e,depth:0}];for(;l.length>0&&o.size<a;){let e=l.shift();if(!e||c.has(e.nodeId)||e.depth>n)continue;c.add(e.nodeId);let t=await this.getNeighbors(e.nodeId,{direction:r,edgeType:i,limit:a-o.size});for(let r of t.nodes)o.has(r.id)||(o.set(r.id,r),e.depth+1<n&&l.push({nodeId:r.id,depth:e.depth+1}));for(let e of t.edges)s.set(e.id,e)}return{nodes:[...o.values()],edges:[...s.values()]}}async findNodes(e){await this.ensureOpen();let t=[],n=[];e.type&&(t.push(`type = ?`),n.push(e.type)),e.namePattern&&(t.push(`name LIKE ?`),n.push(`%${e.namePattern}%`)),e.sourcePath&&(t.push(`source_path = ?`),n.push(e.sourcePath));let r=t.length>0?`WHERE ${t.join(` AND `)}`:``,i=e.limit??100;return this.query(`SELECT * FROM nodes ${r} LIMIT ?`,[...n,i]).map(e=>j(e))}async findEdges(e){await this.ensureOpen();let t=[],n=[];e.type&&(t.push(`type = ?`),n.push(e.type)),e.fromId&&(t.push(`from_id = ?`),n.push(e.fromId)),e.toId&&(t.push(`to_id = ?`),n.push(e.toId));let r=t.length>0?`WHERE ${t.join(` AND `)}`:``,i=e.limit??100;return this.query(`SELECT * FROM edges ${r} LIMIT ?`,[...n,i]).map(e=>M(e))}async deleteNode(e){await this.ensureOpen();let t=this.getAdapter();t.exec(`BEGIN TRANSACTION`);try{this.run(`DELETE FROM edges WHERE from_id = ? OR to_id = ?`,[e,e]),this.run(`DELETE FROM nodes WHERE id = ?`,[e]),t.exec(`COMMIT`),t.flush()}catch(e){throw t.exec(`ROLLBACK`),e}}async deleteBySourcePath(e){await this.ensureOpen();let t=this.query(`SELECT id FROM nodes WHERE source_path = ?`,[e]);if(t.length===0)return 0;let n=this.getAdapter();n.exec(`BEGIN TRANSACTION`);try{for(let e of t)this.run(`DELETE FROM edges WHERE from_id = ? OR to_id = ?`,[e.id,e.id]);this.run(`DELETE FROM nodes WHERE source_path = ?`,[e]),n.exec(`COMMIT`),n.flush()}catch(e){throw n.exec(`ROLLBACK`),e}return t.length}async clear(){await this.ensureOpen();let e=this.getAdapter();e.exec(`BEGIN TRANSACTION`);try{this.run(`DELETE FROM process_steps`),this.run(`DELETE FROM processes`),this.run(`DELETE FROM edges`),this.run(`DELETE FROM nodes`),e.exec(`COMMIT`)}catch(t){throw e.exec(`ROLLBACK`),t}e.flush()}async getStats(){await this.ensureOpen();let e=this.query(`SELECT COUNT(*) as count FROM nodes`)[0]?.count??0,t=this.query(`SELECT COUNT(*) as count FROM edges`)[0]?.count??0,n=this.query(`SELECT type, COUNT(*) as count FROM nodes GROUP BY type`),r={};for(let e of n)r[e.type]=e.count;let i=this.query(`SELECT type, COUNT(*) as count FROM edges GROUP BY type`),a={};for(let e of i)a[e.type]=e.count;return{nodeCount:e,edgeCount:t,nodeTypes:r,edgeTypes:a}}async validate(){await this.ensureOpen();let e=await this.getStats(),t=this.query(`SELECT e.id AS edgeId,
73
73
  CASE
74
74
  WHEN n1.id IS NULL THEN e.from_id
75
75
  WHEN n2.id IS NULL THEN e.to_id
@@ -87,7 +87,7 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
87
87
  VALUES (?, ?, ?, '{}', ?)`,[a,e,t,o]);for(let e=0;e<n.length;e++)this.run(`INSERT INTO process_steps (process_id, node_id, step_order) VALUES (?, ?, ?)`,[a,n[e],e]);s.exec(`COMMIT`),s.flush()}catch(e){throw s.exec(`ROLLBACK`),e}return{id:a,entryNodeId:e,label:t,properties:{},steps:n,createdAt:o}}async getProcesses(e){await this.ensureOpen();let t;t=e?this.query(`SELECT DISTINCT p.id, p.entry_node_id, p.label, p.properties, p.created_at
88
88
  FROM processes p
89
89
  JOIN process_steps ps ON p.id = ps.process_id
90
- WHERE ps.node_id = ?`,[e]):this.query(`SELECT * FROM processes`);let n=[];for(let e of t){let t=this.query(`SELECT node_id FROM process_steps WHERE process_id = ? ORDER BY step_order`,[e.id]);n.push({id:e.id,entryNodeId:e.entry_node_id,label:e.label,properties:D(e.properties),steps:t.map(e=>e.node_id),createdAt:e.created_at})}return n}async deleteProcess(e){await this.ensureOpen();let t=this.getAdapter();t.exec(`BEGIN TRANSACTION`);try{this.run(`DELETE FROM process_steps WHERE process_id = ?`,[e]),this.run(`DELETE FROM processes WHERE id = ?`,[e]),t.exec(`COMMIT`),t.flush()}catch(e){throw t.exec(`ROLLBACK`),e}}async depthGroupedTraversal(e,t=3,n){await this.ensureOpen();let r=n?.direction??`both`,i=n?.edgeType,a=n?.limit??100,o={},s=new Set;s.add(e);let c=[e];for(let e=1;e<=t;e++){let t=[],n=[];for(let e of c){let o=await this.getNeighbors(e,{direction:r,edgeType:i,limit:a});for(let e of o.nodes)s.has(e.id)||(s.add(e.id),t.push(e.id),n.push(e))}if(n.length>0&&(o[e]=n),c=t,c.length===0||s.size>=a)break}return o}async getCohesionScore(e){await this.ensureOpen();let t=this.query(`SELECT id FROM nodes WHERE community = ?`,[e]);if(t.length===0)return 0;let n=new Set(t.map(e=>e.id)),r=t.map(()=>`?`).join(`,`),i=t.map(e=>e.id),a=this.query(`SELECT from_id, to_id FROM edges WHERE from_id IN (${r}) OR to_id IN (${r})`,[...i,...i]);if(a.length===0)return 0;let o=0;for(let e of a)n.has(e.from_id)&&n.has(e.to_id)&&o++;return o/a.length}async getSymbol360(e){let t=await this.getNode(e);if(!t)throw Error(`Node '${e}' not found`);let n=await this.findEdges({toId:e}),r=await this.findEdges({fromId:e}),i=await this.getProcesses(e);return{node:t,incoming:n,outgoing:r,community:t.community??null,processes:i}}releaseMemory(){if(this.adapter)try{this.adapter.exec(`PRAGMA shrink_memory`),this.adapter.exec(`PRAGMA wal_checkpoint(TRUNCATE)`)}catch{}}async close(){this.adapter&&=(this.externalAdapter||this.adapter.close(),null)}};function D(e){if(!e)return{};try{return JSON.parse(e)}catch{return{}}}function O(e){return{id:e.id,type:e.type,name:e.name,properties:D(e.properties),sourceRecordId:e.source_record_id??void 0,sourcePath:e.source_path??void 0,createdAt:e.created_at,community:e.community??void 0}}function k(e){return{id:e.id,fromId:e.from_id,toId:e.to_id,type:e.type,weight:e.weight??1,confidence:e.confidence??1,properties:D(e.properties)}}function A(e){return{id:e.edge_id,fromId:e.from_id,toId:e.to_id,type:e.edge_type,weight:e.weight??1,confidence:e.edge_confidence??1,properties:D(e.edge_props??`{}`)}}function j(e){return{id:e.node_id,type:e.node_type,name:e.node_name,properties:D(e.node_props??`{}`),sourceRecordId:e.node_src_rec??void 0,sourcePath:e.node_src_path??void 0,createdAt:e.node_created,community:e.node_community??void 0}}function M(e){let t=0;for(let n=0;n<e.length;n++){let r=e[n]<0?-e[n]:e[n];r>t&&(t=r)}let n=new Int8Array(e.length);if(t===0)return Buffer.from(n.buffer,n.byteOffset,n.byteLength);let r=127/t;for(let t=0;t<e.length;t++){let i=Math.round(e[t]*r);n[t]=i<-127?-127:i>127?127:i}return Buffer.from(n.buffer,n.byteOffset,n.byteLength)}const N=/^[\w.\-/ ]+$/,P=p(`sqlite-vec-store`);function F(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function I(e,t){if(!N.test(e))throw Error(`Invalid ${t} filter value: contains disallowed characters`);return e}var L=class{adapter=null;externalAdapter;dbPath;embeddingDim;coarseDim;vectorEnabled=!1;ftsEnabled=!1;warnedVectorDisabled=!1;_draining=!1;_priorityQueue=[];_normalQueue=[];_onCloseHooks=[];constructor(e={}){this.embeddingDim=e.embeddingDim??u.dimensions,this.coarseDim=Math.min(128,this.embeddingDim),e.adapter?(this.adapter=e.adapter,this.externalAdapter=!0,this.dbPath=``):(this.dbPath=e.path??`${f.path}/aikit.db`,this.externalAdapter=!1)}async initialize(){if(!this.adapter){let e=s(this.dbPath);t(e)||n(e,{recursive:!0}),this.adapter=await w(this.dbPath)}this.vectorEnabled=this.adapter.vectorCapable,this.createKnowledgeTable(),this.createFtsTable(),this.vectorEnabled?this.ensureVecTable():this.warnedVectorDisabled||(this.warnedVectorDisabled=!0,P.warn(`SqliteVecStore: vector search disabled (sqlite-vec extension not loaded). Hybrid search will return FTS-only results.`))}getDiagnostics(){let e=this.adapter,t=e?e.constructor.name:`unknown`,n=null,r=this.externalAdapter?``:this.dbPath;if(r)try{let{statSync:e}=g(`node:fs`);n=e(r).size}catch{n=null}return{adapterType:t,vectorSearchEnabled:this.vectorEnabled,ftsEnabled:this.ftsEnabled,degradedMode:!this.vectorEnabled,dbPath:r,dbSizeBytes:n,embeddingDim:this.embeddingDim,vectorDtype:`int8`}}createKnowledgeTable(){let e=this.getAdapter();e.exec(`
90
+ WHERE ps.node_id = ?`,[e]):this.query(`SELECT * FROM processes`);let n=[];for(let e of t){let t=this.query(`SELECT node_id FROM process_steps WHERE process_id = ? ORDER BY step_order`,[e.id]);n.push({id:e.id,entryNodeId:e.entry_node_id,label:e.label,properties:A(e.properties),steps:t.map(e=>e.node_id),createdAt:e.created_at})}return n}async deleteProcess(e){await this.ensureOpen();let t=this.getAdapter();t.exec(`BEGIN TRANSACTION`);try{this.run(`DELETE FROM process_steps WHERE process_id = ?`,[e]),this.run(`DELETE FROM processes WHERE id = ?`,[e]),t.exec(`COMMIT`),t.flush()}catch(e){throw t.exec(`ROLLBACK`),e}}async depthGroupedTraversal(e,t=3,n){await this.ensureOpen();let r=n?.direction??`both`,i=n?.edgeType,a=n?.limit??100,o={},s=new Set;s.add(e);let c=[e];for(let e=1;e<=t;e++){let t=[],n=[];for(let e of c){let o=await this.getNeighbors(e,{direction:r,edgeType:i,limit:a});for(let e of o.nodes)s.has(e.id)||(s.add(e.id),t.push(e.id),n.push(e))}if(n.length>0&&(o[e]=n),c=t,c.length===0||s.size>=a)break}return o}async getCohesionScore(e){await this.ensureOpen();let t=this.query(`SELECT id FROM nodes WHERE community = ?`,[e]);if(t.length===0)return 0;let n=new Set(t.map(e=>e.id)),r=t.map(()=>`?`).join(`,`),i=t.map(e=>e.id),a=this.query(`SELECT from_id, to_id FROM edges WHERE from_id IN (${r}) OR to_id IN (${r})`,[...i,...i]);if(a.length===0)return 0;let o=0;for(let e of a)n.has(e.from_id)&&n.has(e.to_id)&&o++;return o/a.length}async getSymbol360(e){let t=await this.getNode(e);if(!t)throw Error(`Node '${e}' not found`);let n=await this.findEdges({toId:e}),r=await this.findEdges({fromId:e}),i=await this.getProcesses(e);return{node:t,incoming:n,outgoing:r,community:t.community??null,processes:i}}releaseMemory(){if(this.adapter)try{this.adapter.exec(`PRAGMA shrink_memory`),this.adapter.exec(`PRAGMA wal_checkpoint(TRUNCATE)`)}catch{}}async close(){this.adapter&&=(this.externalAdapter||this.adapter.close(),null)}};function A(e){if(!e)return{};try{return JSON.parse(e)}catch{return{}}}function j(e){return{id:e.id,type:e.type,name:e.name,properties:A(e.properties),sourceRecordId:e.source_record_id??void 0,sourcePath:e.source_path??void 0,createdAt:e.created_at,community:e.community??void 0}}function M(e){return{id:e.id,fromId:e.from_id,toId:e.to_id,type:e.type,weight:e.weight??1,confidence:e.confidence??1,properties:A(e.properties)}}function N(e){return{id:e.edge_id,fromId:e.from_id,toId:e.to_id,type:e.edge_type,weight:e.weight??1,confidence:e.edge_confidence??1,properties:A(e.edge_props??`{}`)}}function P(e){return{id:e.node_id,type:e.node_type,name:e.node_name,properties:A(e.node_props??`{}`),sourceRecordId:e.node_src_rec??void 0,sourcePath:e.node_src_path??void 0,createdAt:e.node_created,community:e.node_community??void 0}}function F(e){let t=0;for(let n=0;n<e.length;n++){let r=e[n]<0?-e[n]:e[n];r>t&&(t=r)}let n=new Int8Array(e.length);if(t===0)return Buffer.from(n.buffer,n.byteOffset,n.byteLength);let r=127/t;for(let t=0;t<e.length;t++){let i=Math.round(e[t]*r);n[t]=i<-127?-127:i>127?127:i}return Buffer.from(n.buffer,n.byteOffset,n.byteLength)}const I=/^[\w.\-/ ]+$/,L=p(`sqlite-vec-store`);function R(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function z(e,t){if(!I.test(e))throw Error(`Invalid ${t} filter value: contains disallowed characters`);return e}var B=class{adapter=null;externalAdapter;dbPath;embeddingDim;coarseDim;vectorEnabled=!1;ftsEnabled=!1;warnedVectorDisabled=!1;_draining=!1;_priorityQueue=[];_normalQueue=[];_onCloseHooks=[];constructor(e={}){this.embeddingDim=e.embeddingDim??u.dimensions,this.coarseDim=Math.min(128,this.embeddingDim),e.adapter?(this.adapter=e.adapter,this.externalAdapter=!0,this.dbPath=``):(this.dbPath=e.path??`${f.path}/aikit.db`,this.externalAdapter=!1)}async initialize(){if(!this.adapter){let e=s(this.dbPath);t(e)||n(e,{recursive:!0}),this.adapter=await D(this.dbPath)}this.vectorEnabled=this.adapter.vectorCapable,this.createKnowledgeTable(),this.createFtsTable(),this.vectorEnabled?this.ensureVecTable():this.warnedVectorDisabled||(this.warnedVectorDisabled=!0,L.warn(`SqliteVecStore: vector search disabled (sqlite-vec extension not loaded). Hybrid search will return FTS-only results.`))}getDiagnostics(){let e=this.adapter,t=e?e.constructor.name:`unknown`,n=null,r=this.externalAdapter?``:this.dbPath;if(r)try{let{statSync:e}=g(`node:fs`);n=e(r).size}catch{n=null}return{adapterType:t,vectorSearchEnabled:this.vectorEnabled,ftsEnabled:this.ftsEnabled,degradedMode:!this.vectorEnabled,dbPath:r,dbSizeBytes:n,embeddingDim:this.embeddingDim,vectorDtype:`int8`}}createKnowledgeTable(){let e=this.getAdapter();e.exec(`
91
91
  CREATE TABLE IF NOT EXISTS knowledge (
92
92
  id TEXT PRIMARY KEY,
93
93
  content TEXT NOT NULL,
@@ -112,7 +112,7 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
112
112
  content,
113
113
  tokenize = 'unicode61 remove_diacritics 2'
114
114
  )
115
- `),this.ftsEnabled=!0}catch(e){this.ftsEnabled=!1,P.warn(`FTS5 unavailable — keyword search disabled`,m(e))}}ensureVecTable(){let e=this.getAdapter(),t=e.queryAll(`SELECT name, sql FROM sqlite_master WHERE type='table' AND name='vec_knowledge'`);if(t.length>0){let n=t[0].sql??``,r=n.match(/(?:float|int8)\[(\d+)\]/i),i=r?Number(r[1]):null,a=/int8\[/i.test(n);(i!==null&&i!==this.embeddingDim||!a)&&(P.warn(`Vec table schema mismatch — dropping for recreation`,{existingDim:i,newDim:this.embeddingDim,wasInt8:a}),e.exec(`DROP TABLE vec_knowledge`))}e.exec(`
115
+ `),this.ftsEnabled=!0}catch(e){this.ftsEnabled=!1,L.warn(`FTS5 unavailable — keyword search disabled`,m(e))}}ensureVecTable(){let e=this.getAdapter(),t=e.queryAll(`SELECT name, sql FROM sqlite_master WHERE type='table' AND name='vec_knowledge'`);if(t.length>0){let n=t[0].sql??``,r=n.match(/(?:float|int8)\[(\d+)\]/i),i=r?Number(r[1]):null,a=/int8\[/i.test(n);(i!==null&&i!==this.embeddingDim||!a)&&(L.warn(`Vec table schema mismatch — dropping for recreation`,{existingDim:i,newDim:this.embeddingDim,wasInt8:a}),e.exec(`DROP TABLE vec_knowledge`))}e.exec(`
116
116
  CREATE VIRTUAL TABLE IF NOT EXISTS vec_knowledge USING vec0(
117
117
  embedding int8[${this.embeddingDim}] distance_metric=cosine,
118
118
  +knowledge_id TEXT
@@ -122,7 +122,7 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
122
122
  embedding int8[${this.coarseDim}] distance_metric=cosine,
123
123
  +knowledge_id TEXT
124
124
  )
125
- `)}enqueueWrite(e,t=!1){return new Promise((n,r)=>{let i=async()=>{try{n(await e())}catch(e){r(e)}};t?this._priorityQueue.push(i):this._normalQueue.push(i),this._drain()})}async _drain(){if(!this._draining){this._draining=!0;try{for(;this._priorityQueue.length>0||this._normalQueue.length>0;){let e=this._priorityQueue.shift()??this._normalQueue.shift();e&&await e()}}finally{this._draining=!1}}}async upsert(e,t){if(e.length!==0){if(e.length!==t.length)throw Error(`Record count (${e.length}) does not match vector count (${t.length})`);for(let n=0;n<t.length;n++)if(t[n].length===0)throw Error(`Zero-length vector at index ${n} for record ${e[n].sourcePath}`);return this.enqueueWrite(()=>this._upsertImpl(e,t))}}async upsertInteractive(e,t){if(e.length!==0){if(e.length!==t.length)throw Error(`Record count (${e.length}) does not match vector count (${t.length})`);for(let n=0;n<t.length;n++)if(t[n].length===0)throw Error(`Zero-length vector at index ${n} for record ${e[n].sourcePath}`);return this.enqueueWrite(()=>this._upsertImpl(e,t),!0)}}async upsertWithoutVector(e,t){return this.enqueueWrite(async()=>{let n=this.getAdapter(),r=e;n.exec(`BEGIN`);try{if(this.upsertKnowledgeRow(r),this.ftsEnabled&&(n.run(`DELETE FROM knowledge_fts WHERE id = ?`,[r.id]),n.run(`INSERT INTO knowledge_fts (id, content) VALUES (?, ?)`,[r.id,r.content])),this.vectorEnabled){let e=n.queryAll(`SELECT embedding FROM vec_knowledge WHERE knowledge_id = ?`,[t]);if(e.length>0){if(n.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[r.id]),n.run(`INSERT INTO vec_knowledge (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[e[0].embedding,r.id]),this.coarseDim<this.embeddingDim){let e=n.queryAll(`SELECT embedding FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[t]);e.length>0&&(n.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[r.id]),n.run(`INSERT INTO vec_knowledge_coarse (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[e[0].embedding,r.id]))}}else P.warn(`upsertWithoutVector: source vector not found, record will lack vector`,{recordId:r.id,sourceRecordId:t})}n.exec(`COMMIT`)}catch(e){try{n.exec(`ROLLBACK`)}catch{}throw e}n.flush()})}upsertKnowledgeRow(e){this.getAdapter().run(`INSERT INTO knowledge (id, content, sourcePath, contentType, headingPath, chunkIndex,
125
+ `)}enqueueWrite(e,t=!1){return new Promise((n,r)=>{let i=async()=>{try{n(await e())}catch(e){r(e)}};t?this._priorityQueue.push(i):this._normalQueue.push(i),this._drain()})}async _drain(){if(!this._draining){this._draining=!0;try{for(;this._priorityQueue.length>0||this._normalQueue.length>0;){let e=this._priorityQueue.shift()??this._normalQueue.shift();e&&await e()}}finally{this._draining=!1}}}async upsert(e,t){if(e.length!==0){if(e.length!==t.length)throw Error(`Record count (${e.length}) does not match vector count (${t.length})`);for(let n=0;n<t.length;n++)if(t[n].length===0)throw Error(`Zero-length vector at index ${n} for record ${e[n].sourcePath}`);return this.enqueueWrite(()=>this._upsertImpl(e,t))}}async upsertInteractive(e,t){if(e.length!==0){if(e.length!==t.length)throw Error(`Record count (${e.length}) does not match vector count (${t.length})`);for(let n=0;n<t.length;n++)if(t[n].length===0)throw Error(`Zero-length vector at index ${n} for record ${e[n].sourcePath}`);return this.enqueueWrite(()=>this._upsertImpl(e,t),!0)}}async upsertWithoutVector(e,t){return this.enqueueWrite(async()=>{let n=this.getAdapter(),r=e;n.exec(`BEGIN`);try{if(this.upsertKnowledgeRow(r),this.ftsEnabled&&(n.run(`DELETE FROM knowledge_fts WHERE id = ?`,[r.id]),n.run(`INSERT INTO knowledge_fts (id, content) VALUES (?, ?)`,[r.id,r.content])),this.vectorEnabled){let e=n.queryAll(`SELECT embedding FROM vec_knowledge WHERE knowledge_id = ?`,[t]);if(e.length>0){if(n.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[r.id]),n.run(`INSERT INTO vec_knowledge (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[e[0].embedding,r.id]),this.coarseDim<this.embeddingDim){let e=n.queryAll(`SELECT embedding FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[t]);e.length>0&&(n.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[r.id]),n.run(`INSERT INTO vec_knowledge_coarse (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[e[0].embedding,r.id]))}}else L.warn(`upsertWithoutVector: source vector not found, record will lack vector`,{recordId:r.id,sourceRecordId:t})}n.exec(`COMMIT`)}catch(e){try{n.exec(`ROLLBACK`)}catch{}throw e}n.flush()})}upsertKnowledgeRow(e){this.getAdapter().run(`INSERT INTO knowledge (id, content, sourcePath, contentType, headingPath, chunkIndex,
126
126
  totalChunks, startLine, endLine, fileHash, content_hash, indexedAt, origin, tags, category, version)
127
127
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
128
128
  ON CONFLICT(id) DO UPDATE SET
@@ -140,7 +140,7 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
140
140
  origin = excluded.origin,
141
141
  tags = excluded.tags,
142
142
  category = excluded.category,
143
- version = excluded.version`,[e.id,e.content,e.sourcePath,e.contentType,e.headingPath??``,e.chunkIndex,e.totalChunks,e.startLine,e.endLine,e.fileHash,e.contentHash??``,e.indexedAt,e.origin,JSON.stringify(e.tags??[]),e.category??``,e.version])}async _upsertImpl(e,t){let n=this.getAdapter();n.exec(`BEGIN`);try{for(let r=0;r<e.length;r++){let i=e[r];if(this.upsertKnowledgeRow(i),this.ftsEnabled&&(n.run(`DELETE FROM knowledge_fts WHERE id = ?`,[i.id]),n.run(`INSERT INTO knowledge_fts (id, content) VALUES (?, ?)`,[i.id,i.content])),this.vectorEnabled&&(n.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[i.id]),n.run(`INSERT INTO vec_knowledge (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[M(t[r]),i.id]),this.coarseDim<this.embeddingDim)){let e=t[r].subarray(0,this.coarseDim);n.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[i.id]),n.run(`INSERT INTO vec_knowledge_coarse (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[M(e),i.id])}}n.exec(`COMMIT`)}catch(e){try{n.exec(`ROLLBACK`)}catch{}throw e}n.flush()}async search(e,t){if(e.length===0)return[];if(!this.vectorEnabled)return this.warnedVectorDisabled||(this.warnedVectorDisabled=!0,P.warn(`search() called but vector backend is disabled — returning []`)),[];let n=this.getAdapter(),r=t?.limit??d.maxResults,i=t?.minScore??d.minScore,a=r*4,o=`
143
+ version = excluded.version`,[e.id,e.content,e.sourcePath,e.contentType,e.headingPath??``,e.chunkIndex,e.totalChunks,e.startLine,e.endLine,e.fileHash,e.contentHash??``,e.indexedAt,e.origin,JSON.stringify(e.tags??[]),e.category??``,e.version])}async _upsertImpl(e,t){let n=this.getAdapter();n.exec(`BEGIN`);try{for(let r=0;r<e.length;r++){let i=e[r];if(this.upsertKnowledgeRow(i),this.ftsEnabled&&(n.run(`DELETE FROM knowledge_fts WHERE id = ?`,[i.id]),n.run(`INSERT INTO knowledge_fts (id, content) VALUES (?, ?)`,[i.id,i.content])),this.vectorEnabled&&(n.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[i.id]),n.run(`INSERT INTO vec_knowledge (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[F(t[r]),i.id]),this.coarseDim<this.embeddingDim)){let e=t[r].subarray(0,this.coarseDim);n.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[i.id]),n.run(`INSERT INTO vec_knowledge_coarse (embedding, knowledge_id) VALUES (vec_int8(?), ?)`,[F(e),i.id])}}n.exec(`COMMIT`)}catch(e){try{n.exec(`ROLLBACK`)}catch{}throw e}n.flush()}async search(e,t){if(e.length===0)return[];if(!this.vectorEnabled)return this.warnedVectorDisabled||(this.warnedVectorDisabled=!0,L.warn(`search() called but vector backend is disabled — returning []`)),[];let n=this.getAdapter(),r=t?.limit??d.maxResults,i=t?.minScore??d.minScore,a=r*4,o=`
144
144
  SELECT k.*, v.distance AS _distance
145
145
  FROM (
146
146
  SELECT knowledge_id, distance
@@ -153,7 +153,7 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
153
153
  ${this.buildFilterSqlSuffix(t)}
154
154
  ORDER BY v.distance ASC
155
155
  LIMIT ?
156
- `,s;try{s=n.queryAll(o,[M(e),a,r])}catch(e){return P.warn(`vector search failed`,m(e)),[]}return s.map(e=>({record:this.fromRow(e),score:1-(e._distance??1)})).filter(e=>e.score>=i).slice(0,r)}async coarseSearch(e,t){if(!this.vectorEnabled||this.coarseDim>=this.embeddingDim)return this.search(e,t);let n=this.getAdapter(),r=t?.limit??d.maxResults,i=t?.minScore??d.minScore,a=r*4,o=e.subarray(0,this.coarseDim),s=`
156
+ `,s;try{s=n.queryAll(o,[F(e),a,r])}catch(e){return L.warn(`vector search failed`,m(e)),[]}return s.map(e=>({record:this.fromRow(e),score:1-(e._distance??1)})).filter(e=>e.score>=i).slice(0,r)}async coarseSearch(e,t){if(!this.vectorEnabled||this.coarseDim>=this.embeddingDim)return this.search(e,t);let n=this.getAdapter(),r=t?.limit??d.maxResults,i=t?.minScore??d.minScore,a=r*4,o=e.subarray(0,this.coarseDim),s=`
157
157
  SELECT k.*, v.distance AS _distance
158
158
  FROM (
159
159
  SELECT knowledge_id, distance
@@ -166,7 +166,7 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
166
166
  ${this.buildFilterSqlSuffix(t)}
167
167
  ORDER BY v.distance ASC
168
168
  LIMIT ?
169
- `,c;try{c=n.queryAll(s,[M(o),a,r])}catch(n){return P.warn(`coarse vector search failed, falling back to full search`,m(n)),this.search(e,t)}return c.length===0?this.search(e,t):c.map(e=>({record:this.fromRow(e),score:1-(e._distance??1)})).filter(e=>e.score>=i).slice(0,r)}async ftsSearch(e,t){if(!e||e.trim().length===0||!this.ftsEnabled)return[];let n=this.getAdapter(),r=t?.limit??d.maxResults,i=`
169
+ `,c;try{c=n.queryAll(s,[F(o),a,r])}catch(n){return L.warn(`coarse vector search failed, falling back to full search`,m(n)),this.search(e,t)}return c.length===0?this.search(e,t):c.map(e=>({record:this.fromRow(e),score:1-(e._distance??1)})).filter(e=>e.score>=i).slice(0,r)}async ftsSearch(e,t){if(!e||e.trim().length===0||!this.ftsEnabled)return[];let n=this.getAdapter(),r=t?.limit??d.maxResults,i=`
170
170
  SELECT k.*, bm25(knowledge_fts) AS _bm25
171
171
  FROM knowledge_fts
172
172
  JOIN knowledge k ON k.id = knowledge_fts.id
@@ -174,4 +174,4 @@ import{createRequire as e}from"node:module";import{existsSync as t,mkdirSync as
174
174
  ${this.buildFilterSqlSuffix(t,!0)}
175
175
  ORDER BY _bm25 ASC
176
176
  LIMIT ?
177
- `,a;try{let t=R(e);a=n.queryAll(i,[t,r])}catch(e){return P.warn(`fts search failed`,m(e)),[]}return a.map(e=>({record:this.fromRow(e),score:z(e._bm25)}))}async getById(e){let t=this.getAdapter().queryAll(`SELECT * FROM knowledge WHERE id = ? LIMIT 1`,[e]);return t.length===0?null:this.fromRow(t[0])}async deleteBySourcePath(e){return this.enqueueWrite(()=>this._deleteBySourcePathImpl(e))}async _deleteBySourcePathImpl(e){let t=this.getAdapter(),n=t.queryAll(`SELECT id FROM knowledge WHERE sourcePath = ?`,[e]);if(n.length===0)return 0;t.exec(`BEGIN`);try{for(let{id:e}of n)this.vectorEnabled&&(t.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[e]),this.coarseDim<this.embeddingDim&&t.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[e])),this.ftsEnabled&&t.run(`DELETE FROM knowledge_fts WHERE id = ?`,[e]);t.run(`DELETE FROM knowledge WHERE sourcePath = ?`,[e]),t.exec(`COMMIT`)}catch(e){try{t.exec(`ROLLBACK`)}catch{}throw e}return t.flush(),n.length}async deleteById(e){return this.enqueueWrite(()=>this._deleteByIdImpl(e))}async deleteByIdInteractive(e){return this.enqueueWrite(()=>this._deleteByIdImpl(e),!0)}async _deleteByIdImpl(e){let t=this.getAdapter();if(t.queryAll(`SELECT id FROM knowledge WHERE id = ? LIMIT 1`,[e]).length===0)return!1;t.exec(`BEGIN`);try{this.vectorEnabled&&(t.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[e]),this.coarseDim<this.embeddingDim&&t.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[e])),this.ftsEnabled&&t.run(`DELETE FROM knowledge_fts WHERE id = ?`,[e]),t.run(`DELETE FROM knowledge WHERE id = ?`,[e]),t.exec(`COMMIT`)}catch(e){try{t.exec(`ROLLBACK`)}catch{}throw e}return t.flush(),!0}async getBySourcePath(e){return this.getAdapter().queryAll(`SELECT * FROM knowledge WHERE sourcePath = ? ORDER BY chunkIndex ASC`,[e]).map(e=>this.fromRow(e))}async getStats(){let e=this.getAdapter(),t=e.queryAll(`SELECT COUNT(*) AS n FROM knowledge`)[0]?.n??0;if(t===0)return{totalRecords:0,totalFiles:0,contentTypeBreakdown:{},lastIndexedAt:null,storeBackend:`sqlite-vec`,embeddingModel:u.model};let n=e.queryAll(`SELECT COUNT(DISTINCT sourcePath) AS n FROM knowledge`),r=e.queryAll(`SELECT contentType, COUNT(*) AS n FROM knowledge GROUP BY contentType`),i=e.queryAll(`SELECT MAX(indexedAt) AS ts FROM knowledge`),a={};for(let e of r)a[e.contentType]=e.n;return{totalRecords:t,totalFiles:n[0]?.n??0,contentTypeBreakdown:a,lastIndexedAt:i[0]?.ts??null,storeBackend:`sqlite-vec`,embeddingModel:u.model}}async listSourcePaths(){return this.getAdapter().queryAll(`SELECT DISTINCT sourcePath FROM knowledge ORDER BY sourcePath`).map(e=>e.sourcePath)}async createFtsIndex(){this.createFtsTable()}async dropTable(){return this.enqueueWrite(()=>this._dropTableImpl())}async _dropTableImpl(){let e=this.getAdapter();this.ftsEnabled&&e.exec(`DROP TABLE IF EXISTS knowledge_fts`),this.vectorEnabled&&(this.coarseDim<this.embeddingDim&&e.exec(`DROP TABLE IF EXISTS vec_knowledge_coarse`),e.exec(`DROP TABLE IF EXISTS vec_knowledge`)),e.exec(`DROP TABLE IF EXISTS knowledge`),e.flush(),this.createKnowledgeTable(),this.createFtsTable(),this.vectorEnabled&&this.ensureVecTable()}releaseMemory(){if(this.adapter)try{this.adapter.exec(`PRAGMA shrink_memory`),this.adapter.exec(`PRAGMA wal_checkpoint(TRUNCATE)`)}catch{}}onBeforeClose(e){this._onCloseHooks.push(e)}async close(){for(let e of this._onCloseHooks)try{e()}catch{}for(this._onCloseHooks.length=0;this._priorityQueue.length>0||this._normalQueue.length>0||this._draining;)await new Promise(e=>setTimeout(e,5));this.adapter&&!this.externalAdapter&&this.adapter.close(),this.adapter=null}getAdapter(){if(!this.adapter)throw Error(`SqliteVecStore: not initialized — call initialize() first`);return this.adapter}buildFilterSqlSuffix(e,t=!1){if(!e)return``;let n=[];if(e.contentType&&n.push(`k.contentType = '${I(e.contentType,`contentType`)}'`),e.sourceType){let t=h(e.sourceType);if(t.length>0){let e=t.map(e=>`'${I(e,`sourceType`)}'`).join(`, `);n.push(`k.contentType IN (${e})`)}}if(e.origin&&n.push(`k.origin = '${I(e.origin,`origin`)}'`),e.category&&n.push(`k.category = '${I(e.category,`category`)}'`),e.tags&&e.tags.length>0){let t=e.tags.map(e=>`k.tags LIKE '%' || '${I(e,`tag`)}' || '%'`);n.push(`(${t.join(` OR `)})`)}if(n.length===0)return``;let r=n.join(` AND `);return t?`AND ${r}`:`WHERE ${r}`}fromRow(e){return{id:e.id,content:e.content,sourcePath:e.sourcePath,contentType:e.contentType,headingPath:e.headingPath||void 0,chunkIndex:e.chunkIndex,totalChunks:e.totalChunks,startLine:e.startLine,endLine:e.endLine,fileHash:e.fileHash,contentHash:e.content_hash||void 0,indexedAt:e.indexedAt,origin:e.origin,tags:F(e.tags),category:e.category||void 0,version:e.version}}};function R(e){let t=e.replace(/["'()*:^-]/g,` `).trim();return t?t.split(/\s+/).filter(e=>e.length>0).map(e=>`"${e}"`).join(` OR `):`""`}function z(e){return e==null||Number.isNaN(e)?0:1-Math.exp(-Math.abs(e)/5)}async function B(e){switch(e.backend){case`lancedb`:{let{LanceStore:t}=await import(`./lance-store-BIP1LEiS.js`);return new t({path:e.path})}case`sqlite-vec`:return new L({path:e.path,adapter:e.adapter,embeddingDim:e.embeddingDim});default:{let t=e.backend;throw Error(`Unknown store backend: "${t}". Supported: lancedb, sqlite-vec`)}}}export{E as SqliteGraphStore,L as SqliteVecStore,T as createSqlJsAdapter,w as createSqliteAdapter,B as createStore};
177
+ `,a;try{let t=V(e);a=n.queryAll(i,[t,r])}catch(e){return L.warn(`fts search failed`,m(e)),[]}return a.map(e=>({record:this.fromRow(e),score:H(e._bm25)}))}async getById(e){let t=this.getAdapter().queryAll(`SELECT * FROM knowledge WHERE id = ? LIMIT 1`,[e]);return t.length===0?null:this.fromRow(t[0])}async deleteBySourcePath(e){return this.enqueueWrite(()=>this._deleteBySourcePathImpl(e))}async _deleteBySourcePathImpl(e){let t=this.getAdapter(),n=t.queryAll(`SELECT id FROM knowledge WHERE sourcePath = ?`,[e]);if(n.length===0)return 0;t.exec(`BEGIN`);try{for(let{id:e}of n)this.vectorEnabled&&(t.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[e]),this.coarseDim<this.embeddingDim&&t.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[e])),this.ftsEnabled&&t.run(`DELETE FROM knowledge_fts WHERE id = ?`,[e]);t.run(`DELETE FROM knowledge WHERE sourcePath = ?`,[e]),t.exec(`COMMIT`)}catch(e){try{t.exec(`ROLLBACK`)}catch{}throw e}return t.flush(),n.length}async deleteById(e){return this.enqueueWrite(()=>this._deleteByIdImpl(e))}async deleteByIdInteractive(e){return this.enqueueWrite(()=>this._deleteByIdImpl(e),!0)}async _deleteByIdImpl(e){let t=this.getAdapter();if(t.queryAll(`SELECT id FROM knowledge WHERE id = ? LIMIT 1`,[e]).length===0)return!1;t.exec(`BEGIN`);try{this.vectorEnabled&&(t.run(`DELETE FROM vec_knowledge WHERE knowledge_id = ?`,[e]),this.coarseDim<this.embeddingDim&&t.run(`DELETE FROM vec_knowledge_coarse WHERE knowledge_id = ?`,[e])),this.ftsEnabled&&t.run(`DELETE FROM knowledge_fts WHERE id = ?`,[e]),t.run(`DELETE FROM knowledge WHERE id = ?`,[e]),t.exec(`COMMIT`)}catch(e){try{t.exec(`ROLLBACK`)}catch{}throw e}return t.flush(),!0}async getBySourcePath(e){return this.getAdapter().queryAll(`SELECT * FROM knowledge WHERE sourcePath = ? ORDER BY chunkIndex ASC`,[e]).map(e=>this.fromRow(e))}async getStats(){let e=this.getAdapter(),t=e.queryAll(`SELECT COUNT(*) AS n FROM knowledge`)[0]?.n??0;if(t===0)return{totalRecords:0,totalFiles:0,contentTypeBreakdown:{},lastIndexedAt:null,storeBackend:`sqlite-vec`,embeddingModel:u.model};let n=e.queryAll(`SELECT COUNT(DISTINCT sourcePath) AS n FROM knowledge`),r=e.queryAll(`SELECT contentType, COUNT(*) AS n FROM knowledge GROUP BY contentType`),i=e.queryAll(`SELECT MAX(indexedAt) AS ts FROM knowledge`),a={};for(let e of r)a[e.contentType]=e.n;return{totalRecords:t,totalFiles:n[0]?.n??0,contentTypeBreakdown:a,lastIndexedAt:i[0]?.ts??null,storeBackend:`sqlite-vec`,embeddingModel:u.model}}async listSourcePaths(){return this.getAdapter().queryAll(`SELECT DISTINCT sourcePath FROM knowledge ORDER BY sourcePath`).map(e=>e.sourcePath)}async createFtsIndex(){this.createFtsTable()}async dropTable(){return this.enqueueWrite(()=>this._dropTableImpl())}async _dropTableImpl(){let e=this.getAdapter();this.ftsEnabled&&e.exec(`DROP TABLE IF EXISTS knowledge_fts`),this.vectorEnabled&&(this.coarseDim<this.embeddingDim&&e.exec(`DROP TABLE IF EXISTS vec_knowledge_coarse`),e.exec(`DROP TABLE IF EXISTS vec_knowledge`)),e.exec(`DROP TABLE IF EXISTS knowledge`),e.flush(),this.createKnowledgeTable(),this.createFtsTable(),this.vectorEnabled&&this.ensureVecTable()}releaseMemory(){if(this.adapter)try{this.adapter.exec(`PRAGMA shrink_memory`),this.adapter.exec(`PRAGMA wal_checkpoint(TRUNCATE)`)}catch{}}onBeforeClose(e){this._onCloseHooks.push(e)}async close(){for(let e of this._onCloseHooks)try{e()}catch{}for(this._onCloseHooks.length=0;this._priorityQueue.length>0||this._normalQueue.length>0||this._draining;)await new Promise(e=>setTimeout(e,5));this.adapter&&!this.externalAdapter&&this.adapter.close(),this.adapter=null}getAdapter(){if(!this.adapter)throw Error(`SqliteVecStore: not initialized — call initialize() first`);return this.adapter}buildFilterSqlSuffix(e,t=!1){if(!e)return``;let n=[];if(e.contentType&&n.push(`k.contentType = '${z(e.contentType,`contentType`)}'`),e.sourceType){let t=h(e.sourceType);if(t.length>0){let e=t.map(e=>`'${z(e,`sourceType`)}'`).join(`, `);n.push(`k.contentType IN (${e})`)}}if(e.origin&&n.push(`k.origin = '${z(e.origin,`origin`)}'`),e.category&&n.push(`k.category = '${z(e.category,`category`)}'`),e.tags&&e.tags.length>0){let t=e.tags.map(e=>`k.tags LIKE '%' || '${z(e,`tag`)}' || '%'`);n.push(`(${t.join(` OR `)})`)}if(n.length===0)return``;let r=n.join(` AND `);return t?`AND ${r}`:`WHERE ${r}`}fromRow(e){return{id:e.id,content:e.content,sourcePath:e.sourcePath,contentType:e.contentType,headingPath:e.headingPath||void 0,chunkIndex:e.chunkIndex,totalChunks:e.totalChunks,startLine:e.startLine,endLine:e.endLine,fileHash:e.fileHash,contentHash:e.content_hash||void 0,indexedAt:e.indexedAt,origin:e.origin,tags:R(e.tags),category:e.category||void 0,version:e.version}}};function V(e){let t=e.replace(/["'()*:^-]/g,` `).trim();return t?t.split(/\s+/).filter(e=>e.length>0).map(e=>`"${e}"`).join(` OR `):`""`}function H(e){return e==null||Number.isNaN(e)?0:1-Math.exp(-Math.abs(e)/5)}async function U(e){switch(e.backend){case`lancedb`:{let{LanceStore:t}=await import(`./lance-store-BIP1LEiS.js`);return new t({path:e.path})}case`sqlite-vec`:return new B({path:e.path,adapter:e.adapter,embeddingDim:e.embeddingDim});default:{let t=e.backend;throw Error(`Unknown store backend: "${t}". Supported: lancedb, sqlite-vec`)}}}export{k as SqliteGraphStore,B as SqliteVecStore,O as createSqlJsAdapter,D as createSqliteAdapter,U as createStore};
@@ -2,6 +2,8 @@ const e=()=>"**`AGENTS.md` is already in your context** — do NOT re-read it. J
2
2
 
3
3
  ## Bootstrap (before any work)
4
4
 
5
+ > **HARD RULE:** Your FIRST ACTION in EVERY session MUST be \`status({})\`. No exceptions. This ensures tool availability, workspace awareness, and index state before any other operation. Skipping this causes tool avoidance and degraded performance.
6
+
5
7
  1. \`status({})\` — if onboard ❌ → \`onboard({ path: "." })\`, wait for completion, note **Onboard Directory**
6
8
  2. Read onboard artifacts: \`compact({ path: "<Onboard Dir>/synthesis-guide.md" })\`, \`structure.md\`, \`code-map.md\`
7
9
  3. Read \`aikit\` skill, check \`AGENTS.md\` (decision protocol and FORGE protocol are inlined below)
@@ -68,6 +68,8 @@ Use AI Kit retrieval and compression tools first. Prefer reusable compressed con
68
68
  | \`run_in_terminal\` for test | \`test_run({})\` | Run tests with structured output |
69
69
  | Editing without reading | \`file_summary\` then targeted \`read_file\` | Prevents wrong-position edits |
70
70
 
71
+ > **Path Note:** \`compact({path})\` and \`file_summary({path})\` accept ANY absolute path — not just indexed workspace files. They read the file directly from disk. Use them freely for cross-workspace and cross-repository file access without needing to index the target workspace first.
72
+
71
73
  **\`read_file\` is ONLY acceptable when you need exact line content FOR EDITING (before \`replace_string_in_file\`).**
72
74
 
73
75
  For edits, first understand structure with \`file_summary\` or \`compact\`, then use targeted \`read_file\` only for the exact region.
@@ -1 +1 @@
1
- function e(){return[{file:`SKILL.md`,content:'---\nname: present\ndescription: "Use the AI Kit `present` tool to display rich interactive dashboards, charts, timelines, status boards, and data visualizations in the browser or in-chat. Covers all block types, chart types, actions, composition patterns, and MCP Apps templates (list-sort, data-table, picker, flame-graph, form, checklist, document, report, error, timeline, kanban, tree, diff-view, dashboard, status-board). Load this skill before calling the present tool to ensure professional output."\nmetadata:\n category: cross-cutting\n domain: general\n applicability: always\n inputs: [data, analysis]\n outputs: [dashboards, charts, reports]\n requires: [aikit]\nargument-hint: "Content to present — data, analysis results, status report, comparison, or interactive MCP App"\n---\n\n# Present Tool — Rich Interactive Dashboards\n\n## 1. API Shape\n\n- `schemaVersion: 1` is always required.\n- `title: string` is always required; do not use unicode escapes like `\\u2014`, use the actual character.\n- Optional layout fields: `layout?: { maxWidth?: string, padding?: string, columns?: number }`.\n- Optional appearance field: `colorScheme?: z.enum([\'light\', \'dark\', \'auto\'])`.\n- Interactive surfaces can also include `response?: { timeout?: number, required?: boolean }`.\n- Mode 1: block surface with `blocks: [{ type, title?, value }]`.\n- Mode 2: MCP App template with `template` + `data`.\n- Mode 3: viewer template with `template` + `data` for richer diagrams or flows.\n- No `actions` array means inline MCP App transport in the host client (except browser-only templates which auto-route to browser).\n- Any `actions` array means browser transport and opens the system browser. Valid action types: `button | select | multi-select | form-submit | text-submit | confirm | custom`.\n- CLI has no inline host; add a minimal action if you need the browser to open.\n\n```json\n{ "schemaVersion": 1, "title": "Release Review", "blocks": [{ "type": "metrics", "value": [] }] }\n```\n\n## 2. Content Blocks\n\nPass content as a `blocks` array of `{ type, title?, value }` objects.\n\n| Type | Value shape | Use for |\n|------|-------------|---------|\n| `prompt` | string | Prompt or callout text |\n| `text` | string | Plain text without markdown parsing |\n| `heading` | string | Standalone section headings |\n| `paragraph` | string | Standalone paragraph copy |\n| `markdown` | string | Prose, headings, code snippets |\n| `table` | `Record[]` or `{ headers, rows }` | Tabular data |\n| `chart` | `{ chartType, data, xKey?, yKeys? }` | `line | area | bar | horizontal-bar | pie | donut | sparkline | heatmap` |\n| `metrics` | `[{ label, value, trend?, status? }]` | KPI cards |\n| `cards` | `[{ title, body?, badge?, status? }]` | Summary cards |\n| `mermaid` | string | Diagrams (works in both mcp-app and browser) |\n| `tree` | `{ name, children? }` | Hierarchies |\n| `timeline` | `[{ title, description?, timestamp?, status? }]` | Event sequences |\n| `checklist` | `[{ label, checked }]` | Task lists |\n| `code` | string | Code or diff excerpts |\n| `comparison` | `[{title, items}]` or `{columns: [{title, items}]}` | Side-by-side option comparisons |\n| `status-board` | `[{category, items}]` or `{items: [{category, items}]}` | Service health boards |\n| `progress` | `{label, value, max?}` or `{items: [{label, value, max?, color?}]}` | Progress bars |\n| `graph` | `{ nodes: [{id, label?}], edges: [{from, to, label?}] }` | Network graphs |\n| `docs-browser` | `{ files: [{path, title?, content?, status?}], title? }` | Documentation indexes |\n| `actions` | `[{ type, id, label, variant?, options? }]` | Inline action group using the same action enum as top-level `actions` |\n| `separator` | none | Visual divider between block groups |\n\n> Anti-patterns:\n> - No tables in `markdown`; use a `table` block.\n> - Use `{ chartType, data, xKey, yKeys }`, not Chart.js `{ labels, datasets }`.\n> - Keep `markdown` for prose, headings, and code, not UI structure.\n\n## 3. Actions\n\nAction shape: `{ type, id, label, variant?, options? }`\n\nValid action types: `button | select | multi-select | form-submit | text-submit | confirm | custom`\n\n\n```json\n{ "actions": [{ "type": "button", "id": "ack", "label": "Acknowledge", "variant": "primary" }] }\n```\n\n## 4. MCP App Templates\n\nUse templates for structured interactive widgets. They take `template` + `data`; returns depend on the widget.\n\n| Template | Purpose | Data shape | Returns |\n|----------|---------|------------|---------|\n| `list-sort` | Reorder a ranked list | `{ items: [{ id, label }] }` | Reordered items or IDs |\n| `data-table` | Inspect structured rows | `{ columns, rows, stats? }` | Current selection or filtered rows |\n| `picker` | Choose items from categories | `{ categories, items }` | Selected item IDs |\n| `flame-graph` | Explore hierarchical totals | `{ profile }` | Clicked node |\n| `form` | Collect field values | `{ fields }` | Submitted field map |\n| `checklist` | Run an interactive checklist | Template-specific object | Checked state or submission payload |\n| `document` | Present structured long-form content | Template-specific object | Display only |\n| `report` | Present synthesized report content | Template-specific object | Display only |\n| `error` | Show structured error states and recovery actions | Template-specific object | Display only |\n| `kanban` | Move cards across columns | `{ columns, cards }` | Card move payload |\n| `timeline` | Show ordered milestones | `{ events }` | Display only |\n| `tree` | Browse nested nodes | `{ root }` | Display only |\n| `diff-view` | Show file diffs | `{ files, stats? }` | Display only |\n| `dashboard` | Show metric cards | `{ metrics }` | Display only |\n| `status-board` | Show grouped status or health views | Template-specific object | Display only |\n\n> **Auto-routing:** Browser-only templates (form, kanban, flame-graph, picker, list-sort) auto-route to browser transport even without explicit `actions`. No need to add a dummy action — the tool handles it.\n\n### `list-sort`\n`data`: `{ "items": [{ "id": "task-1", "label": "Fix auth bug" }] }`\n\n### `data-table`\n`data`: `{ "columns": [{ "key": "file", "label": "File" }], "rows": [{ "file": "auth.ts" }], "stats": [{ "label": "Files", "value": "1" }] }`\n\n### `picker`\n`data`: `{ "categories": [{ "id": "core", "label": "Core" }], "items": [{ "id": "auth", "label": "Auth", "category": "core", "tags": ["security"] }] }`\n\n### `flame-graph`\n`data`: `{ "profile": { "name": "root", "total": 100, "children": [{ "name": "packages/server", "total": 60 }] } }`\n\n### `form`\n`data`: `{ "fields": [{ "name": "projectName", "label": "Project Name", "type": "text", "value": "my-app" }] }`\n\n### `kanban`\n`data`: `{ "columns": [{ "id": "todo", "label": "To Do" }], "cards": [{ "id": "c1", "title": "Fix auth bug", "column": "todo" }] }`\n\n### `timeline`\n`data`: `{ "events": [{ "title": "v1.0 Release", "timestamp": "2026-05", "status": "complete" }] }`\n\nThe timeline template also accepts `entries` or `items` as the array field name (all three are equivalent).\n\n### `tree`\n`data`: `{ "root": { "label": "packages/", "children": [{ "label": "server/" }] } }`\n\n### `diff-view`\n`data`: `{ "files": [{ "path": "auth.ts", "status": "modified", "hunks": [{ "header": "@@ -1 +1 @@", "changes": [{ "type": "add", "content": "const ok = true;" }] }] }], "stats": { "filesChanged": 1 } }`\n\n### `dashboard`\n`data`: `{ "metrics": [{ "label": "Uptime", "value": "99.9%", "status": "success" }] }`\n\n## 5. Viewer Templates\n\nViewer templates render richer visual surfaces from `template` + `data`.\n\n| Template | Transport | Purpose |\n|----------|-----------|---------|\n| `c4-static@1` | mcp-app | Static architecture diagram |\n| `c4@1` | browser | Interactive architecture diagram |\n| `process-flow-static@1` | mcp-app | Static process flow |\n| `process-flow@1` | browser | Interactive process flow |\n| `tour@1` | browser | Guided walkthrough or code tour |\n| `task-plan-static@1` | mcp-app | Static task execution plan with phases, batches, agent assignments |\n| `task-plan@1` | browser | Interactive task plan with ReactFlow — drag/zoom/pan, dependency edges, ELK layout |\n\n`c4`: `{ "nodes": [{ "id": "web", "type": "container", "label": "Web App", "technology?": "React" }], "edges": [{ "source": "web", "target": "api", "label": "HTTPS" }] }`\n\n`process-flow`: `{ "nodes": [{ "id": "start", "label": "Start", "type": "start-end" }], "edges": [{ "source": "start", "target": "review", "type?": "standard" }] }`\n\nNode types: `start-end` | `manual` | `automated` | `integration` | `decision` | `prerequisite`. Default: `manual`.\nEdge types: `standard` | `loop-back` | `exception`. Default: `standard`. `loop-back` and `exception` edges animate.\n\n`tour`: `{ "steps": [{ "id": "overview", "title": "Overview", "file": "src/index.ts", "explanation": "Entry point", "code?": "...", "line?": 1 }] }`\n\n`task-plan`: `{ "title": "Feature Name", "description?": "Optional", "phases": [{ "id": "p1", "label": "Phase 1", "outcome?": "...", "batches": [{ "id": "b1", "order": 1, "parallel": true, "label?": "Parallel Research", "tasks": [{ "id": "t1", "title": "Research auth", "agent": "Researcher-Alpha", "files?": ["src/auth/"], "status?": "done", "description?": "...", "dependsOn?": [] }] }] }] }`\n\nTask status values: `pending` | `in-progress` | `done` | `blocked`. Default: `pending`.\n\nGotcha: edges use `source` and `target`, not `from` and `to`.\n\nDecision tree:\n- Architecture, systems, containers, deployments: load the `c4-architecture` skill and use `c4@1` or `c4-static@1`.\n- Stateful workflows, approvals, or branching processes: use `process-flow@1` or `process-flow-static@1`.\n- Agent task decomposition, execution plans, or parallel batch visualization: use `task-plan@1` or `task-plan-static@1`.\n- Ordered walkthroughs, onboarding, or narrated analysis: use `tour@1`.\n\n## 6. Rules\n\n- Pick transport once: no `actions` for inline, any `actions` for browser.\n- Combine block types in one `blocks` array; metrics + chart + table + markdown is the normal composition pattern.\n- Use concise schemas in docs; do not expand to full `present()` calls unless behavior depends on them.\n- Use `table`, `comparison`, and `status-board` for structured data instead of formatting it inside markdown.\n- Architecture requests are delegated: load the `c4-architecture` skill for diagram guidance and data contracts.\n- If inline rendering only shows a title, raw JSON, or fallback text, switch to browser transport with a minimal action. (Browser-only templates now auto-route, so this mainly applies to custom block surfaces.)\n- For large or mixed-content surfaces, browser transport is usually easier to review and more reliable.\n- For custom HTML demos or visual prototypes, do NOT use the "html" block type (renders as raw text inline). Instead, write an HTML file, serve it locally (e.g. "npx serve" or "python -m http.server"), then open with the AI Kit "browser" tool: browser({ action: "open", url: "http://localhost:PORT/file.html", mode: "ui" }). This gives a controlled Chromium instance the LLM can read/interact with. Avoid "Start-Process" (opens uncontrolled system browser).\n'}]}export{e as default};
1
+ function e(){return[{file:`SKILL.md`,content:'---\nname: present\ndescription: "Use the AI Kit `present` tool to display rich interactive dashboards, charts, timelines, status boards, and data visualizations in the browser or in-chat. Covers all block types, chart types, actions, composition patterns, and MCP Apps templates (list-sort, data-table, picker, flame-graph, form, checklist, document, report, error, timeline, kanban, tree, diff-view, dashboard, status-board). Load this skill before calling the present tool to ensure professional output."\nmetadata:\n category: cross-cutting\n domain: general\n applicability: always\n inputs: [data, analysis]\n outputs: [dashboards, charts, reports]\n requires: [aikit]\nargument-hint: "Content to present — data, analysis results, status report, comparison, or interactive MCP App"\n---\n\n# Present Tool — Rich Interactive Dashboards\n\n## 1. API Shape\n\n- `schemaVersion: 1` is always required.\n- `title: string` is always required; do not use unicode escapes like `\\u2014`, use the actual character.\n- Optional layout fields: `layout?: { maxWidth?: string, padding?: string, columns?: number }`.\n- Optional appearance field: `colorScheme?: z.enum([\'light\', \'dark\', \'auto\'])`.\n- Interactive surfaces can also include `response?: { timeout?: number, required?: boolean }`.\n- Mode 1: block surface with `blocks: [{ type, title?, value }]`.\n- Mode 2: MCP App template with `template` + `data`.\n- Mode 3: viewer template with `template` + `data` for richer diagrams or flows.\n- No `actions` array means inline MCP App transport in the host client (except browser-only templates which auto-route to browser).\n- Any `actions` array means browser transport and opens the system browser. Valid action types: `button | select | multi-select | form-submit | text-submit | confirm | custom`.\n- CLI has no inline host; add a minimal action if you need the browser to open.\n\n```json\n{ "schemaVersion": 1, "title": "Release Review", "blocks": [{ "type": "metrics", "value": [] }] }\n```\n\n## 2. Content Blocks\n\nPass content as a `blocks` array of `{ type, title?, value }` objects.\n\n| Type | Value shape | Use for |\n|------|-------------|---------|\n| `prompt` | string | Prompt or callout text |\n| `text` | string | Plain text without markdown parsing |\n| `heading` | string | Standalone section headings |\n| `paragraph` | string | Standalone paragraph copy |\n| `markdown` | string | Prose, headings, code snippets |\n| `table` | `Record[]` or `{ headers, rows }` | Tabular data |\n| `chart` | `{ chartType, data, xKey?, yKeys? }` | `line | area | bar | horizontal-bar | pie | donut | sparkline | heatmap` |\n| `metrics` | `[{ label, value, trend?, status? }]` | KPI cards |\n| `cards` | `[{ title, body?, badge?, status? }]` | Summary cards |\n| `mermaid` | string | Diagrams (works in both mcp-app and browser) |\n| `tree` | `{ name, children? }` | Hierarchies |\n| `timeline` | `[{ title, description?, timestamp?, status? }]` | Event sequences |\n| `checklist` | `[{ label, checked }]` | Task lists |\n| `code` | string | Code or diff excerpts |\n| `comparison` | `[{title, items}]` or `{columns: [{title, items}]}` | Side-by-side option comparisons |\n| `status-board` | `[{category, items}]` or `{items: [{category, items}]}` | Service health boards |\n| `progress` | `{label, value, max?}` or `{items: [{label, value, max?, color?}]}` | Progress bars |\n| `graph` | `{ nodes: [{id, label?}], edges: [{from, to, label?}] }` | Network graphs |\n| `docs-browser` | `{ files: [{path, title?, content?, status?}], title? }` | Documentation indexes |\n| `actions` | `[{ type, id, label, variant?, options? }]` | Inline action group using the same action enum as top-level `actions` |\n| `separator` | none | Visual divider between block groups |\n\n> Anti-patterns:\n> - Prefer `table` blocks for tabular data (pipe tables in `markdown` are auto-normalized, but explicit table blocks are clearer and more efficient).\n> - Use `{ chartType, data, xKey, yKeys }`, not Chart.js `{ labels, datasets }`.\n> - Keep `markdown` for prose, headings, and code, not UI structure.\n\n## 3. Actions\n\nAction shape: `{ type, id, label, variant?, options? }`\n\nValid action types: `button | select | multi-select | form-submit | text-submit | confirm | custom`\n\n\n```json\n{ "actions": [{ "type": "button", "id": "ack", "label": "Acknowledge", "variant": "primary" }] }\n```\n\n## 4. MCP App Templates\n\nUse templates for structured interactive widgets. They take `template` + `data`; returns depend on the widget.\n\n| Template | Purpose | Data shape | Returns |\n|----------|---------|------------|---------|\n| `list-sort` | Reorder a ranked list | `{ items: [{ id, label }] }` | Reordered items or IDs |\n| `data-table` | Inspect structured rows | `{ columns, rows, stats? }` | Current selection or filtered rows |\n| `picker` | Choose items from categories | `{ categories, items }` | Selected item IDs |\n| `flame-graph` | Explore hierarchical totals | `{ profile }` | Clicked node |\n| `form` | Collect field values | `{ fields }` | Submitted field map |\n| `checklist` | Run an interactive checklist | Template-specific object | Checked state or submission payload |\n| `document` | Present structured long-form content | Template-specific object | Display only |\n| `report` | Present synthesized report content | Template-specific object | Display only |\n| `error` | Show structured error states and recovery actions | Template-specific object | Display only |\n| `kanban` | Move cards across columns | `{ columns, cards }` | Card move payload |\n| `timeline` | Show ordered milestones | `{ events }` | Display only |\n| `tree` | Browse nested nodes | `{ root }` | Display only |\n| `diff-view` | Show file diffs | `{ files, stats? }` | Display only |\n| `dashboard` | Show metric cards | `{ metrics }` | Display only |\n| `status-board` | Show grouped status or health views | Template-specific object | Display only |\n\n> **Auto-routing:** Browser-only templates (form, kanban, flame-graph, picker, list-sort) auto-route to browser transport even without explicit `actions`. No need to add a dummy action — the tool handles it.\n\n### `list-sort`\n`data`: `{ "items": [{ "id": "task-1", "label": "Fix auth bug" }] }`\n\n### `data-table`\n`data`: `{ "columns": [{ "key": "file", "label": "File" }], "rows": [{ "file": "auth.ts" }], "stats": [{ "label": "Files", "value": "1" }] }`\n\n### `picker`\n`data`: `{ "categories": [{ "id": "core", "label": "Core" }], "items": [{ "id": "auth", "label": "Auth", "category": "core", "tags": ["security"] }] }`\n\n### `flame-graph`\n`data`: `{ "profile": { "name": "root", "total": 100, "children": [{ "name": "packages/server", "total": 60 }] } }`\n\n### `form`\n`data`: `{ "fields": [{ "name": "projectName", "label": "Project Name", "type": "text", "value": "my-app" }] }`\n\n### `kanban`\n`data`: `{ "columns": [{ "id": "todo", "label": "To Do" }], "cards": [{ "id": "c1", "title": "Fix auth bug", "column": "todo" }] }`\n\n### `timeline`\n`data`: `{ "events": [{ "title": "v1.0 Release", "timestamp": "2026-05", "status": "complete" }] }`\n\nThe timeline template also accepts `entries` or `items` as the array field name (all three are equivalent).\n\n### `tree`\n`data`: `{ "root": { "label": "packages/", "children": [{ "label": "server/" }] } }`\n\n### `diff-view`\n`data`: `{ "files": [{ "path": "auth.ts", "status": "modified", "hunks": [{ "header": "@@ -1 +1 @@", "changes": [{ "type": "add", "content": "const ok = true;" }] }] }], "stats": { "filesChanged": 1 } }`\n\n### `dashboard`\n`data`: `{ "metrics": [{ "label": "Uptime", "value": "99.9%", "status": "success" }] }`\n\n## 5. Viewer Templates\n\nViewer templates render richer visual surfaces from `template` + `data`.\n\n| Template | Transport | Purpose |\n|----------|-----------|---------|\n| `c4-static@1` | mcp-app | Static architecture diagram |\n| `c4@1` | browser | Interactive architecture diagram |\n| `process-flow-static@1` | mcp-app | Static process flow |\n| `process-flow@1` | browser | Interactive process flow |\n| `tour@1` | browser | Guided walkthrough or code tour |\n| `task-plan-static@1` | mcp-app | Static task execution plan with phases, batches, agent assignments |\n| `task-plan@1` | browser | Interactive task plan with ReactFlow — drag/zoom/pan, dependency edges, ELK layout |\n\n`c4`: `{ "nodes": [{ "id": "web", "type": "container", "label": "Web App", "technology?": "React" }], "edges": [{ "source": "web", "target": "api", "label": "HTTPS" }] }`\n\n`process-flow`: `{ "nodes": [{ "id": "start", "label": "Start", "type": "start-end" }], "edges": [{ "source": "start", "target": "review", "type?": "standard" }] }`\n\nNode types: `start-end` | `manual` | `automated` | `integration` | `decision` | `prerequisite`. Default: `manual`.\nEdge types: `standard` | `loop-back` | `exception`. Default: `standard`. `loop-back` and `exception` edges animate.\n\n`tour`: `{ "steps": [{ "id": "overview", "title": "Overview", "file": "src/index.ts", "explanation": "Entry point", "code?": "...", "line?": 1 }] }`\n\n`task-plan`: `{ "title": "Feature Name", "description?": "Optional", "phases": [{ "id": "p1", "label": "Phase 1", "outcome?": "...", "batches": [{ "id": "b1", "order": 1, "parallel": true, "label?": "Parallel Research", "tasks": [{ "id": "t1", "title": "Research auth", "agent": "Researcher-Alpha", "files?": ["src/auth/"], "status?": "done", "description?": "...", "dependsOn?": [] }] }] }] }`\n\nTask status values: `pending` | `in-progress` | `done` | `blocked`. Default: `pending`.\n\nGotcha: edges use `source` and `target`, not `from` and `to`.\n\nDecision tree:\n- Architecture, systems, containers, deployments: load the `c4-architecture` skill and use `c4@1` or `c4-static@1`.\n- Stateful workflows, approvals, or branching processes: use `process-flow@1` or `process-flow-static@1`.\n- Agent task decomposition, execution plans, or parallel batch visualization: use `task-plan@1` or `task-plan-static@1`.\n- Ordered walkthroughs, onboarding, or narrated analysis: use `tour@1`.\n\n## 6. Rules\n\n- Pick transport once: no `actions` for inline, any `actions` for browser.\n- Combine block types in one `blocks` array; metrics + chart + table + markdown is the normal composition pattern.\n- Use concise schemas in docs; do not expand to full `present()` calls unless behavior depends on them.\n- Use `table`, `comparison`, and `status-board` for structured data instead of formatting it inside markdown.\n- Architecture requests are delegated: load the `c4-architecture` skill for diagram guidance and data contracts.\n- If inline rendering only shows a title, raw JSON, or fallback text, switch to browser transport with a minimal action. (Browser-only templates now auto-route, so this mainly applies to custom block surfaces.)\n- For large or mixed-content surfaces, browser transport is usually easier to review and more reliable.\n- For custom HTML demos or visual prototypes, do NOT use the "html" block type (renders as raw text inline). Instead, write an HTML file, serve it locally (e.g. "npx serve" or "python -m http.server"), then open with the AI Kit "browser" tool: browser({ action: "open", url: "http://localhost:PORT/file.html", mode: "ui" }). This gives a controlled Chromium instance the LLM can read/interact with. Avoid "Start-Process" (opens uncontrolled system browser).\n'}]}export{e as default};