wyrm-mcp 9.4.0 → 9.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/agent-loop.js +3 -3
  2. package/dist/analytics.js +13 -13
  3. package/dist/audit.js +2 -2
  4. package/dist/brief-cache.js +1 -0
  5. package/dist/causality.js +5 -5
  6. package/dist/cloud/cli.js +14 -6
  7. package/dist/cloud/sync-engine.js +11 -8
  8. package/dist/connectors/index.js +1 -1
  9. package/dist/connectors/ingest.js +2 -2
  10. package/dist/content-signature.js +1 -1
  11. package/dist/daemon-write-endpoint.js +1 -1
  12. package/dist/database.js +43 -43
  13. package/dist/design.js +6 -6
  14. package/dist/entity-populate.js +3 -3
  15. package/dist/erasure-cli.js +3 -3
  16. package/dist/erasure-log.js +3 -3
  17. package/dist/erasure.js +15 -1
  18. package/dist/events.js +6 -6
  19. package/dist/failure-patterns.js +52 -42
  20. package/dist/federation.js +13 -13
  21. package/dist/fts-health.js +1 -1
  22. package/dist/goals.js +9 -9
  23. package/dist/governance.js +1 -1
  24. package/dist/handlers/arg-keys.js +1 -1
  25. package/dist/handlers/boundary.js +1 -1
  26. package/dist/handlers/capture.js +60 -58
  27. package/dist/handlers/datalake.js +2 -2
  28. package/dist/handlers/failure.js +13 -11
  29. package/dist/handlers/intelligence.js +57 -57
  30. package/dist/handlers/project.js +23 -22
  31. package/dist/handlers/prompts.js +3 -3
  32. package/dist/handlers/run.js +10 -10
  33. package/dist/handlers/session.js +9 -9
  34. package/dist/handlers/shims.js +1 -1
  35. package/dist/handlers/skill.js +44 -44
  36. package/dist/http-auth.js +2 -2
  37. package/dist/http-fast.js +18 -18
  38. package/dist/inbound-redact.js +1 -0
  39. package/dist/index.js +1 -2
  40. package/dist/intelligence.js +35 -31
  41. package/dist/internal-dispatch.js +2 -2
  42. package/dist/knowledge-graph.js +9 -9
  43. package/dist/logger.js +2 -2
  44. package/dist/mcp-client.js +4 -4
  45. package/dist/memory-artifacts.js +14 -14
  46. package/dist/metabolize.js +6 -6
  47. package/dist/performance.js +1 -1
  48. package/dist/presence.js +5 -5
  49. package/dist/providers/wordpiece.js +1 -1
  50. package/dist/read-memo.js +5 -5
  51. package/dist/receipts.js +4 -4
  52. package/dist/redact-backfill.js +1 -1
  53. package/dist/rerank.js +1 -1
  54. package/dist/reverse-bridge.js +4 -4
  55. package/dist/security.js +1 -1
  56. package/dist/skill-author.js +2 -2
  57. package/dist/sub-agent.js +6 -6
  58. package/dist/text-columns.js +1 -0
  59. package/dist/thread-store.js +2 -2
  60. package/dist/tool-analytics.js +4 -4
  61. package/dist/tool-manifest-v2.json +1 -1
  62. package/dist/tool-manifest.json +1 -1
  63. package/dist/trace-harvest.js +5 -5
  64. package/dist/ui-dashboard.js +30 -6
  65. package/dist/ui-login-open.js +2 -0
  66. package/dist/validate.js +1 -1
  67. package/dist/vectors.js +4 -4
  68. package/dist/wyrm-cli.js +88 -88
  69. package/dist/wyrm-manifest.json +1 -1
  70. package/dist/wyrm-ui.js +19 -11
  71. package/package.json +6 -2
  72. package/scripts/hooks/wyrm-session-capture.mjs +15 -2
  73. package/scripts/postinstall.cjs +26 -17
@@ -1 +1 @@
1
- function r(e){return e.replace(/\/\*[\s\S]*?\*\//g," ").replace(/(^|[^:])\/\/[^\n]*/gm,"$1").replace(/#[^\n]*/g," ").replace(/\s+/g," ").trim()}function i(e,t,a=24){const n=r(t);return n.length<a?!1:r(e).includes(n)}export{i as containsFailedSignature,r as normalizeContentSignature};
1
+ import{redactCredentials as t}from"./security.js";function n(e){return e.replace(/\/\*[\s\S]*?\*\//g," ").replace(/(^|[^:])\/\/[^\n]*/gm,"$1").replace(/#[^\n]*/g," ").replace(/\s+/g," ").trim()}function c(e,a,i=24){const r=n(a);return r.length<i?!1:n(e).includes(r)}function l(e){return t(n(t(e)))}export{c as containsFailedSignature,n as normalizeContentSignature,l as redactedContentSignature};
@@ -1 +1 @@
1
- import{isSqliteConstraintError as _}from"./sqlite-busy.js";import{runWithActor as g,sanitizeActorId as y}from"./handlers/boundary.js";const w=["data_insert","failure_record","truth_set","artifact_add","quest_add"];function k(t){return typeof t=="string"&&w.includes(t)}function h(t){return typeof t=="number"&&Number.isInteger(t)&&t>0?t:null}function s(t){return typeof t=="string"&&t.length>0?t:null}const b=10*6e4,D=4096,f=new Map;function S(){f.clear()}function F(t){for(const i of f.values())i.committedAt-=t}function q(t,i){const d=Date.now();for(const[a,e]of f){if(d-e.committedAt<b)break;f.delete(a)}if(!f.has(t)&&f.size>=D){const a=f.keys().next().value;a!==void 0&&f.delete(a)}f.set(t,{result:i,committedAt:d})}function v(t){return typeof t=="string"&&t.length>0&&t.length<=64?t:null}function R(t,i){const d=i.op;if(!k(d))return{e:"unknown op"};const a=i.input??{},e=h(i.project_id),o=v(i.write_id);if(o!==null){const r=f.get(o);if(r!==void 0)return r.result}let n;try{const r=i.actor;if(r!=null&&typeof r=="object"){const c=r,u=y(c.agent_id),l=y(c.run_id);n=g(u===null?{agent_id:"http",run_id:l,source:"writer"}:{agent_id:u,run_id:l,source:"param"},()=>m(t,d,e,i.project_id,a))}else n=m(t,d,e,i.project_id,a)}catch(r){if(_(r))return{e:r instanceof Error?r.message:String(r)};throw r}return o!==null&&"ok"in n&&n.ok===!0&&q(o,n),n}function m(t,i,d,a,e){switch(i){case"data_insert":{if(d===null)return{e:"project_id required"};const o=s(e.category),n=s(e.key),r=s(e.value);if(!o||!n||!r)return{e:"category, key, value required"};const c=t.db.insertData(d,o,n,r,e.metadata);return{ok:!0,op:i,row:c}}case"failure_record":{if(a!=null&&d===null)return{e:"project_id must be an integer"};const o=s(e.target),n=s(e.description),r=s(e.scope);if(!r||!o||!n)return{e:"scope, target, description required"};const c=t.failures.record({project_id:d,scope:r,target:o,description:n,why_failed:typeof e.why_failed=="string"?e.why_failed:void 0,severity:typeof e.severity=="string"?e.severity:void 0});return{ok:!0,op:i,row:c}}case"truth_set":{if(d===null)return{e:"project_id required"};const o=s(e.category),n=s(e.key),r=s(e.value);if(!o||!n||!r)return{e:"category, key, value required"};const c=t.truths.set(d,{category:o,key:n,value:r,rationale:typeof e.rationale=="string"?e.rationale:void 0,source:typeof e.source=="string"?e.source:void 0,confidence:typeof e.confidence=="number"?e.confidence:void 0,ttl_days:typeof e.ttl_days=="number"?e.ttl_days:void 0});return{ok:!0,op:i,row:c}}case"artifact_add":{if(d===null)return{e:"project_id required"};const o=s(e.kind),n=s(e.problem);if(!o||!n)return{e:"kind, problem required"};const r=t.artifacts.add(d,{kind:o,problem:n,constraints:typeof e.constraints=="string"?e.constraints:void 0,validatedFix:typeof e.validatedFix=="string"?e.validatedFix:void 0,whyItWorked:typeof e.whyItWorked=="string"?e.whyItWorked:void 0,outcome:typeof e.outcome=="string"?e.outcome:void 0,sourceSessionId:typeof e.sourceSessionId=="number"?e.sourceSessionId:void 0,tags:Array.isArray(e.tags)?e.tags.filter(c=>typeof c=="string"):void 0,confidence:typeof e.confidence=="number"?e.confidence:void 0,createdBy:typeof e.createdBy=="string"?e.createdBy:void 0,needsReview:typeof e.needsReview=="number"?e.needsReview:void 0,holdForReview:e.holdForReview===!0?!0:void 0});return{ok:!0,op:i,row:r}}case"quest_add":{if(d===null)return{e:"project_id required"};const o=s(e.title);if(!o)return{e:"title required"};const n=t.db.addQuest(d,o,typeof e.description=="string"?e.description:void 0,typeof e.priority=="string"?e.priority:"medium",typeof e.tags=="string"?e.tags:void 0);return{ok:!0,op:i,row:n}}}}export{w as DAEMON_WRITE_OPS,b as DEDUP_TTL_MS,F as _ageDaemonWriteDedupForTests,S as _resetDaemonWriteDedupForTests,R as handleDaemonWrite,k as isDaemonWriteOp};
1
+ import{isSqliteConstraintError as _}from"./sqlite-busy.js";import{runWithActor as g,sanitizeActorId as y}from"./handlers/boundary.js";const w=["data_insert","failure_record","truth_set","artifact_add","quest_add"];function h(t){return typeof t=="string"&&w.includes(t)}function k(t){return typeof t=="number"&&Number.isInteger(t)&&t>0?t:null}function s(t){return typeof t=="string"&&t.length>0?t:null}const b=10*6e4,D=4096,f=new Map;function S(){f.clear()}function F(t){for(const i of f.values())i.committedAt-=t}function q(t,i){const d=Date.now();for(const[a,e]of f){if(d-e.committedAt<b)break;f.delete(a)}if(!f.has(t)&&f.size>=D){const a=f.keys().next().value;a!==void 0&&f.delete(a)}f.set(t,{result:i,committedAt:d})}function v(t){return typeof t=="string"&&t.length>0&&t.length<=64?t:null}function R(t,i){const d=i.op;if(!h(d))return{e:"unknown op"};const a=i.input??{},e=k(i.project_id),o=v(i.write_id);if(o!==null){const r=f.get(o);if(r!==void 0)return r.result}let n;try{const r=i.actor;if(r!=null&&typeof r=="object"){const c=r,u=y(c.agent_id),l=y(c.run_id);n=g(u===null?{agent_id:"http",run_id:l,source:"writer"}:{agent_id:u,run_id:l,source:"param"},()=>m(t,d,e,i.project_id,a))}else n=m(t,d,e,i.project_id,a)}catch(r){if(_(r))return{e:r instanceof Error?r.message:String(r)};throw r}if("ok"in n&&n.ok===!0){o!==null&&q(o,n);try{t.onCommit?.(d)}catch{}}return n}function m(t,i,d,a,e){switch(i){case"data_insert":{if(d===null)return{e:"project_id required"};const o=s(e.category),n=s(e.key),r=s(e.value);if(!o||!n||!r)return{e:"category, key, value required"};const c=t.db.insertData(d,o,n,r,e.metadata);return{ok:!0,op:i,row:c}}case"failure_record":{if(a!=null&&d===null)return{e:"project_id must be an integer"};const o=s(e.target),n=s(e.description),r=s(e.scope);if(!r||!o||!n)return{e:"scope, target, description required"};const c=t.failures.record({project_id:d,scope:r,target:o,description:n,why_failed:typeof e.why_failed=="string"?e.why_failed:void 0,severity:typeof e.severity=="string"?e.severity:void 0});return{ok:!0,op:i,row:c}}case"truth_set":{if(d===null)return{e:"project_id required"};const o=s(e.category),n=s(e.key),r=s(e.value);if(!o||!n||!r)return{e:"category, key, value required"};const c=t.truths.set(d,{category:o,key:n,value:r,rationale:typeof e.rationale=="string"?e.rationale:void 0,source:typeof e.source=="string"?e.source:void 0,confidence:typeof e.confidence=="number"?e.confidence:void 0,ttl_days:typeof e.ttl_days=="number"?e.ttl_days:void 0});return{ok:!0,op:i,row:c}}case"artifact_add":{if(d===null)return{e:"project_id required"};const o=s(e.kind),n=s(e.problem);if(!o||!n)return{e:"kind, problem required"};const r=t.artifacts.add(d,{kind:o,problem:n,constraints:typeof e.constraints=="string"?e.constraints:void 0,validatedFix:typeof e.validatedFix=="string"?e.validatedFix:void 0,whyItWorked:typeof e.whyItWorked=="string"?e.whyItWorked:void 0,outcome:typeof e.outcome=="string"?e.outcome:void 0,sourceSessionId:typeof e.sourceSessionId=="number"?e.sourceSessionId:void 0,tags:Array.isArray(e.tags)?e.tags.filter(c=>typeof c=="string"):void 0,confidence:typeof e.confidence=="number"?e.confidence:void 0,createdBy:typeof e.createdBy=="string"?e.createdBy:void 0,needsReview:typeof e.needsReview=="number"?e.needsReview:void 0,holdForReview:e.holdForReview===!0?!0:void 0});return{ok:!0,op:i,row:r}}case"quest_add":{if(d===null)return{e:"project_id required"};const o=s(e.title);if(!o)return{e:"title required"};const n=t.db.addQuest(d,o,typeof e.description=="string"?e.description:void 0,typeof e.priority=="string"?e.priority:"medium",typeof e.tags=="string"?e.tags:void 0);return{ok:!0,op:i,row:n}}}}export{w as DAEMON_WRITE_OPS,b as DEDUP_TTL_MS,F as _ageDaemonWriteDedupForTests,S as _resetDaemonWriteDedupForTests,R as handleDaemonWrite,h as isDaemonWriteOp};
package/dist/database.js CHANGED
@@ -1,14 +1,14 @@
1
- import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSync as N,readdirSync as w,statSync as j,writeFileSync as M,chmodSync as H}from"fs";import{homedir as y}from"os";import{join as l,basename as v,resolve as F,normalize as W}from"path";import{spawnSync as T}from"child_process";import{getResilienceManager as U}from"./resilience.js";import{getStoreLogger as x}from"./logger.js";import{runMigrations as q,schemaSkew as P,getSchemaVersion as B,retryOpenOnBusy as $}from"./migrations.js";import{recordGovernanceEvent as b}from"./governance.js";import{enforceReadOnlyOpen as Y,newerStoreMessage as G,newerStoreWritesAllowed as V,NEWER_STORE_OVERRIDE_ENV as J}from"./store-readonly.js";import{describeStoreError as Le}from"./store-readonly.js";import{validateProjectPath as C,buildFtsMatchQuery as m,redactCredentials as k}from"./security.js";import{emitEvent as E,eventsSince as Q,subscribeEvents as z,isLiveMemoryEnabled as K,ingestRemoteEvent as Z,eventsForPush as X,pruneEvents as ee,getMeta as te,setMeta as se}from"./events.js";import{getActor as D}from"./handlers/boundary.js";import{eraseLakeRows as re}from"./erasure.js";import{readSkillContent as I,slugify as ie,skillContentSha as ne}from"./skill-author.js";import{resolveProjectForPath as oe,projectIdByPathKey as L}from"./project-resolve.js";function Ce(){return process.env.WYRM_DB_PATH??process.env.WYRM_DB??l(y(),".wyrm","wyrm.db")}function ae(S,t){const e=(s,r)=>{try{u(s)&&H(s,r)}catch{}};t&&e(t,448),e(S,384),e(`${S}-wal`,384),e(`${S}-shm`,384)}const ce={truth:{select:`SELECT t.cross_project_visibility AS vis, t.project_id AS pid, p.name AS pname, p.sync_policy AS policy
1
+ import{openDatabase as J}from"./storage/index.js";import{existsSync as h,mkdirSync as v,readdirSync as Q,statSync as z,writeFileSync as K,chmodSync as X}from"fs";import{homedir as W}from"os";import{join as u,basename as Z,resolve as ee,normalize as te}from"path";import{spawnSync as L}from"child_process";import{getResilienceManager as se}from"./resilience.js";import{getStoreLogger as re}from"./logger.js";import{runMigrations as ie,schemaSkew as ne,getSchemaVersion as oe,retryOpenOnBusy as ae}from"./migrations.js";import{recordGovernanceEvent as D}from"./governance.js";import{enforceReadOnlyOpen as ce,newerStoreMessage as le,newerStoreWritesAllowed as de,NEWER_STORE_OVERRIDE_ENV as ue}from"./store-readonly.js";import{describeStoreError as rt}from"./store-readonly.js";import{validateProjectPath as F,buildFtsMatchQuery as R,redactCredentials as m,redactCredentialsDeep as pe,redactedJson as U,redactField as E}from"./security.js";import{ValidationError as Ee}from"./validate.js";import{isEncryptedValue as he}from"./crypto.js";import{emitEvent as _,eventsSince as me,subscribeEvents as _e,isLiveMemoryEnabled as ge,ingestRemoteEvent as Se,eventsForPush as fe,pruneEvents as Re,getMeta as Te,setMeta as Ne}from"./events.js";import{getActor as x}from"./handlers/boundary.js";import{eraseLakeRows as be}from"./erasure.js";import{readSkillContent as q,skillFrontmatter as Oe,slugify as ye,skillContentSha as Ce}from"./skill-author.js";import{resolveProjectForPath as ke,projectIdByPathKey as P}from"./project-resolve.js";function Ze(){return process.env.WYRM_DB_PATH??process.env.WYRM_DB??u(W(),".wyrm","wyrm.db")}function Le(p,t){const e=(s,r)=>{try{h(s)&&X(s,r)}catch{}};t&&e(t,448),e(p,384),e(`${p}-wal`,384),e(`${p}-shm`,384)}const De={truth:{select:`SELECT t.cross_project_visibility AS vis, t.project_id AS pid, p.name AS pname, p.sync_policy AS policy
2
2
  FROM ground_truths t LEFT JOIN projects p ON p.id = t.project_id WHERE t.id = ?`,update:"UPDATE ground_truths SET cross_project_visibility = ?, updated_at = datetime('now') WHERE id = ?"},memory:{select:`SELECT t.cross_project_visibility AS vis, t.project_id AS pid, p.name AS pname, p.sync_policy AS policy
3
3
  FROM memory_artifacts t LEFT JOIN projects p ON p.id = t.project_id WHERE t.id = ?`,update:"UPDATE memory_artifacts SET cross_project_visibility = ?, updated_at = datetime('now') WHERE id = ?"},quest:{select:`SELECT t.cross_project_visibility AS vis, t.project_id AS pid, p.name AS pname, p.sync_policy AS policy
4
- FROM quests t LEFT JOIN projects p ON p.id = t.project_id WHERE t.id = ?`,update:"UPDATE quests SET cross_project_visibility = ?, updated_at = datetime('now') WHERE id = ?"}},de=new Set(["within","org","public"]);class ke{db;BATCH_SIZE=1e3;resilience;logger;dbPath;constructor(t){const e=l(y(),".wyrm");u(e)||N(e,{recursive:!0,mode:448}),this.dbPath=t||l(e,"wyrm.db"),this.logger=x(this.dbPath),this.resilience=U(),this.db=this.initializeDatabase(this.dbPath),ae(this.dbPath,e);const s=$(()=>(this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("cache_size = -64000"),this.db.pragma("temp_store = MEMORY"),this.db.pragma("busy_timeout = 5000"),this.db.pragma("mmap_size = 268435456"),this.db.pragma("page_size = 4096"),this.db.pragma("foreign_keys = ON"),this.db.pragma("secure_delete = ON"),q(this.db))),r=P(this.db);r.storeIsNewer&&(V()?this.logger.warn(`This store is at schema v${r.store}, newer than this Wyrm knows (v${r.binary}). ${J}=1 is set, so this older binary is writing to it under older rules. Upgrade wyrm-mcp on this machine.`):(Y(this.db,r),this.logger.warn(G(r.store,r.binary)))),s.length>0&&this.logger.info(`Applied ${s.length} migration(s), now at v${B(this.db)}`),this.recoverIncompleteOperations()}getDatabase(){return this.db}getDatabasePath(){return this.dbPath}initializeDatabase(t){const e=this.resilience.withRetrySync(()=>A(t),"database_init",{maxAttempts:3,baseDelayMs:500});if(!e.success)throw this.logger.error("Failed to initialize database",{path:t,error:e.error?.message}),e.error||new Error("Database initialization failed");return e.data}recoverIncompleteOperations(){const t=this.resilience.getIncompleteOperations();for(const e of t)this.logger.warn("Found incomplete operation from previous session",{operation:e.operation,stage:e.stage,id:e.id}),e.operation==="batch_insert"&&this.logger.info("Batch insert was incomplete - data may need re-import"),this.resilience.completeCheckpoint(e.id)}addWatchDir(t,e=!0){return C(t),this.db.prepare(`
4
+ FROM quests t LEFT JOIN projects p ON p.id = t.project_id WHERE t.id = ?`,update:"UPDATE quests SET cross_project_visibility = ?, updated_at = datetime('now') WHERE id = ?"}},Ie=new Set(["within","org","public"]),Ae=["objectives","completed","issues","commits","files_changed","notes","summary"];function B(p){const t={...p};for(const e of Ae)typeof t[e]=="string"&&(t[e]=m(t[e]));return t}function G(p,t){return{value:he(p)?p:m(p),metadata:t?JSON.stringify(pe(t)):null}}function y(p){return[m(p),p]}function we(p){return p==="connector_config"||p.startsWith("connector:")}const je="(key <> 'connector_config' AND key NOT GLOB 'connector:*')",Me="(key = 'connector_config' OR key GLOB 'connector:*')";class et{db;BATCH_SIZE=1e3;resilience;logger;dbPath;constructor(t){const e=u(W(),".wyrm");h(e)||v(e,{recursive:!0,mode:448}),this.dbPath=t||u(e,"wyrm.db"),this.logger=re(this.dbPath),this.resilience=se(),this.db=this.initializeDatabase(this.dbPath),Le(this.dbPath,e);const s=ae(()=>(this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("cache_size = -64000"),this.db.pragma("temp_store = MEMORY"),this.db.pragma("busy_timeout = 5000"),this.db.pragma("mmap_size = 268435456"),this.db.pragma("page_size = 4096"),this.db.pragma("foreign_keys = ON"),this.db.pragma("secure_delete = ON"),ie(this.db))),r=ne(this.db);r.storeIsNewer&&(de()?this.logger.warn(`This store is at schema v${r.store}, newer than this Wyrm knows (v${r.binary}). ${ue}=1 is set, so this older binary is writing to it under older rules. Upgrade wyrm-mcp on this machine.`):(ce(this.db,r),this.logger.warn(le(r.store,r.binary)))),s.length>0&&this.logger.info(`Applied ${s.length} migration(s), now at v${oe(this.db)}`),this.recoverIncompleteOperations()}getDatabase(){return this.db}getDatabasePath(){return this.dbPath}initializeDatabase(t){const e=this.resilience.withRetrySync(()=>J(t),"database_init",{maxAttempts:3,baseDelayMs:500});if(!e.success)throw this.logger.error("Failed to initialize database",{path:t,error:e.error?.message}),e.error||new Error("Database initialization failed");return e.data}recoverIncompleteOperations(){const t=this.resilience.getIncompleteOperations();for(const e of t)this.logger.warn("Found incomplete operation from previous session",{operation:e.operation,stage:e.stage,id:e.id}),e.operation==="batch_insert"&&this.logger.info("Batch insert was incomplete - data may need re-import"),this.resilience.completeCheckpoint(e.id)}addWatchDir(t,e=!0){return F(t),this.db.prepare(`
5
5
  INSERT INTO watch_dirs (path, recursive)
6
6
  VALUES (?, ?)
7
7
  ON CONFLICT(path) DO UPDATE SET recursive = excluded.recursive
8
8
  RETURNING *
9
- `).get(t,e?1:0)}getWatchDirs(){return this.db.prepare("SELECT * FROM watch_dirs").all()}removeWatchDir(t){this.db.prepare("DELETE FROM watch_dirs WHERE path = ?").run(t)}scanForProjects(t,e=!0){C(t);const s=[],r=(i,n=0)=>{if(!(n>3&&e))try{const a=w(i,{withFileTypes:!0});for(const o of a){if(!o.isDirectory()||o.name.startsWith(".")&&o.name!==".git")continue;const c=l(i,o.name),d=l(c,".git");if(u(d)){const p=this.registerProjectFromPath(c);p&&s.push(p)}else e&&n<3&&r(c,n+1)}}catch{}};return r(t),this.db.prepare(`
9
+ `).get(t,e?1:0)}getWatchDirs(){return this.db.prepare("SELECT * FROM watch_dirs").all()}removeWatchDir(t){this.db.prepare("DELETE FROM watch_dirs WHERE path = ?").run(t)}scanForProjects(t,e=!0){F(t);const s=[],r=(i,n=0)=>{if(!(n>3&&e))try{const a=Q(i,{withFileTypes:!0});for(const o of a){if(!o.isDirectory()||o.name.startsWith(".")&&o.name!==".git")continue;const c=u(i,o.name),l=u(c,".git");if(h(l)){const d=this.registerProjectFromPath(c);d&&s.push(d)}else e&&n<3&&r(c,n+1)}}catch{}};return r(t),this.db.prepare(`
10
10
  UPDATE watch_dirs SET last_scan = datetime('now') WHERE path = ?
11
- `).run(t),s}scanAllWatchDirs(){const t=this.getWatchDirs(),e=[];for(const s of t){const r=this.scanForProjects(s.path,!!s.recursive);e.push(...r)}return e}registerProjectFromPath(t){try{const e=W(F(t));if(!u(e)||!j(e).isDirectory())return null;const s=v(e);let r,i,n,a;try{const o=T("git",["config","--get","remote.origin.url"],{cwd:e,encoding:"utf-8",timeout:5e3,shell:!1});o.status===0&&(r=o.stdout.trim());const c=T("git",["rev-parse","--abbrev-ref","HEAD"],{cwd:e,encoding:"utf-8",timeout:5e3,shell:!1});c.status===0&&(i=c.stdout.trim());const d=T("git",["log","-1","--format=%h %s"],{cwd:e,encoding:"utf-8",timeout:5e3,shell:!1});d.status===0&&(n=d.stdout.trim())}catch{}return u(l(e,"package.json"))?(a="Node.js",u(l(e,"next.config.js"))||u(l(e,"next.config.ts"))||u(l(e,"next.config.mjs"))?a="Next.js":u(l(e,"vite.config.ts"))&&(a="Vite")):u(l(e,"requirements.txt"))||u(l(e,"pyproject.toml"))?a="Python":u(l(e,"composer.json"))?a="PHP":u(l(e,"Cargo.toml"))?a="Rust":u(l(e,"go.mod"))&&(a="Go"),this.registerProject(s,e,r,a,n,i)}catch{return null}}registerProject(t,e,s,r,i,n,a=process.platform){const o=()=>this.db.prepare(`
11
+ `).run(t),s}scanAllWatchDirs(){const t=this.getWatchDirs(),e=[];for(const s of t){const r=this.scanForProjects(s.path,!!s.recursive);e.push(...r)}return e}registerProjectFromPath(t){try{const e=te(ee(t));if(!h(e)||!z(e).isDirectory())return null;const s=Z(e);let r,i,n,a;try{const o=L("git",["config","--get","remote.origin.url"],{cwd:e,encoding:"utf-8",timeout:5e3,shell:!1});o.status===0&&(r=o.stdout.trim());const c=L("git",["rev-parse","--abbrev-ref","HEAD"],{cwd:e,encoding:"utf-8",timeout:5e3,shell:!1});c.status===0&&(i=c.stdout.trim());const l=L("git",["log","-1","--format=%h %s"],{cwd:e,encoding:"utf-8",timeout:5e3,shell:!1});l.status===0&&(n=l.stdout.trim())}catch{}return h(u(e,"package.json"))?(a="Node.js",h(u(e,"next.config.js"))||h(u(e,"next.config.ts"))||h(u(e,"next.config.mjs"))?a="Next.js":h(u(e,"vite.config.ts"))&&(a="Vite")):h(u(e,"requirements.txt"))||h(u(e,"pyproject.toml"))?a="Python":h(u(e,"composer.json"))?a="PHP":h(u(e,"Cargo.toml"))?a="Rust":h(u(e,"go.mod"))&&(a="Go"),this.registerProject(s,e,r,a,n,i)}catch{return null}}registerProject(t,e,s,r,i,n,a=process.platform){const o=E(s||null),c=E(i||null),l=()=>this.db.prepare(`
12
12
  INSERT INTO projects (name, path, repo, stack, last_commit, branch)
13
13
  VALUES (?, ?, ?, ?, ?, ?)
14
14
  ON CONFLICT(path) DO UPDATE SET
@@ -19,7 +19,7 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
19
19
  branch = COALESCE(excluded.branch, projects.branch),
20
20
  updated_at = datetime('now')
21
21
  RETURNING *
22
- `).get(t,e,s||null,r||null,i||null,n||null);return a!=="win32"?o():this.db.transaction(()=>{const d=this.db.prepare("SELECT 1 FROM projects WHERE path = ?").get(e)?void 0:L(this.db,e,a);return d===void 0?o():this.db.prepare(`
22
+ `).get(t,e,o,r||null,c,n||null);return a!=="win32"?l():this.db.transaction(()=>{const f=this.db.prepare("SELECT 1 FROM projects WHERE path = ?").get(e)?void 0:P(this.db,e,a);return f===void 0?l():this.db.prepare(`
23
23
  UPDATE projects SET
24
24
  name = ?,
25
25
  repo = COALESCE(?, repo),
@@ -29,30 +29,30 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
29
29
  updated_at = datetime('now')
30
30
  WHERE id = ?
31
31
  RETURNING *
32
- `).get(t,s||null,r||null,i||null,n||null,d)}).immediate()}getProject(t,e=process.platform){const s=this.db.prepare("SELECT * FROM projects WHERE path = ?").get(t);if(s||e!=="win32")return s;const r=L(this.db,t,e);return r===void 0?void 0:this.getProjectById(r)}getProjectForPath(t,e=process.platform){const{id:s,root:r}=oe(this.db,t,e);return s!==null&&r!==null?{id:s,path:r}:void 0}getProjectById(t){return this.db.prepare("SELECT * FROM projects WHERE id = ?").get(t)}getProjectByName(t){return this.db.prepare("SELECT * FROM projects WHERE name = ?").get(t)}getAllProjects(t=100,e=0){return this.db.prepare(`
32
+ `).get(t,o,r||null,c,n||null,f)}).immediate()}getProject(t,e=process.platform){const s=this.db.prepare("SELECT * FROM projects WHERE path = ?").get(t);if(s||e!=="win32")return s;const r=P(this.db,t,e);return r===void 0?void 0:this.getProjectById(r)}getProjectForPath(t,e=process.platform){const{id:s,root:r}=ke(this.db,t,e);return s!==null&&r!==null?{id:s,path:r}:void 0}getProjectById(t){return this.db.prepare("SELECT * FROM projects WHERE id = ?").get(t)}getProjectByName(t){return this.db.prepare("SELECT * FROM projects WHERE name = ?").get(t)}getAllProjects(t=100,e=0){return this.db.prepare(`
33
33
  SELECT * FROM projects ORDER BY updated_at DESC LIMIT ? OFFSET ?
34
34
  `).all(t,e)}searchProjects(t){const e=`%${t}%`;return this.db.prepare(`
35
35
  SELECT * FROM projects
36
36
  WHERE name LIKE ? OR stack LIKE ? OR repo LIKE ?
37
37
  ORDER BY updated_at DESC
38
38
  LIMIT 50
39
- `).all(e,e,e)}createSession(t,e){const s=this.estimateTokens((e.objectives||"")+(e.completed||"")+(e.issues||"")+(e.notes||"")),r=D(),i=this.resilience.withRetrySync(()=>this.db.prepare(`
39
+ `).all(e,e,e)}createSession(t,e){e=B(e);const s=this.estimateTokens((e.objectives||"")+(e.completed||"")+(e.issues||"")+(e.notes||"")),r=x(),i=this.resilience.withRetrySync(()=>this.db.prepare(`
40
40
  INSERT INTO sessions (project_id, date, objectives, completed, issues, commits, files_changed, notes, tokens_estimate, agent_id, run_id)
41
41
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
42
42
  RETURNING *
43
- `).get(t,e.date||new Date().toISOString().split("T")[0],e.objectives||"",e.completed||"",e.issues||"",e.commits||"",e.files_changed||"",e.notes||"",s,r.agent_id,r.run_id),"createSession");if(!i.success)throw i.error||new Error("Failed to create session");return E(this.db,{projectId:t,kind:"session_update",refTable:"sessions",refId:i.data.id}),i.data}updateSession(t,e){const s=[],r=[],i=new Set(["objectives","completed","issues","commits","files_changed","notes","summary","is_archived"]);for(const[a,o]of Object.entries(e))i.has(a)&&o!==void 0&&(s.push(`${a} = ?`),r.push(o));if(s.length===0)return this.getSession(t);if(e.objectives||e.completed||e.issues||e.notes){const a=this.getSession(t);if(a){const o=this.estimateTokens((e.objectives||a.objectives)+(e.completed||a.completed)+(e.issues||a.issues)+(e.notes||a.notes));s.push("tokens_estimate = ?"),r.push(o)}}r.push(t);const n=this.resilience.withRetrySync(()=>this.db.prepare(`
43
+ `).get(t,e.date||new Date().toISOString().split("T")[0],e.objectives||"",e.completed||"",e.issues||"",e.commits||"",e.files_changed||"",e.notes||"",s,r.agent_id,r.run_id),"createSession");if(!i.success)throw i.error||new Error("Failed to create session");return _(this.db,{projectId:t,kind:"session_update",refTable:"sessions",refId:i.data.id}),i.data}updateSession(t,e){e=B(e);const s=[],r=[],i=new Set(["objectives","completed","issues","commits","files_changed","notes","summary","is_archived"]);for(const[a,o]of Object.entries(e))i.has(a)&&o!==void 0&&(s.push(`${a} = ?`),r.push(o));if(s.length===0)return this.getSession(t);if(e.objectives||e.completed||e.issues||e.notes){const a=this.getSession(t);if(a){const o=this.estimateTokens((e.objectives||a.objectives)+(e.completed||a.completed)+(e.issues||a.issues)+(e.notes||a.notes));s.push("tokens_estimate = ?"),r.push(o)}}r.push(t);const n=this.resilience.withRetrySync(()=>this.db.prepare(`
44
44
  UPDATE sessions SET ${s.join(", ")} WHERE id = ? RETURNING *
45
- `).get(...r),"updateSession");if(!n.success)throw n.error||new Error("Failed to update session");return E(this.db,{projectId:n.data.project_id,kind:"session_update",refTable:"sessions",refId:t}),n.data}getSession(t){return this.db.prepare("SELECT * FROM sessions WHERE id = ?").get(t)}updateContinuity(t,e,s){const r=e.trim();if(!r)return;const i=12e3,n=this.db.prepare("SELECT thread, turn_count FROM project_continuity WHERE project_id = ?").get(t),a=new Date().toISOString(),o=`[${a}${s?" \xB7 "+s:""}] ${r}`;let c=n?.thread?n.thread+`
45
+ `).get(...r),"updateSession");if(!n.success)throw n.error||new Error("Failed to update session");return _(this.db,{projectId:n.data.project_id,kind:"session_update",refTable:"sessions",refId:t}),n.data}getSession(t){return this.db.prepare("SELECT * FROM sessions WHERE id = ?").get(t)}updateContinuity(t,e,s){const r=m(e.trim());if(!r)return;const i=12e3,n=this.db.prepare("SELECT thread, turn_count FROM project_continuity WHERE project_id = ?").get(t),a=new Date().toISOString(),o=`[${a}${s?" \xB7 "+s:""}] ${r}`;let c=n?.thread?n.thread+`
46
46
 
47
47
  `+o:o;c.length>i&&(c=`[\u2026earlier continuity trimmed; gist retained below\u2026]
48
48
 
49
- `+c.slice(c.length-i));const d=(n?.turn_count??0)+1;this.resilience.withRetrySync(()=>(this.db.prepare(`
49
+ `+c.slice(c.length-i));const l=(n?.turn_count??0)+1;this.resilience.withRetrySync(()=>(this.db.prepare(`
50
50
  INSERT INTO project_continuity (project_id, thread, updated_at, updated_by, turn_count)
51
51
  VALUES (?, ?, ?, ?, ?)
52
52
  ON CONFLICT(project_id) DO UPDATE SET
53
53
  thread = excluded.thread, updated_at = excluded.updated_at,
54
54
  updated_by = excluded.updated_by, turn_count = excluded.turn_count
55
- `).run(t,c,a,s??null,d),!0),"updateContinuity")}getContinuity(t){return this.db.prepare("SELECT thread, updated_at, updated_by, turn_count FROM project_continuity WHERE project_id = ?").get(t)??null}recordRouting(t){const e=t.projectId==null?-1:t.projectId,s=(t.taskType??"").trim(),r=(t.modelId??"").trim();if(!s)throw new Error("recordRouting: taskType is required");if(!r)throw new Error("recordRouting: modelId is required");let i=null;if(Array.isArray(t.toolNames)){const o=[...new Set(t.toolNames.map(c=>String(c).trim()).filter(Boolean))];i=o.length?o.join(","):null}else if(typeof t.toolNames=="string"){const o=[...new Set(t.toolNames.split(",").map(c=>c.trim()).filter(Boolean))];i=o.length?o.join(","):null}const n=t.latencyMs==null||!Number.isFinite(t.latencyMs)?null:Math.max(0,Math.round(t.latencyMs));let a=-1;return this.resilience.withRetrySync(()=>{const o=this.db.prepare(`
55
+ `).run(t,c,a,s??null,l),!0),"updateContinuity")}getContinuity(t){return this.db.prepare("SELECT thread, updated_at, updated_by, turn_count FROM project_continuity WHERE project_id = ?").get(t)??null}recordRouting(t){const e=t.projectId==null?-1:t.projectId,s=(t.taskType??"").trim(),r=(t.modelId??"").trim();if(!s)throw new Error("recordRouting: taskType is required");if(!r)throw new Error("recordRouting: modelId is required");let i=null;if(Array.isArray(t.toolNames)){const o=[...new Set(t.toolNames.map(c=>String(c).trim()).filter(Boolean))];i=o.length?o.join(","):null}else if(typeof t.toolNames=="string"){const o=[...new Set(t.toolNames.split(",").map(c=>c.trim()).filter(Boolean))];i=o.length?o.join(","):null}const n=t.latencyMs==null||!Number.isFinite(t.latencyMs)?null:Math.max(0,Math.round(t.latencyMs));let a=-1;return this.resilience.withRetrySync(()=>{const o=this.db.prepare(`
56
56
  INSERT INTO routing_memory (project_id, task_type, model_id, tool_names, success, latency_ms)
57
57
  VALUES (?, ?, ?, ?, ?, ?)
58
58
  `).run(e,s,r,i,t.success?1:0,n);return a=Number(o.lastInsertRowid),!0},"recordRouting"),a}recallRouting(t,e={}){const s=(t??"").trim();if(!s)return[];const r=Math.min(100,Math.max(1,e.limit??10)),i=["task_type = ?"],n=[s];e.projectId!=null&&(i.push("(project_id = ? OR project_id = -1)"),n.push(e.projectId)),e.sinceDays!=null&&Number.isFinite(e.sinceDays)&&e.sinceDays>0&&(i.push("ts >= datetime('now', ?)"),n.push(`-${Math.round(e.sinceDays)} days`)),n.push(r);try{return this.db.prepare(`
@@ -76,7 +76,7 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
76
76
  LIMIT ?
77
77
  `).all(t,e)}getTodaySession(t){const e=new Date().toISOString().split("T")[0];return this.db.prepare(`
78
78
  SELECT * FROM sessions WHERE project_id = ? AND date = ?
79
- `).get(t,e)}searchSessions(t,e){const s=m(t);if(!s)return[];try{return e?this.db.prepare(`
79
+ `).get(t,e)}searchSessions(t,e){const s=R(t);if(!s)return[];try{return e?this.db.prepare(`
80
80
  SELECT s.* FROM sessions s
81
81
  JOIN sessions_fts fts ON s.id = fts.rowid
82
82
  WHERE sessions_fts MATCH ? AND s.project_id = ?
@@ -102,19 +102,19 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
102
102
  `).run(t,t,e).changes}getSessionTokenUsage(t){return this.db.prepare(`
103
103
  SELECT COALESCE(SUM(tokens_estimate), 0) as total
104
104
  FROM sessions WHERE project_id = ? AND is_archived = 0
105
- `).get(t).total}addQuest(t,e,s,r="medium",i){const n=D(),a=this.db.prepare(`
105
+ `).get(t).total}addQuest(t,e,s,r="medium",i){const n=x(),a=this.db.prepare(`
106
106
  INSERT INTO quests (project_id, title, description, priority, tags, agent_id, run_id, updated_at)
107
107
  VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
108
108
  RETURNING *
109
- `).get(t,k(e),k(s||""),r,i||null,n.agent_id,n.run_id);return E(this.db,{projectId:t,kind:"quest",refTable:"quests",refId:a.id}),a}updateQuest(t,e){const s=e==="completed"?new Date().toISOString():null,r=this.db.prepare(`
109
+ `).get(t,m(e),m(s||""),r,E(i)||null,n.agent_id,n.run_id);return _(this.db,{projectId:t,kind:"quest",refTable:"quests",refId:a.id}),a}updateQuest(t,e){const s=e==="completed"?new Date().toISOString():null,r=this.db.prepare(`
110
110
  UPDATE quests SET status = ?, completed_at = ?, updated_at = datetime('now') WHERE id = ? RETURNING *
111
- `).get(e,s,t);return r&&E(this.db,{projectId:r.project_id,kind:"quest",refTable:"quests",refId:t}),r}parkQuest(t){const e=this.db.prepare(`
111
+ `).get(e,s,t);return r&&_(this.db,{projectId:r.project_id,kind:"quest",refTable:"quests",refId:t}),r}parkQuest(t){const e=this.db.prepare(`
112
112
  UPDATE quests SET status = 'parked', updated_at = datetime('now')
113
113
  WHERE id = ? AND status IN ('pending', 'in_progress') RETURNING *
114
- `).get(t);return e?(E(this.db,{projectId:e.project_id,kind:"quest",refTable:"quests",refId:t}),e):null}unparkQuest(t){const e=this.db.prepare(`
114
+ `).get(t);return e?(_(this.db,{projectId:e.project_id,kind:"quest",refTable:"quests",refId:t}),e):null}unparkQuest(t){const e=this.db.prepare(`
115
115
  UPDATE quests SET status = 'pending', updated_at = datetime('now')
116
116
  WHERE id = ? AND status = 'parked' RETURNING *
117
- `).get(t);return e?(E(this.db,{projectId:e.project_id,kind:"quest",refTable:"quests",refId:t}),e):null}getAllParkedQuests(){return this.db.prepare(`
117
+ `).get(t);return e?(_(this.db,{projectId:e.project_id,kind:"quest",refTable:"quests",refId:t}),e):null}getAllParkedQuests(){return this.db.prepare(`
118
118
  SELECT q.*, p.name as project_name FROM quests q
119
119
  JOIN projects p ON q.project_id = p.id
120
120
  WHERE q.status = 'parked'
@@ -149,7 +149,7 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
149
149
  WHEN 'low' THEN 4
150
150
  END,
151
151
  q.created_at ASC
152
- `).all()}searchQuests(t,e){const s=m(t);if(!s)return[];try{return e!==void 0?this.db.prepare(`
152
+ `).all()}searchQuests(t,e){const s=R(t);if(!s)return[];try{return e!==void 0?this.db.prepare(`
153
153
  SELECT q.* FROM quests q
154
154
  JOIN quests_fts fts ON q.id = fts.rowid
155
155
  WHERE quests_fts MATCH ? AND q.project_id = ?
@@ -172,11 +172,11 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
172
172
  ON CONFLICT(project_id, key) DO UPDATE SET
173
173
  value = excluded.value,
174
174
  updated_at = datetime('now')
175
- `).run(t,e,s)}getContext(t,e){return this.db.prepare(`
175
+ `).run(t,e,E(s))}getContext(t,e){return this.db.prepare(`
176
176
  SELECT value FROM context WHERE project_id = ? AND key = ?
177
177
  `).get(t,e)?.value}getAllContext(t){const e=this.db.prepare(`
178
178
  SELECT key, value FROM context WHERE project_id = ?
179
- `).all(t),s={};for(const r of e)s[r.key]=r.value;return s}liveMemoryEnabled(){return K()}eventsSince(t,e=0,s=100){return Q(this.db,t,e,s)}subscribeEvents(t,e=20){return z(this.db,t,e)}publishEvent(t){E(this.db,t)}pruneEvents(t={}){return ee(this.db,t)}ingestRemoteEvent(t,e){return Z(this.db,t,e)}eventsForPush(t,e=0,s=200){return X(this.db,t,e,s)}getMeta(t){return te(this.db,t)}setMeta(t,e){se(this.db,t,e)}setGlobalContext(t,e){this.db.prepare(`
179
+ `).all(t),s={};for(const r of e)s[r.key]=r.value;return s}liveMemoryEnabled(){return ge()}eventsSince(t,e=0,s=100){return me(this.db,t,e,s)}subscribeEvents(t,e=20){return _e(this.db,t,e)}publishEvent(t){_(this.db,t)}pruneEvents(t={}){return Re(this.db,t)}ingestRemoteEvent(t,e){return Se(this.db,t,e)}eventsForPush(t,e=0,s=200){return fe(this.db,t,e,s)}getMeta(t){return Te(this.db,t)}setMeta(t,e){Ne(this.db,t,e)}setGlobalContext(t,e){if(we(t))throw new Ee("key","is a connector setting; manage it with `wyrm connector`");this.writeGlobalContext(t,E(e))}setGlobalSetting(t,e){this.writeGlobalContext(t,e)}writeGlobalContext(t,e){this.db.prepare(`
180
180
  INSERT INTO global_context (key, value)
181
181
  VALUES (?, ?)
182
182
  ON CONFLICT(key) DO UPDATE SET
@@ -184,19 +184,19 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
184
184
  updated_at = datetime('now')
185
185
  `).run(t,e)}getGlobalContext(t){return this.db.prepare(`
186
186
  SELECT value FROM global_context WHERE key = ?
187
- `).get(t)?.value}getAllGlobalContext(){const t=this.db.prepare("SELECT key, value FROM global_context").all(),e={};for(const s of t)e[s.key]=s.value;return e}registerSkill(t,e,s,r,i,n,a,o){const c=o?.tier??"atomic",d=JSON.stringify(Array.isArray(o?.governs)?o.governs:[]),p=JSON.stringify(Array.isArray(o?.composes)?o.composes:[]),_=I({skillPath:s,name:t}),g=_?.content??null,f=_?.sha256??null,R=_?new Date().toISOString():null,h=this.resilience.withRetrySync(()=>this.db.prepare(`
187
+ `).get(t)?.value}getAllGlobalContext(){const t=this.db.prepare(`SELECT key, value FROM global_context WHERE ${je}`).all(),e={};for(const s of t)e[s.key]=s.value;return e}countGlobalSettings(){return this.db.prepare(`SELECT COUNT(*) AS n FROM global_context WHERE ${Me}`).get().n}registerSkill(t,e,s,r,i,n,a,o){const c=o?.tier!==void 0,l=Array.isArray(o?.governs),d=Array.isArray(o?.composes),f=o?.tier??"atomic",T=U(l?o.governs:[]),N=U(d?o.composes:[]),g=q({skillPath:s,name:t}),b=g?.content??null,O=g?.sha256??null,$=g?new Date().toISOString():null,S=k=>typeof k=="string"&&k.trim()!==""?E(k):null,I=g?Oe(g.content):{},A=S(e),w=S(r),Y=A??S(I.description)??t,V=w??S(I.category),j=S(i),M=S(n),H=S(a),C=this.resilience.withRetrySync(()=>this.db.prepare(`
188
188
  INSERT INTO skills (name, description, skill_path, category, author, version, tags, tier, governs, composes, content, content_sha256, content_updated_at, is_active, usage_count)
189
189
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0)
190
190
  ON CONFLICT(name) DO UPDATE SET
191
- description = excluded.description,
191
+ description = CASE WHEN ? OR skills.description IS NULL OR skills.description = '' THEN excluded.description ELSE skills.description END,
192
192
  skill_path = excluded.skill_path,
193
- category = excluded.category,
194
- author = excluded.author,
195
- version = excluded.version,
196
- tags = excluded.tags,
197
- tier = excluded.tier,
198
- governs = excluded.governs,
199
- composes = excluded.composes,
193
+ category = CASE WHEN ? OR skills.category IS NULL OR skills.category = '' THEN excluded.category ELSE skills.category END,
194
+ author = CASE WHEN ? THEN excluded.author ELSE skills.author END,
195
+ version = CASE WHEN ? THEN excluded.version ELSE skills.version END,
196
+ tags = CASE WHEN ? THEN excluded.tags ELSE skills.tags END,
197
+ tier = CASE WHEN ? THEN excluded.tier ELSE skills.tier END,
198
+ governs = CASE WHEN ? THEN excluded.governs ELSE skills.governs END,
199
+ composes = CASE WHEN ? THEN excluded.composes ELSE skills.composes END,
200
200
  content = CASE WHEN excluded.content IS NOT NULL THEN excluded.content ELSE skills.content END,
201
201
  content_sha256 = CASE WHEN excluded.content IS NOT NULL THEN excluded.content_sha256 ELSE skills.content_sha256 END,
202
202
  content_updated_at = CASE
@@ -206,13 +206,13 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
206
206
  updated_at = datetime('now'),
207
207
  is_active = 1
208
208
  RETURNING *
209
- `).get(t,e,s,r||null,i||null,n||null,a||null,c,d,p,g,f,R),"registerSkill");if(!h.success)throw h.error||new Error("Failed to register skill");return h.data}getSkill(t){const e=this.db.prepare("SELECT * FROM skills WHERE name = ?").get(t);return e&&this.db.prepare("UPDATE skills SET last_used = datetime('now'), usage_count = usage_count + 1 WHERE id = ?").run(e.id),e}backfillSkillContent(t={}){const e=t.read??I,s=this.db.prepare("SELECT id, name, skill_path, content_sha256 FROM skills").all();let r=0,i=0,n=0;const a=this.db.prepare("UPDATE skills SET content = ?, content_sha256 = ?, content_updated_at = ?, updated_at = datetime('now') WHERE id = ?");return this.db.transaction(()=>{for(const c of s){const d=e({skillPath:c.skill_path,name:c.name,skillsDir:t.skillsDir});if(!d){n++;continue}if(c.content_sha256&&c.content_sha256===d.sha256){i++;continue}a.run(d.content,d.sha256,new Date().toISOString(),c.id),r++}})(),{total:s.length,filled:r,unchanged:i,missing:n}}exportSkillContent(t,e={}){const s=e.includeInactive?"":" WHERE is_active = 1",r=this.db.prepare(`SELECT name, content FROM skills${s} ORDER BY name`).all();let i=0,n=0,a=0;const o=new Set;for(const c of r){if(c.content==null){n++;continue}let d=ie(c.name);if(!d){n++;continue}o.has(d)&&(d=`${d}-${ne(c.name).slice(0,8)}`,a++),o.add(d);const p=l(t,d);N(p,{recursive:!0}),M(l(p,"SKILL.md"),c.content,"utf-8"),i++}return{total:r.length,written:i,skipped_no_content:n,collisions:a}}setSkillVisibility(t,e){return this.db.transaction(()=>{const s=this.db.prepare("UPDATE skills SET cross_project_visibility = ?, updated_at = datetime('now') WHERE name = ? RETURNING *").get(e,t);return s&&b(this.db,"visibility.set",{kind:"skill",row_id:s.id,to:e}),s})()}setAllSkillsVisibility(t,e={}){const s=[],r=[t];e.includeInactive||s.push("is_active = 1"),e.tier&&(s.push("tier = ?"),r.push(e.tier));const i=s.length?" WHERE "+s.join(" AND "):"";return this.db.transaction(()=>{const n=this.db.prepare(`UPDATE skills SET cross_project_visibility = ?, updated_at = datetime('now')${i}`).run(...r);return n.changes>0&&b(this.db,"visibility.set",{kind:"skill",count:n.changes,to:t}),n.changes})()}setRowVisibility(t,e,s){const r=ce[t];if(!r)throw new Error(`Unknown visibility kind: ${String(t)} (use truth|memory|quest)`);if(!de.has(s))throw new Error(`Unknown visibility: ${String(s)} (use within|org|public)`);const i=this.db.prepare(r.select).get(e),n={kind:t,id:e,visibility:s};if(!i)return{...n,outcome:"not_found",projectId:null,projectName:null,syncPolicy:null,egressEligible:!1};const a={projectId:i.pid,projectName:i.pname,syncPolicy:i.policy,previous:i.vis},o=c=>c!=="within"&&(i.pid==null||i.policy==="cloud"||i.policy==="team");return s!=="within"&&i.policy==="private"?{...n,...a,outcome:"refused_private_grove",egressEligible:!1}:i.vis===s?{...n,...a,outcome:"unchanged",egressEligible:o(s)}:(this.db.transaction(()=>{this.db.prepare(r.update).run(s,e),b(this.db,"visibility.set",{kind:t,row_id:e,from:i.vis,to:s},{projectId:i.pid})})(),{...n,...a,outcome:"updated",egressEligible:o(s)})}listSkills(t,e,s,r){let i="SELECT * FROM skills WHERE 1=1";const n=[];if(t!==void 0&&(i+=" AND is_active = ?",n.push(t?1:0)),e&&(i+=" AND category = ?",n.push(e)),r&&(i+=" AND tier = ?",n.push(r)),s){const a=m(s);a&&(i+=" AND id IN (SELECT rowid FROM skills_fts WHERE skills_fts MATCH ?)",n.push(a))}return i+=" ORDER BY updated_at DESC",this.db.prepare(i).all(...n)}searchSkills(t,e=20,s){const r=m(t);if(!r)return[];const i=s?" AND s.tier = ?":"",n=this.db.prepare(`
209
+ `).get(t,Y,s,V,j,M,H,f,T,N,b,O,$,A!==null?1:0,w!==null?1:0,j!==null?1:0,M!==null?1:0,H!==null?1:0,c?1:0,l?1:0,d?1:0),"registerSkill");if(!C.success)throw C.error||new Error("Failed to register skill");return C.data}getSkill(t){const e=this.db.prepare("SELECT * FROM skills WHERE name = ?").get(t);return e&&this.db.prepare("UPDATE skills SET last_used = datetime('now'), usage_count = usage_count + 1 WHERE id = ?").run(e.id),e}backfillSkillContent(t={}){const e=t.read??q,s=this.db.prepare("SELECT id, name, skill_path, content_sha256 FROM skills").all();let r=0,i=0,n=0;const a=this.db.prepare("UPDATE skills SET content = ?, content_sha256 = ?, content_updated_at = ?, updated_at = datetime('now') WHERE id = ?");return this.db.transaction(()=>{for(const c of s){const l=e({skillPath:c.skill_path,name:c.name,skillsDir:t.skillsDir});if(!l){n++;continue}if(c.content_sha256&&c.content_sha256===l.sha256){i++;continue}a.run(l.content,l.sha256,new Date().toISOString(),c.id),r++}})(),{total:s.length,filled:r,unchanged:i,missing:n}}exportSkillContent(t,e={}){const s=e.includeInactive?"":" WHERE is_active = 1",r=this.db.prepare(`SELECT name, content FROM skills${s} ORDER BY name`).all();let i=0,n=0,a=0;const o=new Set;for(const c of r){if(c.content==null){n++;continue}let l=ye(c.name);if(!l){n++;continue}o.has(l)&&(l=`${l}-${Ce(c.name).slice(0,8)}`,a++),o.add(l);const d=u(t,l);v(d,{recursive:!0}),K(u(d,"SKILL.md"),c.content,"utf-8"),i++}return{total:r.length,written:i,skipped_no_content:n,collisions:a}}setSkillVisibility(t,e){return this.db.transaction(()=>{const s=this.db.prepare("UPDATE skills SET cross_project_visibility = ?, updated_at = datetime('now') WHERE name = ? RETURNING *").get(e,t);return s&&D(this.db,"visibility.set",{kind:"skill",row_id:s.id,to:e}),s})()}setAllSkillsVisibility(t,e={}){const s=[],r=[t];e.includeInactive||s.push("is_active = 1"),e.tier&&(s.push("tier = ?"),r.push(e.tier));const i=s.length?" WHERE "+s.join(" AND "):"";return this.db.transaction(()=>{const n=this.db.prepare(`UPDATE skills SET cross_project_visibility = ?, updated_at = datetime('now')${i}`).run(...r);return n.changes>0&&D(this.db,"visibility.set",{kind:"skill",count:n.changes,to:t}),n.changes})()}setRowVisibility(t,e,s){const r=De[t];if(!r)throw new Error(`Unknown visibility kind: ${String(t)} (use truth|memory|quest)`);if(!Ie.has(s))throw new Error(`Unknown visibility: ${String(s)} (use within|org|public)`);const i=this.db.prepare(r.select).get(e),n={kind:t,id:e,visibility:s};if(!i)return{...n,outcome:"not_found",projectId:null,projectName:null,syncPolicy:null,egressEligible:!1};const a={projectId:i.pid,projectName:i.pname,syncPolicy:i.policy,previous:i.vis},o=c=>c!=="within"&&(i.pid==null||i.policy==="cloud"||i.policy==="team");return s!=="within"&&i.policy==="private"?{...n,...a,outcome:"refused_private_grove",egressEligible:!1}:i.vis===s?{...n,...a,outcome:"unchanged",egressEligible:o(s)}:(this.db.transaction(()=>{this.db.prepare(r.update).run(s,e),D(this.db,"visibility.set",{kind:t,row_id:e,from:i.vis,to:s},{projectId:i.pid})})(),{...n,...a,outcome:"updated",egressEligible:o(s)})}listSkills(t,e,s,r){let i="SELECT * FROM skills WHERE 1=1";const n=[];if(t!==void 0&&(i+=" AND is_active = ?",n.push(t?1:0)),e&&(i+=" AND category = ?",n.push(e)),r&&(i+=" AND tier = ?",n.push(r)),s){const a=R(s);a&&(i+=" AND id IN (SELECT rowid FROM skills_fts WHERE skills_fts MATCH ?)",n.push(a))}return i+=" ORDER BY updated_at DESC",this.db.prepare(i).all(...n)}searchSkills(t,e=20,s){const r=R(t);if(!r)return[];const i=s?" AND s.tier = ?":"",n=this.db.prepare(`
210
210
  SELECT s.* FROM skills s
211
211
  JOIN skills_fts ON s.id = skills_fts.rowid
212
212
  WHERE skills_fts MATCH ?${i}
213
213
  ORDER BY rank
214
214
  LIMIT ?
215
- `);return s?n.all(r,s,e):n.all(r,e)}getSkillGraph(t){const e=this.db.prepare("SELECT * FROM skills").all(),s=new Map;for(const o of e)s.set(o.name,o);const r=o=>{if(!o)return[];try{const c=JSON.parse(o);return Array.isArray(c)?c.filter(d=>typeof d=="string"):[]}catch{return[]}},i=(o,c)=>{const d=s.get(o);if(!d)return null;const p=r(d.governs),_=r(d.composes),g={name:d.name,tier:d.tier??"atomic",description:d.description,governs:p,composes:_,children:[]},f=[];if(!c.has(o)){const R=new Set(c).add(o);for(const h of p){const O=i(h,R);O?g.children.push(O):f.push(h)}}return f.length&&(g.missing=f),g};if(t){const o=i(t,new Set);return o?[o]:[]}const n=e.filter(o=>(o.tier??"atomic")==="god").map(o=>o.name).sort(),a=[];for(const o of n){const c=i(o,new Set);c&&a.push(c)}return a}updateSkill(t,e){const s=[],r=[];return e.description!==void 0&&(s.push("description = ?"),r.push(e.description)),e.skill_path!==void 0&&(s.push("skill_path = ?"),r.push(e.skill_path)),e.category!==void 0&&(s.push("category = ?"),r.push(e.category)),e.is_active!==void 0&&(s.push("is_active = ?"),r.push(e.is_active?1:0)),e.tags!==void 0&&(s.push("tags = ?"),r.push(e.tags)),e.version!==void 0&&(s.push("version = ?"),r.push(e.version)),s.length===0?this.getSkill(t):(s.push("updated_at = datetime('now')"),r.push(t),this.db.prepare(`
215
+ `);return s?n.all(r,s,e):n.all(r,e)}getSkillGraph(t){const e=this.db.prepare("SELECT * FROM skills").all(),s=new Map;for(const o of e)s.set(o.name,o);const r=o=>{if(!o)return[];try{const c=JSON.parse(o);return Array.isArray(c)?c.filter(l=>typeof l=="string"):[]}catch{return[]}},i=(o,c)=>{const l=s.get(o);if(!l)return null;const d=r(l.governs),f=r(l.composes),T={name:l.name,tier:l.tier??"atomic",description:l.description,governs:d,composes:f,children:[]},N=[];if(!c.has(o)){const g=new Set(c).add(o);for(const b of d){const O=i(b,g);O?T.children.push(O):N.push(b)}}return N.length&&(T.missing=N),T};if(t){const o=i(t,new Set);return o?[o]:[]}const n=e.filter(o=>(o.tier??"atomic")==="god").map(o=>o.name).sort(),a=[];for(const o of n){const c=i(o,new Set);c&&a.push(c)}return a}updateSkill(t,e){const s=[],r=[];return e.description!==void 0&&(s.push("description = ?"),r.push(E(e.description))),e.skill_path!==void 0&&(s.push("skill_path = ?"),r.push(e.skill_path)),e.category!==void 0&&(s.push("category = ?"),r.push(e.category)),e.is_active!==void 0&&(s.push("is_active = ?"),r.push(e.is_active?1:0)),e.tags!==void 0&&(s.push("tags = ?"),r.push(E(e.tags))),e.version!==void 0&&(s.push("version = ?"),r.push(e.version)),s.length===0?this.getSkill(t):(s.push("updated_at = datetime('now')"),r.push(t),this.db.prepare(`
216
216
  UPDATE skills SET ${s.join(", ")} WHERE name = ? RETURNING *
217
217
  `).get(...r))}deleteSkill(t){return this.db.prepare("DELETE FROM skills WHERE name = ?").run(t).changes>0}deactivateSkill(t){return this.updateSkill(t,{is_active:!1})}activateSkill(t){return this.updateSkill(t,{is_active:!0})}getSkillStats(){const t=this.db.prepare("SELECT COUNT(*) as count FROM skills").get().count,e=this.db.prepare("SELECT COUNT(*) as count FROM skills WHERE is_active = 1").get().count,s=this.db.prepare(`
218
218
  SELECT category, COUNT(*) as count FROM skills WHERE category IS NOT NULL GROUP BY category
@@ -225,29 +225,29 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
225
225
  task_count = excluded.task_count,
226
226
  updated_at = datetime('now')
227
227
  RETURNING *
228
- `).get(t,e,s||null,r||null,i)}getSpec(t,e){return this.db.prepare("SELECT * FROM specs WHERE project_id = ? AND spec_dir = ?").get(t,e)}listSpecs(t){return t!==void 0?this.db.prepare("SELECT * FROM specs WHERE project_id = ? ORDER BY updated_at DESC").all(t):this.db.prepare("SELECT * FROM specs ORDER BY updated_at DESC").all()}findQuestBySpecSignature(t,e){return this.db.prepare(`
228
+ `).get(t,e,E(s)||null,E(r)||null,i)}getSpec(t,e){return this.db.prepare("SELECT * FROM specs WHERE project_id = ? AND spec_dir = ?").get(t,e)}listSpecs(t){return t!==void 0?this.db.prepare("SELECT * FROM specs WHERE project_id = ? ORDER BY updated_at DESC").all(t):this.db.prepare("SELECT * FROM specs ORDER BY updated_at DESC").all()}findQuestBySpecSignature(t,e){return this.db.prepare(`
229
229
  SELECT * FROM quests
230
230
  WHERE project_id = ?
231
231
  AND (tags = ? OR tags LIKE ? OR tags LIKE ? OR tags LIKE ?)
232
232
  LIMIT 1
233
- `).get(t,e,`${e},%`,`%,${e}`,`%,${e},%`)}updateQuestFields(t,e){const s=[],r=[];if(e.title!==void 0&&(s.push("title = ?"),r.push(e.title)),e.description!==void 0&&(s.push("description = ?"),r.push(e.description)),e.tags!==void 0&&(s.push("tags = ?"),r.push(e.tags)),s.length===0)return this.db.prepare("SELECT * FROM quests WHERE id = ?").get(t);s.push("updated_at = datetime('now')"),r.push(t);const i=this.db.prepare(`UPDATE quests SET ${s.join(", ")} WHERE id = ? RETURNING *`).get(...r);return i&&E(this.db,{projectId:i.project_id,kind:"quest",refTable:"quests",refId:t}),i}insertData(t,e,s,r,i){const n=this.resilience.withRetrySync(()=>this.db.prepare(`
233
+ `).get(t,e,`${e},%`,`%,${e}`,`%,${e},%`)}updateQuestFields(t,e){const s=[],r=[];if(e.title!==void 0&&(s.push("title = ?"),r.push(m(e.title))),e.description!==void 0&&(s.push("description = ?"),r.push(m(e.description))),e.tags!==void 0&&(s.push("tags = ?"),r.push(E(e.tags))),s.length===0)return this.db.prepare("SELECT * FROM quests WHERE id = ?").get(t);s.push("updated_at = datetime('now')"),r.push(t);const i=this.db.prepare(`UPDATE quests SET ${s.join(", ")} WHERE id = ? RETURNING *`).get(...r);return i&&_(this.db,{projectId:i.project_id,kind:"quest",refTable:"quests",refId:t}),i}insertData(t,e,s,r,i){const n=G(r,i),a=this.resilience.withRetrySync(()=>this.db.prepare(`
234
234
  INSERT INTO data_lake (project_id, category, key, value, metadata)
235
235
  VALUES (?, ?, ?, ?, ?)
236
236
  RETURNING *
237
- `).get(t,e,s,r,i?JSON.stringify(i):null),"insertData");if(!n.success)throw n.error||new Error("Insert data failed");return n.data}batchWrites(t,e){return this.db.transaction(r=>r.map(i=>e(i)))(t)}insertDataBatch(t){const e=this.resilience.generateOperationId("batch_insert"),s=this.BATCH_SIZE;let r=0;this.resilience.createCheckpoint(e,"batch_insert","started",{totalItems:t.length,batchSize:s});const i=this.db.prepare(`
237
+ `).get(t,E(e),E(s),n.value,n.metadata),"insertData");if(!a.success)throw a.error||new Error("Insert data failed");return a.data}batchWrites(t,e){return this.db.transaction(r=>r.map(i=>e(i)))(t)}insertDataBatch(t){const e=this.resilience.generateOperationId("batch_insert"),s=this.BATCH_SIZE;let r=0;this.resilience.createCheckpoint(e,"batch_insert","started",{totalItems:t.length,batchSize:s});const i=this.db.prepare(`
238
238
  INSERT INTO data_lake (project_id, category, key, value, metadata)
239
239
  VALUES (?, ?, ?, ?, ?)
240
- `);try{for(let n=0;n<t.length;n+=s){const a=t.slice(n,n+s),o=Math.floor(n/s)+1;this.resilience.updateCheckpoint(e,`batch_${o}`,{processed:n,currentBatch:o});const c=this.resilience.withRetrySync(()=>this.batchWrites(a,d=>(i.run(d.projectId,d.category,d.key,d.value,d.metadata?JSON.stringify(d.metadata):null),1)).length,`batch_insert_${o}`,{maxAttempts:3});if(!c.success){this.logger.error("Batch insert failed",{batch:o,processed:r,error:c.error?.message}),this.resilience.updateCheckpoint(e,"partial_failure",{inserted:r,failedAt:n});const d=c.error??new Error(`Batch insert failed at chunk ${o}`);throw d.inserted=r,d}r+=c.data}return this.resilience.completeCheckpoint(e),r}catch(n){throw n!==null&&typeof n=="object"&&"inserted"in n||(this.logger.error("Batch insert exception",{inserted:r,error:n.message}),this.resilience.updateCheckpoint(e,"exception",{inserted:r,error:n.message}),n!==null&&typeof n=="object"&&(n.inserted=r)),n}}queryData(t,e,s=100,r=0){return e?this.db.prepare(`
240
+ `);try{for(let n=0;n<t.length;n+=s){const a=t.slice(n,n+s),o=Math.floor(n/s)+1;this.resilience.updateCheckpoint(e,`batch_${o}`,{processed:n,currentBatch:o});const c=this.resilience.withRetrySync(()=>this.batchWrites(a,l=>{const d=G(l.value,l.metadata);return i.run(l.projectId,E(l.category),E(l.key),d.value,d.metadata),1}).length,`batch_insert_${o}`,{maxAttempts:3});if(!c.success){this.logger.error("Batch insert failed",{batch:o,processed:r,error:c.error?.message}),this.resilience.updateCheckpoint(e,"partial_failure",{inserted:r,failedAt:n});const l=c.error??new Error(`Batch insert failed at chunk ${o}`);throw l.inserted=r,l}r+=c.data}return this.resilience.completeCheckpoint(e),r}catch(n){throw n!==null&&typeof n=="object"&&"inserted"in n||(this.logger.error("Batch insert exception",{inserted:r,error:n.message}),this.resilience.updateCheckpoint(e,"exception",{inserted:r,error:n.message}),n!==null&&typeof n=="object"&&(n.inserted=r)),n}}queryData(t,e,s=100,r=0){return e?this.db.prepare(`
241
241
  SELECT * FROM data_lake
242
- WHERE project_id = ? AND category = ?
242
+ WHERE project_id = ? AND category IN (?, ?)
243
243
  ORDER BY created_at DESC
244
244
  LIMIT ? OFFSET ?
245
- `).all(t,e,s,r):this.db.prepare(`
245
+ `).all(t,...y(e),s,r):this.db.prepare(`
246
246
  SELECT * FROM data_lake
247
247
  WHERE project_id = ?
248
248
  ORDER BY created_at DESC
249
249
  LIMIT ? OFFSET ?
250
- `).all(t,s,r)}searchData(t,e){const s=m(t);if(!s)return[];try{return e?this.db.prepare(`
250
+ `).all(t,s,r)}searchData(t,e){const s=R(t);if(!s)return[];try{return e?this.db.prepare(`
251
251
  SELECT d.* FROM data_lake d
252
252
  JOIN data_lake_fts fts ON d.id = fts.rowid
253
253
  WHERE data_lake_fts MATCH ? AND d.project_id = ?
@@ -259,7 +259,7 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
259
259
  WHERE data_lake_fts MATCH ?
260
260
  ORDER BY bm25(data_lake_fts), d.created_at DESC
261
261
  LIMIT 100
262
- `).all(s)}catch{return[]}}searchArtifacts(t,e){const s=m(t);if(!s)return[];try{return e?this.db.prepare(`
262
+ `).all(s)}catch{return[]}}searchArtifacts(t,e){const s=R(t);if(!s)return[];try{return e?this.db.prepare(`
263
263
  SELECT a.* FROM memory_artifacts a
264
264
  JOIN memory_artifacts_fts fts ON a.id = fts.rowid
265
265
  WHERE memory_artifacts_fts MATCH ? AND a.project_id = ?
@@ -279,8 +279,8 @@ import{openDatabase as A}from"./storage/index.js";import{existsSync as u,mkdirSy
279
279
  WHERE project_id = ?
280
280
  GROUP BY category
281
281
  ORDER BY count DESC
282
- `).all(t)}forgetData(t){const e=[],s=[];if(t.match){e.push("(key LIKE ? OR value LIKE ?)");const p=`%${t.match}%`;s.push(p,p)}if(t.dataset&&(e.push("category = ?"),s.push(t.dataset)),typeof t.olderThanDays=="number"&&(e.push("created_at < datetime('now', ?)"),s.push(`-${Math.max(0,Math.floor(t.olderThanDays))} days`)),t.projectId!=null&&(e.push("project_id = ?"),s.push(t.projectId)),e.length===0)return{matched:0,deleted:0,samples:[],counts:{},erasureId:null};const r=e.join(" AND "),i=this.db.prepare(`SELECT id, category, key FROM data_lake WHERE ${r}`).all(...s),n=i.length,a=this.db.prepare(`SELECT category, key, created_at FROM data_lake WHERE ${r} ORDER BY created_at LIMIT 5`).all(...s),o=t.dryRun!==!1,c=re(this.db,i,{subject:t.match,dryRun:o,reason:t.reason??(t.match?"subject":t.dataset?"source":"age"),predicate:{match:!!t.match,dataset:t.dataset??null,older_than_days:typeof t.olderThanDays=="number"?t.olderThanDays:null,project:t.projectId!=null},actor:t.actor??null}),d=o?0:c.counts.data_lake??0;return{matched:n,deleted:d,samples:a,counts:c.counts,erasureId:c.erasureId}}deleteDataCategory(t,e){return this.db.prepare(`
283
- DELETE FROM data_lake WHERE project_id = ? AND category = ?
284
- `).run(t,e).changes}*streamSessions(t){const e=this.db.prepare(`
282
+ `).all(t)}forgetData(t){const e=[],s=[];if(t.match){e.push("(key LIKE ? OR value LIKE ?)");const d=`%${t.match}%`;s.push(d,d)}if(t.dataset&&(e.push("category IN (?, ?)"),s.push(...y(t.dataset))),typeof t.olderThanDays=="number"&&(e.push("created_at < datetime('now', ?)"),s.push(`-${Math.max(0,Math.floor(t.olderThanDays))} days`)),t.projectId!=null&&(e.push("project_id = ?"),s.push(t.projectId)),e.length===0)return{matched:0,deleted:0,samples:[],counts:{},erasureId:null};const r=e.join(" AND "),i=this.db.prepare(`SELECT id, category, key FROM data_lake WHERE ${r}`).all(...s),n=i.length,a=this.db.prepare(`SELECT category, key, created_at FROM data_lake WHERE ${r} ORDER BY created_at LIMIT 5`).all(...s),o=t.dryRun!==!1,c=be(this.db,i,{subject:t.match,dryRun:o,reason:t.reason??(t.match?"subject":t.dataset?"source":"age"),predicate:{match:!!t.match,dataset:t.dataset??null,older_than_days:typeof t.olderThanDays=="number"?t.olderThanDays:null,project:t.projectId!=null},actor:t.actor??null}),l=o?0:c.counts.data_lake??0;return{matched:n,deleted:l,samples:a,counts:c.counts,erasureId:c.erasureId}}deleteDataCategory(t,e){return this.db.prepare(`
283
+ DELETE FROM data_lake WHERE project_id = ? AND category IN (?, ?)
284
+ `).run(t,...y(e)).changes}*streamSessions(t){const e=this.db.prepare(`
285
285
  SELECT * FROM sessions WHERE project_id = ? ORDER BY date DESC
286
- `);for(const s of e.iterate(t))yield s}*streamData(t,e){const s=e?this.db.prepare("SELECT * FROM data_lake WHERE project_id = ? AND category = ?"):this.db.prepare("SELECT * FROM data_lake WHERE project_id = ?"),r=e?[t,e]:[t];for(const i of s.iterate(...r))yield i}getStats(){const t=this.db.prepare("SELECT COUNT(*) as count FROM projects").get(),e=this.db.prepare("SELECT COUNT(*) as count FROM sessions").get(),s=this.db.prepare("SELECT COUNT(*) as count FROM quests").get(),r=this.db.prepare("SELECT COUNT(*) as count FROM memory_artifacts").get(),i=this.db.prepare("SELECT COUNT(*) as count FROM data_lake").get(),n=this.db.prepare("SELECT COALESCE(SUM(tokens_estimate), 0) as total FROM sessions WHERE is_archived = 0").get(),a=this.db.pragma("page_count",{simple:!0}),o=this.db.pragma("page_size",{simple:!0}),c=a*o/(1024*1024);return{projects:t.count,sessions:e.count,quests:s.count,artifacts:r.count,dataPoints:i.count,totalTokens:n.total,dbSize:`${c.toFixed(2)} MB`}}getProjectStats(t){const e=this.db.prepare("SELECT COUNT(*) as count FROM sessions WHERE project_id = ?").get(t),s=this.db.prepare("SELECT COUNT(*) as count FROM quests WHERE project_id = ? AND status IN ('pending', 'in_progress')").get(t),r=this.db.prepare("SELECT COUNT(*) as count FROM quests WHERE project_id = ? AND status = 'completed'").get(t),i=this.db.prepare("SELECT COUNT(*) as count FROM data_lake WHERE project_id = ?").get(t),n=this.db.prepare("SELECT COALESCE(SUM(tokens_estimate), 0) as total FROM sessions WHERE project_id = ? AND is_archived = 0").get(t);return{sessions:e.count,quests:{pending:s.count,completed:r.count},dataPoints:i.count,tokens:n.total}}estimateTokens(t){return Math.ceil(t.length/4)}vacuum(){this.db.exec("VACUUM")}checkpoint(){this.db.pragma("wal_checkpoint(TRUNCATE)")}getResilienceStatus(){const t=this.resilience.getCircuitStatus(),e=this.resilience.getIncompleteOperations();return{circuitState:t.state,failures:t.failures,incompleteOps:e.length}}resetCircuitBreaker(){this.resilience.resetCircuit()}close(){try{this.checkpoint(),this.logger.info("Database checkpoint completed")}catch(t){this.logger.error("Checkpoint failed during close",{error:t.message})}try{this.db.close(),this.logger.info("Database closed successfully")}catch(t){this.logger.error("Database close failed",{error:t.message})}}checkIntegrity(){const t=[];try{const e=this.db.pragma("integrity_check",{simple:!0});e!=="ok"&&t.push(`Integrity check failed: ${e}`)}catch(e){t.push(`Integrity check error: ${e.message}`)}try{const e=this.db.pragma("foreign_key_check");e.length>0&&t.push(`Foreign key violations: ${e.length}`)}catch(e){t.push(`FK check error: ${e.message}`)}return{ok:t.length===0,issues:t}}}export{ke as WyrmDB,Le as describeStoreError,Ce as resolveDbPath,ae as tightenStorePermissions};
286
+ `);for(const s of e.iterate(t))yield s}*streamData(t,e){const s=e?this.db.prepare("SELECT * FROM data_lake WHERE project_id = ? AND category IN (?, ?)"):this.db.prepare("SELECT * FROM data_lake WHERE project_id = ?"),r=e?[t,...y(e)]:[t];for(const i of s.iterate(...r))yield i}getStats(){const t=this.db.prepare("SELECT COUNT(*) as count FROM projects").get(),e=this.db.prepare("SELECT COUNT(*) as count FROM sessions").get(),s=this.db.prepare("SELECT COUNT(*) as count FROM quests").get(),r=this.db.prepare("SELECT COUNT(*) as count FROM memory_artifacts").get(),i=this.db.prepare("SELECT COUNT(*) as count FROM data_lake").get(),n=this.db.prepare("SELECT COALESCE(SUM(tokens_estimate), 0) as total FROM sessions WHERE is_archived = 0").get(),a=this.db.pragma("page_count",{simple:!0}),o=this.db.pragma("page_size",{simple:!0}),c=a*o/(1024*1024);return{projects:t.count,sessions:e.count,quests:s.count,artifacts:r.count,dataPoints:i.count,totalTokens:n.total,dbSize:`${c.toFixed(2)} MB`}}getProjectStats(t){const e=this.db.prepare("SELECT COUNT(*) as count FROM sessions WHERE project_id = ?").get(t),s=this.db.prepare("SELECT COUNT(*) as count FROM quests WHERE project_id = ? AND status IN ('pending', 'in_progress')").get(t),r=this.db.prepare("SELECT COUNT(*) as count FROM quests WHERE project_id = ? AND status = 'completed'").get(t),i=this.db.prepare("SELECT COUNT(*) as count FROM data_lake WHERE project_id = ?").get(t),n=this.db.prepare("SELECT COALESCE(SUM(tokens_estimate), 0) as total FROM sessions WHERE project_id = ? AND is_archived = 0").get(t);return{sessions:e.count,quests:{pending:s.count,completed:r.count},dataPoints:i.count,tokens:n.total}}estimateTokens(t){return Math.ceil(t.length/4)}vacuum(){this.db.exec("VACUUM")}checkpoint(){this.db.pragma("wal_checkpoint(TRUNCATE)")}getResilienceStatus(){const t=this.resilience.getCircuitStatus(),e=this.resilience.getIncompleteOperations();return{circuitState:t.state,failures:t.failures,incompleteOps:e.length}}resetCircuitBreaker(){this.resilience.resetCircuit()}close(){try{this.checkpoint(),this.logger.info("Database checkpoint completed")}catch(t){this.logger.error("Checkpoint failed during close",{error:t.message})}try{this.db.close(),this.logger.info("Database closed successfully")}catch(t){this.logger.error("Database close failed",{error:t.message})}}checkIntegrity(){const t=[];try{const e=this.db.pragma("integrity_check",{simple:!0});e!=="ok"&&t.push(`Integrity check failed: ${e}`)}catch(e){t.push(`Integrity check error: ${e.message}`)}try{const e=this.db.pragma("foreign_key_check");e.length>0&&t.push(`Foreign key violations: ${e.length}`)}catch(e){t.push(`FK check error: ${e.message}`)}return{ok:t.length===0,issues:t}}}export{et as WyrmDB,rt as describeStoreError,we as isGlobalSettingKey,Ze as resolveDbPath,Le as tightenStorePermissions};
package/dist/design.js CHANGED
@@ -1,4 +1,4 @@
1
- const d=new Set(["color","type","spacing","motion","shadow","radius","breakpoint","custom"]),c=new Set(["url","image","palette","snippet"]);class E{db;constructor(e){this.db=e}setToken(e){if(!d.has(e.category))throw new Error(`invalid category: ${e.category} (must be one of ${[...d].join(", ")})`);if(!e.key||!e.value)throw new Error("key and value are required");return this.db.prepare(`
1
+ import{redactField as s}from"./security.js";const c=new Set(["color","type","spacing","motion","shadow","radius","breakpoint","custom"]),i=new Set(["url","image","palette","snippet"]);class u{db;constructor(e){this.db=e}setToken(e){if(!c.has(e.category))throw new Error(`invalid category: ${e.category} (must be one of ${[...c].join(", ")})`);if(!e.key||!e.value)throw new Error("key and value are required");return this.db.prepare(`
2
2
  INSERT INTO design_tokens (project_id, category, key, value, notes, source)
3
3
  VALUES (?, ?, ?, ?, ?, ?)
4
4
  ON CONFLICT(project_id, category, key) DO UPDATE SET
@@ -6,7 +6,7 @@ const d=new Set(["color","type","spacing","motion","shadow","radius","breakpoint
6
6
  notes = excluded.notes,
7
7
  source = excluded.source,
8
8
  updated_at = datetime('now')
9
- `).run(e.projectId,e.category,e.key,e.value,e.notes??null,e.source??"user"),this.getToken(e.projectId,e.category,e.key)}getToken(e,r,t){return this.db.prepare(`
9
+ `).run(e.projectId,e.category,e.key,s(e.value),s(e.notes??null),e.source??"user"),this.getToken(e.projectId,e.category,e.key)}getToken(e,r,t){return this.db.prepare(`
10
10
  SELECT * FROM design_tokens
11
11
  WHERE project_id = ? AND category = ? AND key = ?
12
12
  `).get(e,r,t)??null}listTokens(e,r){return r?this.db.prepare(`
@@ -20,11 +20,11 @@ const d=new Set(["color","type","spacing","motion","shadow","radius","breakpoint
20
20
  `).all(e)}deleteToken(e,r,t){return(this.db.prepare(`
21
21
  DELETE FROM design_tokens
22
22
  WHERE project_id = ? AND category = ? AND key = ?
23
- `).run(e,r,t).changes??0)>0}formatForContext(e){const r=this.listTokens(e);if(r.length===0)return"";const t=new Map;for(const s of r){const o=t.get(s.category)??[];o.push(s),t.set(s.category,o)}const n=[];n.push("### \u{1F3A8} Design tokens");for(const[s,o]of t){n.push(""),n.push(`**${s}:**`);for(const a of o)n.push(`- \`${a.key}\` = \`${a.value}\`${a.notes?` (${a.notes})`:""}`)}return n.join(`
24
- `)}addReference(e){if(!c.has(e.kind))throw new Error(`invalid kind: ${e.kind} (must be one of ${[...c].join(", ")})`);if(!e.location)throw new Error("location is required");const r=this.db.prepare(`
23
+ `).run(e,r,t).changes??0)>0}formatForContext(e){const r=this.listTokens(e);if(r.length===0)return"";const t=new Map;for(const o of r){const a=t.get(o.category)??[];a.push(o),t.set(o.category,a)}const n=[];n.push("### \u{1F3A8} Design tokens");for(const[o,a]of t){n.push(""),n.push(`**${o}:**`);for(const d of a)n.push(`- \`${d.key}\` = \`${d.value}\`${d.notes?` (${d.notes})`:""}`)}return n.join(`
24
+ `)}addReference(e){if(!i.has(e.kind))throw new Error(`invalid kind: ${e.kind} (must be one of ${[...i].join(", ")})`);if(!e.location)throw new Error("location is required");const r=this.db.prepare(`
25
25
  INSERT INTO design_references (project_id, kind, location, title, notes, tags, updated_at)
26
26
  VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
27
- `).run(e.projectId??null,e.kind,e.location,e.title??null,e.notes??null,e.tags??null);return this.getReference(Number(r.lastInsertRowid))}getReference(e){return this.db.prepare("SELECT * FROM design_references WHERE id = ?").get(e)??null}listReferences(e={}){const r=Math.min(e.limit??50,200);if(e.tag){const t=`%${e.tag}%`;return e.projectId!=null?this.db.prepare(`
27
+ `).run(e.projectId??null,e.kind,s(e.location),s(e.title??null),s(e.notes??null),s(e.tags??null));return this.getReference(Number(r.lastInsertRowid))}getReference(e){return this.db.prepare("SELECT * FROM design_references WHERE id = ?").get(e)??null}listReferences(e={}){const r=Math.min(e.limit??50,200);if(e.tag){const t=`%${e.tag}%`;return e.projectId!=null?this.db.prepare(`
28
28
  SELECT * FROM design_references
29
29
  WHERE project_id = ? AND tags LIKE ?
30
30
  ORDER BY captured_at DESC LIMIT ?
@@ -43,4 +43,4 @@ const d=new Set(["color","type","spacing","motion","shadow","radius","breakpoint
43
43
  SELECT dr.* FROM design_references_fts ft
44
44
  JOIN design_references dr ON dr.id = ft.rowid
45
45
  WHERE design_references_fts MATCH ?
46
- `;return r.projectId!=null?this.db.prepare(`${n} AND dr.project_id = ? ORDER BY dr.captured_at DESC LIMIT ?`).all(e,r.projectId,t):this.db.prepare(`${n} ORDER BY dr.captured_at DESC LIMIT ?`).all(e,t)}deleteReference(e){return(this.db.prepare("DELETE FROM design_references WHERE id = ?").run(e).changes??0)>0}countReferences(e){return e!=null?this.db.prepare("SELECT COUNT(*) AS n FROM design_references WHERE project_id = ?").get(e).n:this.db.prepare("SELECT COUNT(*) AS n FROM design_references").get().n}}export{E as DesignSystem};
46
+ `;return r.projectId!=null?this.db.prepare(`${n} AND dr.project_id = ? ORDER BY dr.captured_at DESC LIMIT ?`).all(e,r.projectId,t):this.db.prepare(`${n} ORDER BY dr.captured_at DESC LIMIT ?`).all(e,t)}deleteReference(e){return(this.db.prepare("DELETE FROM design_references WHERE id = ?").run(e).changes??0)>0}countReferences(e){return e!=null?this.db.prepare("SELECT COUNT(*) AS n FROM design_references WHERE project_id = ?").get(e).n:this.db.prepare("SELECT COUNT(*) AS n FROM design_references").get().n}}export{u as DesignSystem};
@@ -1,4 +1,4 @@
1
- import{extractEntityCandidates as y}from"./entity-extract.js";const R=6;function N(t,i,_,a){const s=y(_);if(s.length===0)return{created:0,linked:0,entities:0};const d=t.prepare("SELECT id FROM entities WHERE project_id = ? AND LOWER(name) = LOWER(?) LIMIT 1"),l=t.prepare("INSERT INTO entities (project_id, name, type, created_by) VALUES (?, ?, ?, 'auto-extract')"),h=t.prepare("SELECT id, weight FROM relationships WHERE project_id = ? AND source_entity_id = ? AND target_entity_id = ? AND relationship_type = 'co_occurs' LIMIT 1"),u=t.prepare("UPDATE relationships SET weight = MIN(1.0, weight + 0.1) WHERE id = ?"),p=t.prepare("INSERT INTO relationships (project_id, source_entity_id, target_entity_id, relationship_type, weight, confidence, source_memory, extraction_method, created_by) VALUES (?, ?, ?, 'co_occurs', 0.3, 1.0, ?, 'auto-extract', 'auto-extract')");let r=0;const E=[];for(const e of s)try{const n=d.get(i,e.name);if(n){E.push(n.id);continue}const c=l.run(i,e.name,e.type);E.push(c.lastInsertRowid),r++}catch{}let g=0;const o=E.slice(0,R);for(let e=0;e<o.length;e++)for(let n=e+1;n<o.length;n++){const c=Math.min(o[e],o[n]),f=Math.max(o[e],o[n]);if(c!==f)try{const m=h.get(i,c,f);m?u.run(m.id):(p.run(i,c,f,a??null),g++)}catch{}}return{created:r,linked:g,entities:E.length}}function T(t,i,_={}){const a=t.prepare(`SELECT problem, validated_fix FROM memory_artifacts
1
+ import{extractEntityCandidates as y}from"./entity-extract.js";import{redactCredentials as R}from"./security.js";const N=6;function L(t,i,_,a){const s=y(R(_));if(s.length===0)return{created:0,linked:0,entities:0};const d=t.prepare("SELECT id FROM entities WHERE project_id = ? AND LOWER(name) = LOWER(?) LIMIT 1"),l=t.prepare("INSERT INTO entities (project_id, name, type, created_by) VALUES (?, ?, ?, 'auto-extract')"),h=t.prepare("SELECT id, weight FROM relationships WHERE project_id = ? AND source_entity_id = ? AND target_entity_id = ? AND relationship_type = 'co_occurs' LIMIT 1"),u=t.prepare("UPDATE relationships SET weight = MIN(1.0, weight + 0.1) WHERE id = ?"),p=t.prepare("INSERT INTO relationships (project_id, source_entity_id, target_entity_id, relationship_type, weight, confidence, source_memory, extraction_method, created_by) VALUES (?, ?, ?, 'co_occurs', 0.3, 1.0, ?, 'auto-extract', 'auto-extract')");let r=0;const E=[];for(const e of s)try{const n=d.get(i,e.name);if(n){E.push(n.id);continue}const c=l.run(i,e.name,e.type);E.push(c.lastInsertRowid),r++}catch{}let m=0;const o=E.slice(0,N);for(let e=0;e<o.length;e++)for(let n=e+1;n<o.length;n++){const c=Math.min(o[e],o[n]),f=Math.max(o[e],o[n]);if(c!==f)try{const g=h.get(i,c,f);g?u.run(g.id):(p.run(i,c,f,a??null),m++)}catch{}}return{created:r,linked:m,entities:E.length}}function S(t,i,_={}){const a=t.prepare(`SELECT problem, validated_fix FROM memory_artifacts
2
2
  WHERE project_id = ? AND needs_review = 0 AND supersedes_id IS NULL
3
- ORDER BY id DESC LIMIT ?`).all(i,_.limit??5e3);let s=0,d=0,l=0;return t.transaction(u=>{for(const p of u){const r=N(t,i,`${p.problem}
4
- ${p.validated_fix??""}`);s+=r.created,d+=r.linked,l+=r.entities}})(a),{created:s,linked:d,entities:l,artifactsScanned:a.length}}export{T as backfillEntities,N as populateEntities};
3
+ ORDER BY id DESC LIMIT ?`).all(i,_.limit??5e3);let s=0,d=0,l=0;return t.transaction(u=>{for(const p of u){const r=L(t,i,`${p.problem}
4
+ ${p.validated_fix??""}`);s+=r.created,d+=r.linked,l+=r.entities}})(a),{created:s,linked:d,entities:l,artifactsScanned:a.length}}export{S as backfillEntities,L as populateEntities};
@@ -1,3 +1,3 @@
1
- import{c as u,formatTable as y,printSection as f,printSuccess as w,printWarning as $}from"./cli.js";import{enforceIngestRetention as _,ingestedCategories as A,RETENTION_GRACE_DAYS as x}from"./retention.js";import{redactBackfill as S}from"./redact-backfill.js";import{scanBackupHygiene as R,listBackups as v,defaultBackupDir as D,readScheduledStatus as E,scheduledStatusNote as B}from"./backup.js";import{fmtBytes as M}from"./backup-cli.js";function T(r,o=" "){for(const[a,n]of Object.entries(r).sort((e,t)=>t[1]-e[1]))console.log(`${o}${String(n).padStart(6)} ${a}`)}function F(r,o){const a=typeof o.days=="string"?Number(o.days):void 0,n=o.yes===!0&&o["dry-run"]!==!0,e=_(r,{days:a,dryRun:!n,skipGrace:n,actor:"cli"});if(e.status==="disabled"){console.log("Ingest retention is off (WYRM_INGEST_RETAIN_DAYS=0), so nothing ages out.");return}f(n?`Ingest retention: erased (limit ${e.days}d)`:`Ingest retention (dry run, limit ${e.days}d)`),console.log(y(["Category",n?"Erased":"Rows past the limit"],e.categories.map(s=>[s.category,String(n?s.deleted:s.matched)])));const t={};for(const s of e.categories)for(const[i,c]of Object.entries(s.counts))t[i]=(t[i]??0)+c;if(Object.keys(t).length>0&&(console.log(n?" Removed, per table:":" Would remove, per table:"),T(t," ")),e.graceEndsAt){const s=Date.parse(e.graceEndsAt)<=Date.now();console.log(` Automatic enforcement by maintenance ${s?"has been active since":"starts"} ${e.graceEndsAt.slice(0,10)}.`)}else console.log(` Automatic enforcement has not started: the first maintenance run records a preview and waits ${x} days.`);n?(w(`Erased ${e.deleted} ingested row(s) and ${e.derived} derived row(s); the erasure is recorded in erasure_log.`),console.log(" Snapshots still hold the erased rows until they rotate out: wyrm backup prune, wyrm backup legacy.")):(console.log(u.dim(`
2
- Dry run: nothing was deleted. --yes erases these now, without waiting for the grace period.`)),console.log(u.dim(" Change the limit with WYRM_INGEST_RETAIN_DAYS=<days> (0 turns retention off).")))}function O(r,o){const a=o.apply===!0&&o["dry-run"]!==!0,n=r.getDatabase(),e=S(n,{apply:a,ingestedCategories:A(r)});f(a?"Redaction back-fill":"Redaction back-fill (dry run)"),console.log(y(["Table","Scanned","Rows with credentials","Fields"],e.tables.map(s=>[s.table,String(s.scanned),String(s.rows),String(s.fields)])));const t=e.tables.reduce((s,i)=>s+i.rows,0);if(!a){console.log(u.dim(`
3
- Dry run: nothing changed. ${t>0?"Re-run with --apply (add --vacuum to rewrite the file).":"Nothing to redact."}`));return}w(`Redacted ${t} row(s). Matched text is never printed.`),e.vectorsDropped>0&&console.log(` Dropped ${e.vectorsDropped} vector(s) built from the old text: wyrm index rebuild re-embeds them.`),o.vacuum===!0?(r.vacuum(),r.checkpoint(),console.log(" Vacuumed: the file is rewritten, so pages that held the old text are gone.")):t>0&&console.log(u.dim(" Freed pages may still hold the old text: wyrm maintenance redact --apply --vacuum rewrites the file.")),t>0&&$("Existing snapshots still hold the unredacted rows. Take a fresh one (wyrm backup create --encrypt), then remove the old ones (wyrm backup prune, wyrm backup legacy).")}const C=2;function N(r){const o=Number(r.WYRM_BACKUP_MAX_AGE_DAYS);return Number.isFinite(o)&&o>0?o:C}function K(r,o=process.env,a=Date.now()){const n=D(r,o),e=R({dbPath:r,dir:n}),t=[];if(process.platform==="win32"&&t.push({ok:!0,label:"Backup permissions",detail:"not checked on Windows (access there is governed by ACLs, not mode bits)"}),e.exposed.length>0&&t.push({ok:!1,label:"Backup permissions",detail:`${e.exposed.length} backup file(s) or folder(s) in the Wyrm home are readable by other users`,fix:"wyrm backup legacy --dry-run, then wyrm backup legacy --apply (files 0600, folders 0700)"}),e.legacy.length>0){const l=e.legacy.reduce((d,p)=>d+p.bytes,0);t.push({ok:!1,label:"Stray backups",detail:`${e.legacy.length} database copy(ies) outside rotation (${M(l)}); nothing ages them out, and they keep rows an erasure removed`,fix:"wyrm backup legacy --dry-run, then --apply --adopt to move them into rotation"})}const s=N(o),i=v(n),c=i[0],m="the daily sweep and wyrm maintenance take one a day unless WYRM_AUTO_BACKUP=0",g=B(E(n),c??null,a),h=g?`; ${g}`:"";if(!c)t.push({ok:!1,label:"Backups",detail:`no snapshot of the store in ${n}: nothing to restore from if the store is lost or damaged${h}`,fix:`wyrm backup create --encrypt (then ${m})`});else{const l=Math.max(0,a-c.at.getTime()),d=Math.floor(l/864e5),p=d>0?`${d}d old`:`${Math.floor(l/36e5)}h old`,b=i.filter(k=>k.encrypted).length;l>s*864e5?t.push({ok:!1,label:"Backups",detail:`the newest snapshot (${c.file}) is ${p}, past the ${s}d limit (WYRM_BACKUP_MAX_AGE_DAYS); scheduled snapshots have stopped${h}`,fix:`wyrm backup create --encrypt now, then check that ${m}`}):t.push({ok:!0,label:"Backups",detail:`${i.length} rotated snapshot(s) (${b} encrypted), newest ${p}${t.length===0?", no stray or exposed copies":""}${h}`})}return t}export{C as DEFAULT_BACKUP_MAX_AGE_DAYS,F as cmdForgetRetention,O as cmdMaintenanceRedact,K as doctorBackupRows,T as printErasureCounts};
1
+ import{c as g,formatTable as w,printSection as $,printSuccess as k,printWarning as y}from"./cli.js";import{enforceIngestRetention as x,RETENTION_GRACE_DAYS as R}from"./retention.js";import{redactBackfill as S}from"./redact-backfill.js";import{scanBackupHygiene as v,listBackups as D,defaultBackupDir as E,readScheduledStatus as j,scheduledStatusNote as C}from"./backup.js";import{fmtBytes as B}from"./backup-cli.js";function _(r,n=" "){for(const[l,a]of Object.entries(r).sort((e,s)=>s[1]-e[1]))console.log(`${n}${String(a).padStart(6)} ${l}`)}function F(r,n){const l=typeof n.days=="string"?Number(n.days):void 0,a=n.yes===!0&&n["dry-run"]!==!0,e=x(r,{days:l,dryRun:!a,skipGrace:a,actor:"cli"});if(e.status==="disabled"){console.log("Ingest retention is off (WYRM_INGEST_RETAIN_DAYS=0), so nothing ages out.");return}$(a?`Ingest retention: erased (limit ${e.days}d)`:`Ingest retention (dry run, limit ${e.days}d)`),console.log(w(["Category",a?"Erased":"Rows past the limit"],e.categories.map(i=>[i.category,String(a?i.deleted:i.matched)])));const s={};for(const i of e.categories)for(const[c,d]of Object.entries(i.counts))s[c]=(s[c]??0)+d;if(Object.keys(s).length>0&&(console.log(a?" Removed, per table:":" Would remove, per table:"),_(s," ")),e.graceEndsAt){const i=Date.parse(e.graceEndsAt)<=Date.now();console.log(` Automatic enforcement by maintenance ${i?"has been active since":"starts"} ${e.graceEndsAt.slice(0,10)}.`)}else console.log(` Automatic enforcement has not started: the first maintenance run records a preview and waits ${R} days.`);a?(k(`Erased ${e.deleted} ingested row(s) and ${e.derived} derived row(s); the erasure is recorded in erasure_log.`),console.log(" Snapshots still hold the erased rows until they rotate out: wyrm backup prune, wyrm backup legacy.")):(console.log(g.dim(`
2
+ Dry run: nothing was deleted. --yes erases these now, without waiting for the grace period.`)),console.log(g.dim(" Change the limit with WYRM_INGEST_RETAIN_DAYS=<days> (0 turns retention off).")))}function M(r){return r.length===0?"":r.slice(0,20).join(" ")+(r.length>20?" ...":"")}function U(r,n){const l=n.apply===!0&&n["dry-run"]!==!0,a=r.getDatabase(),e=S(a,{apply:l});$(l?"Redaction back-fill":"Redaction back-fill (dry run)");const s=e.tables.reduce((t,o)=>t+o.scanned,0);console.log(` Scanned ${s} row(s) in ${e.tables.length} table(s).`);const i=e.tables.filter(t=>t.rows>0);i.length>0&&console.log(w(["Table","Scanned","Rows with credentials","Fields"],i.map(t=>[t.table,String(t.scanned),String(t.rows),String(t.fields)])));const c=e.tables.reduce((t,o)=>t+o.rows,0),d={};for(const t of e.tables)for(const[o,p]of Object.entries(t.patterns))d[o]=(d[o]??0)+p;Object.keys(d).length>0&&(console.log(" Rows per pattern (a row can match more than one):"),_(d," "));const m=e.tables.flatMap(t=>Object.entries(t.skipped).map(([o,p])=>`${t.table}.${o} (${p})`));if(m.length>0){const t=e.tables.reduce((o,p)=>o+Object.values(p.skipped).reduce((b,A)=>b+A,0),0);y(`Skipped ${t} value(s) that could not be parsed or rewritten, left as they are: ${m.join(", ")}`)}const h=e.tables.flatMap(t=>Object.entries(t.collisions).map(([o,p])=>`${t.table}.${o} (${p})`));h.length>0&&y(l?`Rows left as they are, their redacted key collides with another row's: ${h.join(", ")}`:`Rows the apply would leave as they are, their redacted key would collide with another row's: ${h.join(", ")}`);const f=Object.entries(e.credentialIdentifiers).map(([t,o])=>`${t} (${o})`);f.length>0&&y(`Ids and keys that look like a credential, left as they are (the tools refuse new ones): ${f.join(", ")}`);const u=M(e.staleConfirmations);if(!l){u&&console.log(` Confirmed failure records that would change: ${e.staleConfirmations.length}. Confirm them again after --apply: wyrm failure confirm ${u}`),e.ftsDrifted.length>0&&console.log(` Full-text index the apply would rebuild, its counts disagree with its table: ${e.ftsDrifted.join(", ")}`);const t=c>0||e.ftsDrifted.length>0;console.log(g.dim(`
3
+ Dry run: nothing changed. ${t?"Re-run with --apply (add --vacuum to rewrite the file).":"Nothing to redact."}`));return}k(`Redacted ${c} row(s). Matched text is never printed.`),e.vectorsDropped>0&&console.log(` Dropped ${e.vectorsDropped} vector(s) built from the old text: wyrm index rebuild re-embeds them.`),e.ftsRebuilt.length>0&&console.log(` Rebuilt full-text index: ${e.ftsRebuilt.join(", ")}`),e.uniqueVariants>0&&console.log(` Kept ${e.uniqueVariants} key(s) unique with a #id suffix`),u&&console.log(` Confirmed failure records changed: ${e.staleConfirmations.length}. Their confirmations no longer match; confirm them again: wyrm failure confirm ${u}`),n.vacuum===!0?(r.vacuum(),r.checkpoint(),console.log(" Vacuumed: the file is rewritten, so pages that held the old text are gone.")):c>0&&console.log(g.dim(" Freed pages may still hold the old text: wyrm maintenance redact --apply --vacuum rewrites the file.")),c>0&&y("Existing snapshots still hold the unredacted rows. Take a fresh one (wyrm backup create --encrypt), then remove the old ones (wyrm backup prune, wyrm backup legacy).")}const T=2;function I(r){const n=Number(r.WYRM_BACKUP_MAX_AGE_DAYS);return Number.isFinite(n)&&n>0?n:T}function K(r,n=process.env,l=Date.now()){const a=E(r,n),e=v({dbPath:r,dir:a}),s=[];if(process.platform==="win32"&&s.push({ok:!0,label:"Backup permissions",detail:"not checked on Windows (access there is governed by ACLs, not mode bits)"}),e.exposed.length>0&&s.push({ok:!1,label:"Backup permissions",detail:`${e.exposed.length} backup file(s) or folder(s) in the Wyrm home are readable by other users`,fix:"wyrm backup legacy --dry-run, then wyrm backup legacy --apply (files 0600, folders 0700)"}),e.legacy.length>0){const u=e.legacy.reduce((t,o)=>t+o.bytes,0);s.push({ok:!1,label:"Stray backups",detail:`${e.legacy.length} database copy(ies) outside rotation (${B(u)}); nothing ages them out, and they keep rows an erasure removed`,fix:"wyrm backup legacy --dry-run, then --apply --adopt to move them into rotation"})}const i=I(n),c=D(a),d=c[0],m="the daily sweep and wyrm maintenance take one a day unless WYRM_AUTO_BACKUP=0",h=C(j(a),d??null,l),f=h?`; ${h}`:"";if(!d)s.push({ok:!1,label:"Backups",detail:`no snapshot of the store in ${a}: nothing to restore from if the store is lost or damaged${f}`,fix:`wyrm backup create --encrypt (then ${m})`});else{const u=Math.max(0,l-d.at.getTime()),t=Math.floor(u/864e5),o=t>0?`${t}d old`:`${Math.floor(u/36e5)}h old`,p=c.filter(b=>b.encrypted).length;u>i*864e5?s.push({ok:!1,label:"Backups",detail:`the newest snapshot (${d.file}) is ${o}, past the ${i}d limit (WYRM_BACKUP_MAX_AGE_DAYS); scheduled snapshots have stopped${f}`,fix:`wyrm backup create --encrypt now, then check that ${m}`}):s.push({ok:!0,label:"Backups",detail:`${c.length} rotated snapshot(s) (${p} encrypted), newest ${o}${s.length===0?", no stray or exposed copies":""}${f}`})}return s}export{T as DEFAULT_BACKUP_MAX_AGE_DAYS,F as cmdForgetRetention,U as cmdMaintenanceRedact,K as doctorBackupRows,_ as printErasureCounts};
@@ -1,8 +1,8 @@
1
- import{getActor as u}from"./handlers/boundary.js";const s=new Set;function m(t){return s.add(t),()=>{s.delete(t)}}function _(t){const e={};return t.match&&(e.match=!0),typeof t.dataset=="string"&&t.dataset&&(e.dataset=t.dataset.slice(0,128)),typeof t.older_than_days=="number"&&Number.isFinite(t.older_than_days)&&(e.older_than_days=Math.floor(t.older_than_days)),t.project&&(e.project=!0),e}function g(t){const e={};for(const[r,o]of Object.entries(t))/^[a-z_]{1,64}$/.test(r)&&Number.isFinite(o)&&o>0&&(e[r]=Math.floor(o));return e}function y(t,e){const r=g(e.counts),o=Object.values(r).reduce((n,l)=>n+l,0),d=_(e.predicate),a=u(),c=t.prepare(`
1
+ import{getActor as l}from"./handlers/boundary.js";const s=new Set;function m(t){return s.add(t),()=>{s.delete(t)}}function _(t){const e={};return t.match&&(e.match=!0),typeof t.dataset=="string"&&t.dataset&&(e.dataset=t.dataset.slice(0,128)),typeof t.older_than_days=="number"&&Number.isFinite(t.older_than_days)&&(e.older_than_days=Math.floor(t.older_than_days)),t.project&&(e.project=!0),t.truth&&(e.truth=!0),e}function g(t){const e={};for(const[o,r]of Object.entries(t))/^[a-z_]{1,64}$/.test(o)&&Number.isFinite(r)&&r>0&&(e[o]=Math.floor(r));return e}function y(t,e){const o=g(e.counts),r=Object.values(o).reduce((n,u)=>n+u,0),d=_(e.predicate),a=l(),c=t.prepare(`
2
2
  INSERT INTO erasure_log (mode, reason, category, predicate_json, counts_json, total, actor, agent_id, run_id)
3
3
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
4
4
  RETURNING id, erased_at
5
- `).get(e.mode,e.reason,e.category?e.category.slice(0,128):null,JSON.stringify(d),JSON.stringify(r),o,e.actor??null,a.agent_id,a.run_id),i={id:c.id,erased_at:c.erased_at,mode:e.mode,reason:e.reason,category:e.category,predicate:d,counts:r,total:o,actor:e.actor??null,agent_id:a.agent_id,run_id:a.run_id};for(const n of s)n(i,t);return i}function S(t,e=50){return t.prepare(`
5
+ `).get(e.mode,e.reason,e.category?e.category.slice(0,128):null,JSON.stringify(d),JSON.stringify(o),r,e.actor??null,a.agent_id,a.run_id),i={id:c.id,erased_at:c.erased_at,mode:e.mode,reason:e.reason,category:e.category,predicate:d,counts:o,total:r,actor:e.actor??null,agent_id:a.agent_id,run_id:a.run_id};for(const n of s)n(i,t);return i}function h(t,e=50){return t.prepare(`
6
6
  SELECT id, erased_at, mode, reason, category, predicate_json, counts_json, total, actor, agent_id, run_id
7
7
  FROM erasure_log ORDER BY id DESC LIMIT ?
8
- `).all(Math.max(1,Math.min(1e3,Math.floor(e)))).map(o=>({id:Number(o.id),erased_at:String(o.erased_at),mode:o.mode,reason:o.reason,category:o.category??null,predicate:JSON.parse(String(o.predicate_json)),counts:JSON.parse(String(o.counts_json)),total:Number(o.total),actor:o.actor??null,agent_id:o.agent_id??null,run_id:o.run_id??null}))}export{S as listErasures,m as onErasureRecorded,y as recordErasure};
8
+ `).all(Math.max(1,Math.min(1e3,Math.floor(e)))).map(r=>({id:Number(r.id),erased_at:String(r.erased_at),mode:r.mode,reason:r.reason,category:r.category??null,predicate:JSON.parse(String(r.predicate_json)),counts:JSON.parse(String(r.counts_json)),total:Number(r.total),actor:r.actor??null,agent_id:r.agent_id??null,run_id:r.run_id??null}))}export{h as listErasures,m as onErasureRecorded,y as recordErasure};
package/dist/erasure.js CHANGED
@@ -1 +1,15 @@
1
- import{recordErasure as y}from"./erasure-log.js";import"./governance.js";function F(t){return`lake:${t}`}const O={tool_call_log:"DELETE FROM tool_call_log WHERE id = ?",external_call_log:"DELETE FROM external_call_log WHERE id = ?",llm_query_log:"DELETE FROM llm_query_log WHERE id = ?",usage_events:"DELETE FROM usage_events WHERE id = ?",events:"DELETE FROM events WHERE cursor = ?",served_records:"DELETE FROM served_records WHERE rowid = ?",session_seen_artifacts:"DELETE FROM session_seen_artifacts WHERE rowid = ?",decision_edges:"DELETE FROM decision_edges WHERE id = ?",indexing_queue:"DELETE FROM indexing_queue WHERE id = ?",vectors:"DELETE FROM vectors WHERE id = ?",memory_artifacts:"DELETE FROM memory_artifacts WHERE id = ?",data_lake:"DELETE FROM data_lake WHERE id = ?"},M=3,T=/[A-Za-z0-9_-]/;function v(t,c){if(!c)return!1;let s=0;for(;;){const n=t.indexOf(c,s);if(n<0)return!1;const d=n>0?t[n-1]:"",E=t[n+c.length]??"";if(!(d&&T.test(d))&&!(E&&T.test(E)))return!0;s=n+1}}function i(t,c,...s){try{return t.prepare(c).all(...s)}catch{return[]}}function h(t,c,s){const n=new Set(c.map(e=>e.id)),d=[...new Set(c.map(e=>e.key).filter(Boolean))],E=new Set(d.map(F)),m=s.subject&&s.subject.length>=M?s.subject.toLowerCase():null,l=(...e)=>e.some(o=>{if(!o)return!1;if(m&&o.toLowerCase().includes(m))return!0;for(const f of d)if(v(o,f))return!0;return!1}),R={},r=(e,o)=>{(R[e]??=[]).push(o)};if(n.size===0)return{counts:{},total:0,erasureId:null};for(const e of n)r("data_lake",e);const a=new Set;if(E.size>0)for(const e of i(t,"SELECT id, tags FROM memory_artifacts WHERE instr(tags, 'lake:') > 0"))e.tags.split(",").some(o=>E.has(o.trim()))&&a.add(e.id);for(const e of a)r("memory_artifacts",e);for(const e of i(t,"SELECT id, content_id FROM vectors WHERE content_type = 'note'"))n.has(e.content_id)&&r("vectors",e.id);if(a.size>0)for(const e of i(t,"SELECT id, content_id FROM vectors WHERE content_type = 'artifact'"))a.has(e.content_id)&&r("vectors",e.id);for(const e of i(t,"SELECT id, content_type, content_id FROM indexing_queue WHERE content_type IN ('note', 'artifact')"))(e.content_type==="note"&&n.has(e.content_id)||e.content_type==="artifact"&&a.has(e.content_id))&&r("indexing_queue",e.id);for(const e of i(t,"SELECT cursor, ref_table, ref_id FROM events WHERE ref_table IN ('memory_artifacts', 'data_lake')")){const o=Number(e.ref_id);(e.ref_table==="memory_artifacts"&&a.has(o)||e.ref_table==="data_lake"&&n.has(o))&&r("events",e.cursor)}if(a.size>0){for(const e of i(t,"SELECT rowid, artifact_id FROM served_records WHERE artifact_kind = 'mem'"))a.has(e.artifact_id)&&r("served_records",e.rowid);for(const e of i(t,"SELECT rowid, artifact_id FROM session_seen_artifacts WHERE artifact_kind = 'memory'"))a.has(e.artifact_id)&&r("session_seen_artifacts",e.rowid)}for(const e of i(t,"SELECT id, from_kind, from_id, to_kind, to_id FROM decision_edges WHERE from_kind IN ('data', 'artifact') OR to_kind IN ('data', 'artifact')")){const o=(f,u)=>f==="data"&&n.has(u)||f==="artifact"&&a.has(u);(o(e.from_kind,e.from_id)||o(e.to_kind,e.to_id))&&r("decision_edges",e.id)}if(d.length>0||m){for(const e of i(t,"SELECT id, args_summary AS a, error_message AS e FROM tool_call_log"))l(e.a,e.e)&&r("tool_call_log",e.id);for(const e of i(t,"SELECT id, args_summary AS a, error_message AS e FROM external_call_log"))l(e.a,e.e)&&r("external_call_log",e.id);for(const e of i(t,"SELECT id, query AS q, context_summary AS c, response AS r, error_message AS e FROM llm_query_log"))l(e.q,e.c,e.r,e.e)&&r("llm_query_log",e.id);for(const e of i(t,"SELECT id, error_message AS e FROM usage_events WHERE error_message IS NOT NULL"))l(e.e)&&r("usage_events",e.id)}const _={};for(const[e,o]of Object.entries(R))_[e]=new Set(o).size;const g=Object.values(_).reduce((e,o)=>e+o,0);if(s.dryRun)return{counts:_,total:g,erasureId:null};const S=[...new Set(c.map(e=>e.category))];let L=null;return t.transaction(()=>{for(const e of Object.keys(O)){const o=R[e];if(!o?.length)continue;const f=t.prepare(O[e]);for(const u of new Set(o))f.run(u)}L=y(t,{mode:"erase",reason:s.reason,category:S.length===1?S[0]:null,predicate:s.predicate,counts:_,actor:s.actor??null}).id})(),{counts:_,total:g,erasureId:L}}export{h as eraseLakeRows,F as lakeProvenanceTag,v as quotesKey};
1
+ import{isAbsolute as I,win32 as q}from"node:path";import{ENCRYPTED_PREFIX as x}from"./crypto.js";import{briefProjectId as w}from"./brief-cache.js";import{recordErasure as j}from"./erasure-log.js";import{recordGovernanceEvent as P}from"./governance.js";import{resolveProjectForPath as U}from"./project-resolve.js";function z(o){return`lake:${o}`}const b={tool_call_log:"DELETE FROM tool_call_log WHERE id = ?",external_call_log:"DELETE FROM external_call_log WHERE id = ?",llm_query_log:"DELETE FROM llm_query_log WHERE id = ?",usage_events:"DELETE FROM usage_events WHERE id = ?",events:"DELETE FROM events WHERE cursor = ?",served_records:"DELETE FROM served_records WHERE rowid = ?",session_seen_artifacts:"DELETE FROM session_seen_artifacts WHERE rowid = ?",decision_edges:"DELETE FROM decision_edges WHERE id = ?",indexing_queue:"DELETE FROM indexing_queue WHERE id = ?",vectors:"DELETE FROM vectors WHERE id = ?",memory_artifacts:"DELETE FROM memory_artifacts WHERE id = ?",data_lake:"DELETE FROM data_lake WHERE id = ?"},$=3,T=/[A-Za-z0-9_-]/;function J(o,r){if(!r)return!1;let i=0;for(;;){const a=o.indexOf(r,i);if(a<0)return!1;const d=a>0?o[a-1]:"",n=o[a+r.length]??"";if(!(d&&T.test(d))&&!(n&&T.test(n)))return!0;i=a+1}}function c(o,r,...i){try{return o.prepare(r).all(...i)}catch{return[]}}function ae(o,r,i){const a=new Set(r.map(e=>e.id)),d=[...new Set(r.map(e=>e.key).filter(Boolean))],n=new Set(d.map(z)),_=i.subject&&i.subject.length>=$?i.subject.toLowerCase():null,g=(...e)=>e.some(l=>{if(!l)return!1;if(_&&l.toLowerCase().includes(_))return!0;for(const S of d)if(J(l,S))return!0;return!1}),m={},u=(e,l)=>{(m[e]??=[]).push(l)};if(a.size===0)return{counts:{},total:0,erasureId:null};for(const e of a)u("data_lake",e);const R=new Set;if(n.size>0)for(const e of c(o,"SELECT id, tags FROM memory_artifacts WHERE instr(tags, 'lake:') > 0"))e.tags.split(",").some(l=>n.has(l.trim()))&&R.add(e.id);for(const e of R)u("memory_artifacts",e);for(const e of c(o,"SELECT id, content_id FROM vectors WHERE content_type = 'note'"))a.has(e.content_id)&&u("vectors",e.id);if(R.size>0)for(const e of c(o,"SELECT id, content_id FROM vectors WHERE content_type = 'artifact'"))R.has(e.content_id)&&u("vectors",e.id);for(const e of c(o,"SELECT id, content_type, content_id FROM indexing_queue WHERE content_type IN ('note', 'artifact')"))(e.content_type==="note"&&a.has(e.content_id)||e.content_type==="artifact"&&R.has(e.content_id))&&u("indexing_queue",e.id);for(const e of c(o,"SELECT cursor, ref_table, ref_id FROM events WHERE ref_table IN ('memory_artifacts', 'data_lake')")){const l=Number(e.ref_id);(e.ref_table==="memory_artifacts"&&R.has(l)||e.ref_table==="data_lake"&&a.has(l))&&u("events",e.cursor)}if(R.size>0){for(const e of c(o,"SELECT rowid, artifact_id FROM served_records WHERE artifact_kind = 'mem'"))R.has(e.artifact_id)&&u("served_records",e.rowid);for(const e of c(o,"SELECT rowid, artifact_id FROM session_seen_artifacts WHERE artifact_kind = 'memory'"))R.has(e.artifact_id)&&u("session_seen_artifacts",e.rowid)}for(const e of c(o,"SELECT id, from_kind, from_id, to_kind, to_id FROM decision_edges WHERE from_kind IN ('data', 'artifact') OR to_kind IN ('data', 'artifact')")){const l=(S,p)=>S==="data"&&a.has(p)||S==="artifact"&&R.has(p);(l(e.from_kind,e.from_id)||l(e.to_kind,e.to_id))&&u("decision_edges",e.id)}if(d.length>0||_){for(const e of c(o,"SELECT id, args_summary AS a, error_message AS e FROM tool_call_log"))g(e.a,e.e)&&u("tool_call_log",e.id);for(const e of c(o,"SELECT id, args_summary AS a, error_message AS e FROM external_call_log"))g(e.a,e.e)&&u("external_call_log",e.id);for(const e of c(o,"SELECT id, query AS q, context_summary AS c, response AS r, error_message AS e FROM llm_query_log"))g(e.q,e.c,e.r,e.e)&&u("llm_query_log",e.id);for(const e of c(o,"SELECT id, error_message AS e FROM usage_events WHERE error_message IS NOT NULL"))g(e.e)&&u("usage_events",e.id)}const O={};for(const[e,l]of Object.entries(m))O[e]=new Set(l).size;const L=Object.values(O).reduce((e,l)=>e+l,0);if(i.dryRun)return{counts:O,total:L,erasureId:null};const f=[...new Set(r.map(e=>e.category))];let y=null;return o.transaction(()=>{for(const e of Object.keys(b)){const l=m[e];if(!l?.length)continue;const S=o.prepare(b[e]);for(const p of new Set(l))S.run(p)}y=j(o,{mode:"erase",reason:i.reason,category:f.length===1?f[0]:null,predicate:i.predicate,counts:O,actor:i.actor??null}).id})(),{counts:O,total:L,erasureId:y}}const V=8,B=12,v=/\[REDACTED\]|\[withheld[^\]]*\]/gi;function D(o){const r=(o??"").trim();return!r||r.startsWith(x)?null:r.replace(v,"").replace(/\s+/g,"").length>=V?r:null}function C(o){const r=D(o);if(r===null)return null;const i=r.replace(v," ").trim();return i.replace(/\s+/g,"").length<B?null:/[\p{N}\p{P}\p{S}]/u.test(i)||new RegExp("\\p{Ll}","u").test(i)&&new RegExp("\\p{Lu}","u").test(i)||i.split(/\s+/).filter(Boolean).length>=2?r:null}function N(o,r){if(!r)return!1;const i=T.test(r[0]),a=T.test(r[r.length-1]);let d=0;for(;;){const n=o.indexOf(r,d);if(n<0)return!1;const _=n>0?o[n-1]:"",g=o[n+r.length]??"";if(!(i&&_&&T.test(_))&&!(a&&g&&T.test(g)))return!0;d=n+1}}function Y(o,r){if(N(o,r))return!0;const i=JSON.stringify(r).slice(1,-1);return i!==r&&N(o,i)}const G=/"(project_id|projectId)"\s*:\s*"?(\d+)"?/g,K=/"(project_name|projectName|projectPath|project_path|cwd|project)"\s*:\s*"((?:[^"\\]|\\.)*)("?)/g,Q=new Set(["project_name","projectName"]),X=new Set(["projectPath","project_path","cwd"]);function Z(o,r,i){const a=[];for(const d of o.matchAll(G))a.push(d.index+d[0].length>=o.length?null:Number(d[2]));for(const d of o.matchAll(K)){if(d[3]!=='"'){a.push(null);continue}let n;try{n=JSON.parse(`"${d[2]}"`)}catch{a.push(null);continue}a.push(Q.has(d[1])?r(n):X.has(d[1])?i(n):r(n)??i(n))}return a}function ee(o,r){const i=new Map,a=n=>{if(!i.has(`n:${n}`)){const _=c(o,"SELECT id FROM projects WHERE name = ?",n)[0];i.set(`n:${n}`,_?.id??null)}return i.get(`n:${n}`)??null},d=n=>{if(!I(n)&&!q.isAbsolute(n))return null;if(!i.has(`p:${n}`)){let _=null;try{_=U(o,n).id}catch{_=null}i.set(`p:${n}`,_)}return i.get(`p:${n}`)??null};return(n,_)=>{const g=_?Z(_,a,d):[];return g.some(m=>m!==null&&m!==r)?"elsewhere":n!=null?n===r?"truth":"elsewhere":g.length===0||g.includes(null)?"none":"truth"}}const te={tool_call_log:"DELETE FROM tool_call_log WHERE id = ?",external_call_log:"DELETE FROM external_call_log WHERE id = ?",llm_query_log:"DELETE FROM llm_query_log WHERE id = ?",usage_events:"DELETE FROM usage_events WHERE id = ?",events:"DELETE FROM events WHERE cursor = ?",served_records:"DELETE FROM served_records WHERE rowid = ?",served_counts:"DELETE FROM served_counts WHERE rowid = ?",session_seen_artifacts:"DELETE FROM session_seen_artifacts WHERE rowid = ?",write_ledger:"DELETE FROM write_ledger WHERE id = ?",sync_conflicts:"DELETE FROM sync_conflicts WHERE id = ?",run_briefs:"DELETE FROM run_briefs WHERE rowid = ?",goal_iterations:"DELETE FROM goal_iterations WHERE id = ?",agent_actions:"DELETE FROM agent_actions WHERE id = ?",decision_edges:"DELETE FROM decision_edges WHERE id = ?",ground_truths:"DELETE FROM ground_truths WHERE id = ?"};function de(o,r,i){const a=o.prepare("SELECT id, value FROM ground_truths WHERE project_id = ? AND category = ? AND key = ? ORDER BY id").all(r.project_id,r.category,r.key);if(a.length===0)return{counts:{},total:0,erasureId:null,truthIds:[],tombstones:0,mentions:0,unattributed:0,notSwept:0};const d=a.map(t=>t.id),n=new Set(d),_=[...new Set(a.map(t=>D(t.value)).filter(t=>t!==null))],g=(t,E)=>t.length>0&&E.some(s=>!!s&&t.some(h=>Y(s,h))),m=(...t)=>g(_,t),u=_.filter(t=>C(t)!==null),R=new Set(a.map(t=>(t.value??"").trim()).filter(t=>t&&C(t)===null)).size,O=(...t)=>g(u,t),L={},f=(t,E)=>{(L[t]??=[]).push(E)};for(const t of d)f("ground_truths",t);const y=new Set;for(const t of c(o,"SELECT id, from_kind, from_id, to_kind, to_id FROM decision_edges WHERE from_kind = 'truth' OR to_kind = 'truth'"))(t.from_kind==="truth"&&n.has(t.from_id)||t.to_kind==="truth"&&n.has(t.to_id))&&(y.add(t.id),f("decision_edges",t.id));for(const t of c(o,"SELECT cursor, ref_table, ref_id FROM events WHERE ref_table IN ('ground_truths', 'decision_edges')")){const E=Number(t.ref_id);(t.ref_table==="ground_truths"&&n.has(E)||t.ref_table==="decision_edges"&&y.has(E))&&f("events",t.cursor)}for(const t of c(o,"SELECT rowid, artifact_id FROM served_records WHERE artifact_kind = 'truth'"))n.has(t.artifact_id)&&f("served_records",t.rowid);for(const t of c(o,"SELECT rowid, artifact_id FROM served_counts WHERE artifact_kind = 'truth'"))n.has(t.artifact_id)&&f("served_counts",t.rowid);for(const t of c(o,"SELECT rowid, artifact_id FROM session_seen_artifacts WHERE artifact_kind = 'truth'"))n.has(t.artifact_id)&&f("session_seen_artifacts",t.rowid);for(const t of c(o,"SELECT id, ref_id FROM write_ledger WHERE ref_table = 'truth'"))t.ref_id!==null&&n.has(t.ref_id)&&f("write_ledger",t.id);for(const t of c(o,"SELECT id, local_kind, local_id, remote_payload FROM sync_conflicts"))(t.local_kind==="truth"&&n.has(t.local_id)||m(t.remote_payload))&&f("sync_conflicts",t.id);for(const t of c(o,"SELECT id, local_payload FROM sync_conflicts WHERE local_payload IS NOT NULL"))m(t.local_payload)&&f("sync_conflicts",t.id);let e=0,l=0;for(const t of c(o,"SELECT rowid, body_json FROM run_briefs"))w(t.body_json)===r.project_id&&f("run_briefs",t.rowid);if(_.length>0){for(const s of c(o,`
2
+ SELECT gi.id, g.project_id AS p, gi.observe_summary AS o, gi.orient_summary AS r,
3
+ gi.decided_action AS d, gi.action_result AS a, gi.notes AS n
4
+ FROM goal_iterations gi JOIN goals g ON g.id = gi.goal_id
5
+ WHERE g.project_id = ? OR g.project_id IS NULL
6
+ `,r.project_id))m(s.o,s.r,s.d,s.a,s.n)&&(s.p===r.project_id?f("goal_iterations",s.id):l++);for(const s of c(o,`
7
+ SELECT aa.id, g.project_id AS p, aa.summary AS s
8
+ FROM agent_actions aa LEFT JOIN goals g ON g.id = aa.goal_id
9
+ WHERE g.project_id = ? OR g.project_id IS NULL
10
+ `,r.project_id))m(s.s)&&(s.p===r.project_id?f("agent_actions",s.id):l++);const t=ee(o,r.project_id),E=(s,h,F,M,...k)=>{if(!O(...k))return;const W=t(F,M);W==="truth"?f(s,h):W==="none"&&l++};for(const s of c(o,"SELECT id, project_id AS p, args_summary AS a, error_message AS e FROM tool_call_log"))E("tool_call_log",s.id,s.p,s.a,s.a,s.e);for(const s of c(o,"SELECT id, args_summary AS a, error_message AS e FROM external_call_log"))E("external_call_log",s.id,void 0,s.a,s.a,s.e);for(const s of c(o,"SELECT id, project_id AS p, query AS q, context_summary AS c, response AS r, error_message AS e FROM llm_query_log"))E("llm_query_log",s.id,s.p,null,s.q,s.c,s.r,s.e);for(const s of c(o,"SELECT id, error_message AS e FROM usage_events WHERE error_message IS NOT NULL"))E("usage_events",s.id,void 0,null,s.e);for(const s of c(o,"SELECT problem AS p, validated_fix AS f FROM memory_artifacts"))m(s.p,s.f)&&e++}const S=c(o,`
11
+ SELECT COUNT(*) AS n FROM ground_truths t
12
+ WHERE t.project_id = ? AND t.category = ? AND t.key = ?
13
+ AND t.cross_project_visibility IN ('org', 'public')
14
+ AND (t.project_id IS NULL OR (SELECT sync_policy FROM projects WHERE id = t.project_id) IN ('cloud', 'team'))
15
+ `,r.project_id,r.category,r.key)[0]?.n??0,p={};for(const[t,E]of Object.entries(L))p[t]=new Set(E).size;const A=Object.values(p).reduce((t,E)=>t+E,0);if(i.dryRun)return{counts:p,total:A,erasureId:null,truthIds:d,tombstones:S,mentions:e,unattributed:l,notSwept:R};let H=null;return o.transaction(()=>{for(const[E,s]of Object.entries(te)){const h=L[E];if(!h?.length)continue;const F=o.prepare(s);for(const M of new Set(h))F.run(M)}const t=j(o,{mode:"erase",reason:"manual",category:null,predicate:{truth:!0,project:!0},counts:p,actor:i.actor??null}).id;H=t,P(o,"truth.purge",{erasure_id:t,truth_ids:d,category:r.category,count:d.length,...i.reason?{reason:i.reason}:{}},{projectId:r.project_id})})(),{counts:p,total:A,erasureId:H,truthIds:d,tombstones:S,mentions:e,unattributed:l,notSwept:R}}export{D as distinctiveValue,ae as eraseLakeRows,z as lakeProvenanceTag,C as logSweepValue,de as purgeTruthKey,J as quotesKey,N as quotesWhole};