wyrm-mcp 9.2.2 → 9.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activation.js +1 -1
- package/dist/agent-loop.js +2 -2
- package/dist/audit.js +2 -2
- package/dist/autoconfig.js +6 -6
- package/dist/backup-cli.js +6 -0
- package/dist/backup.js +1 -0
- package/dist/bridge.js +1 -1
- package/dist/buddy-runner.js +1 -1
- package/dist/buddy.js +4 -4
- package/dist/capabilities.js +1 -1
- package/dist/causality.js +3 -3
- package/dist/cloud/cli.js +3 -3
- package/dist/cloud/sync-daemon-entrypoint.js +1 -1
- package/dist/cloud/sync-engine.js +8 -7
- package/dist/cloud-backup.js +4 -4
- package/dist/cloud-sync-entrypoint.js +1 -1
- package/dist/cloud-sync.js +1 -1
- package/dist/connectors/ingest.js +2 -2
- package/dist/constellation.js +1 -1
- package/dist/context-build-budgeted.js +4 -4
- package/dist/context-ranking.js +1 -1
- package/dist/daemon-writer.js +1 -1
- package/dist/database.js +41 -23
- package/dist/design.js +1 -1
- package/dist/erasure-cli.js +3 -0
- package/dist/erasure-log.js +8 -0
- package/dist/erasure.js +1 -0
- package/dist/event-replication.js +1 -1
- package/dist/events.js +11 -11
- package/dist/failure-patterns.js +67 -44
- package/dist/federation.js +11 -10
- package/dist/governance.js +1 -0
- package/dist/handlers/agent.js +4 -4
- package/dist/handlers/alias-adapters.js +1 -1
- package/dist/handlers/audit.js +1 -1
- package/dist/handlers/boundary.js +1 -1
- package/dist/handlers/capture.js +54 -52
- package/dist/handlers/causality.js +2 -2
- package/dist/handlers/cloud.js +9 -9
- package/dist/handlers/companion.js +4 -4
- package/dist/handlers/entity.js +6 -6
- package/dist/handlers/events.js +4 -4
- package/dist/handlers/failure.js +15 -12
- package/dist/handlers/goals.js +2 -2
- package/dist/handlers/intelligence.js +81 -74
- package/dist/handlers/mcpclient.js +3 -3
- package/dist/handlers/presence.js +1 -1
- package/dist/handlers/prompts.js +2 -2
- package/dist/handlers/quest.js +11 -4
- package/dist/handlers/recall.js +10 -10
- package/dist/handlers/resources.js +1 -1
- package/dist/handlers/review.js +3 -3
- package/dist/handlers/run.js +3 -3
- package/dist/handlers/search.js +1 -1
- package/dist/handlers/session.js +27 -25
- package/dist/handlers/share.js +3 -3
- package/dist/handlers/skill.js +2 -2
- package/dist/handlers/symbols.js +5 -5
- package/dist/handlers/syncops.js +2 -2
- package/dist/harvest.js +5 -5
- package/dist/hours.js +1 -1
- package/dist/http-auth.js +2 -2
- package/dist/http-fast.js +17 -17
- package/dist/http-projects.js +4 -0
- package/dist/http-rate-limit.js +1 -0
- package/dist/index-health.js +52 -0
- package/dist/index.js +2 -2
- package/dist/indexer.js +4 -4
- package/dist/intelligence.js +36 -29
- package/dist/lane-provenance.js +39 -0
- package/dist/license.js +1 -1
- package/dist/lineage.js +15 -0
- package/dist/live-embedding-state.js +2 -2
- package/dist/maintenance.js +2 -2
- package/dist/mcp-integrity.js +2 -2
- package/dist/memory-artifacts.js +39 -37
- package/dist/metabolize.js +9 -7
- package/dist/metrics.js +1 -1
- package/dist/migrate-prompt.js +1 -1
- package/dist/migrations.js +236 -45
- package/dist/priority-embed.js +1 -1
- package/dist/providers/embedding-provider.js +1 -1
- package/dist/readonly-gate.js +1 -1
- package/dist/redact-backfill.js +1 -0
- package/dist/rehydration.js +30 -20
- package/dist/reindex.js +1 -1
- package/dist/render-target.js +6 -6
- package/dist/repl-guard.js +1 -1
- package/dist/replication-daemon-entrypoint.js +1 -1
- package/dist/replication-daemon.js +1 -1
- package/dist/retention.js +1 -0
- package/dist/reverse-bridge.js +6 -6
- package/dist/review-sample.js +31 -0
- package/dist/security.js +1 -1
- package/dist/self-maintenance.js +4 -4
- package/dist/served-records.js +8 -2
- package/dist/setup-wizard.js +1 -1
- package/dist/setup.js +1 -1
- package/dist/skill-author.js +2 -2
- package/dist/store-open.js +1 -0
- package/dist/store-readonly.js +1 -0
- package/dist/sub-agent.js +2 -2
- package/dist/tool-manifest-v2.json +1 -1
- package/dist/tool-manifest.json +1 -1
- package/dist/truth-freshness.js +8 -0
- package/dist/ui-dashboard.js +4 -4
- package/dist/vault.js +1 -1
- package/dist/vector-init.js +1 -1
- package/dist/vectors.js +4 -3
- package/dist/visibility.js +12 -12
- package/dist/write-guard.js +1 -0
- package/dist/wyrm-cli.js +108 -92
- package/dist/wyrm-guard.js +13 -12
- package/dist/wyrm-loop.js +3 -3
- package/dist/wyrm-manifest.json +1 -1
- package/dist/wyrm-memo.js +5 -5
- package/package.json +1 -1
- package/scripts/hooks/wyrm-session-capture.mjs +3 -0
package/dist/database.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSync as O,readdirSync as
|
|
1
|
+
import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSync as O,readdirSync as A,statSync as w,writeFileSync as j,chmodSync as M}from"fs";import{homedir as y}from"os";import{join as d,basename as v,resolve as H,normalize as F}from"path";import{spawnSync as T}from"child_process";import{getResilienceManager as W}from"./resilience.js";import{getStoreLogger as U}from"./logger.js";import{runMigrations as x,schemaSkew as P,getSchemaVersion as q}from"./migrations.js";import{recordGovernanceEvent as b}from"./governance.js";import{enforceReadOnlyOpen as B,newerStoreMessage as $,newerStoreWritesAllowed as Y,NEWER_STORE_OVERRIDE_ENV as G}from"./store-readonly.js";import{describeStoreError as De}from"./store-readonly.js";import{validateProjectPath as C,buildFtsMatchQuery as m,redactCredentials as k}from"./security.js";import{emitEvent as E,eventsSince as V,subscribeEvents as J,isLiveMemoryEnabled as Q,ingestRemoteEvent as z,eventsForPush as K,pruneEvents as Z,getMeta as X,setMeta as ee}from"./events.js";import{getActor as D}from"./handlers/boundary.js";import{eraseLakeRows as te}from"./erasure.js";import{readSkillContent as I,slugify as se,skillContentSha as re}from"./skill-author.js";import{resolveProjectForPath as ie}from"./project-resolve.js";function Oe(){return process.env.WYRM_DB_PATH??process.env.WYRM_DB??d(y(),".wyrm","wyrm.db")}function ne(f,t){const e=(s,r)=>{try{u(s)&&M(s,r)}catch{}};t&&e(t,448),e(f,384),e(`${f}-wal`,384),e(`${f}-shm`,384)}const oe={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 = ?"}},
|
|
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 = ?"}},ae=new Set(["within","org","public"]);class ye{db;BATCH_SIZE=1e3;resilience;logger;dbPath;constructor(t){const e=d(y(),".wyrm");u(e)||O(e,{recursive:!0,mode:448}),this.dbPath=t||d(e,"wyrm.db"),this.logger=U(this.dbPath),this.resilience=W(),this.db=this.initializeDatabase(this.dbPath),ne(this.dbPath,e),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");const s=x(this.db),r=P(this.db);r.storeIsNewer&&(Y()?this.logger.warn(`This store is at schema v${r.store}, newer than this Wyrm knows (v${r.binary}). ${G}=1 is set, so this older binary is writing to it under older rules. Upgrade wyrm-mcp on this machine.`):(B(this.db,r),this.logger.warn($(r.store,r.binary)))),s.length>0&&this.logger.info(`Applied ${s.length} migration(s), now at v${q(this.db)}`),this.recoverIncompleteOperations()}getDatabase(){return this.db}getDatabasePath(){return this.dbPath}initializeDatabase(t){const e=this.resilience.withRetrySync(()=>L(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(`
|
|
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){
|
|
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=A(i,{withFileTypes:!0});for(const o of a){if(!o.isDirectory()||o.name.startsWith(".")&&o.name!==".git")continue;const c=d(i,o.name),l=d(c,".git");if(u(l)){const p=this.registerProjectFromPath(c);p&&s.push(p)}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=H(
|
|
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=F(H(t));if(!u(e)||!w(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 l=T("git",["log","-1","--format=%h %s"],{cwd:e,encoding:"utf-8",timeout:5e3,shell:!1});l.status===0&&(n=l.stdout.trim())}catch{}return u(d(e,"package.json"))?(a="Node.js",u(d(e,"next.config.js"))||u(d(e,"next.config.ts"))||u(d(e,"next.config.mjs"))?a="Next.js":u(d(e,"vite.config.ts"))&&(a="Vite")):u(d(e,"requirements.txt"))||u(d(e,"pyproject.toml"))?a="Python":u(d(e,"composer.json"))?a="PHP":u(d(e,"Cargo.toml"))?a="Rust":u(d(e,"go.mod"))&&(a="Go"),this.registerProject(s,e,r,a,n,i)}catch{return null}}registerProject(t,e,s,r,i,n){return 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,22 +19,22 @@ import{openDatabase as L}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)}getProject(t){return this.db.prepare("SELECT * FROM projects WHERE path = ?").get(t)}getProjectForPath(t){const{id:e,root:s}=
|
|
22
|
+
`).get(t,e,s||null,r||null,i||null,n||null)}getProject(t){return this.db.prepare("SELECT * FROM projects WHERE path = ?").get(t)}getProjectForPath(t){const{id:e,root:s}=ie(this.db,t);return e!==null&&s!==null?{id:e,path:s}: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(`
|
|
23
23
|
SELECT * FROM projects ORDER BY updated_at DESC LIMIT ? OFFSET ?
|
|
24
24
|
`).all(t,e)}searchProjects(t){const e=`%${t}%`;return this.db.prepare(`
|
|
25
25
|
SELECT * FROM projects
|
|
26
26
|
WHERE name LIKE ? OR stack LIKE ? OR repo LIKE ?
|
|
27
27
|
ORDER BY updated_at DESC
|
|
28
28
|
LIMIT 50
|
|
29
|
-
`).all(e,e,e)}createSession(t,e){const s=this.estimateTokens((e.objectives||"")+(e.completed||"")+(e.issues||"")+(e.notes||"")),r=
|
|
29
|
+
`).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(`
|
|
30
30
|
INSERT INTO sessions (project_id, date, objectives, completed, issues, commits, files_changed, notes, tokens_estimate, agent_id, run_id)
|
|
31
31
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
32
32
|
RETURNING *
|
|
33
|
-
`).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
|
|
33
|
+
`).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(`
|
|
34
34
|
UPDATE sessions SET ${s.join(", ")} WHERE id = ? RETURNING *
|
|
35
|
-
`).get(...r),"updateSession");if(!n.success)throw n.error||new Error("Failed to update session");return
|
|
35
|
+
`).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+`
|
|
36
36
|
|
|
37
|
-
`+o:o;c.length>i&&(c=`[\u2026earlier continuity trimmed
|
|
37
|
+
`+o:o;c.length>i&&(c=`[\u2026earlier continuity trimmed; gist retained below\u2026]
|
|
38
38
|
|
|
39
39
|
`+c.slice(c.length-i));const l=(n?.turn_count??0)+1;this.resilience.withRetrySync(()=>(this.db.prepare(`
|
|
40
40
|
INSERT INTO project_continuity (project_id, thread, updated_at, updated_by, turn_count)
|
|
@@ -66,7 +66,7 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
66
66
|
LIMIT ?
|
|
67
67
|
`).all(t,e)}getTodaySession(t){const e=new Date().toISOString().split("T")[0];return this.db.prepare(`
|
|
68
68
|
SELECT * FROM sessions WHERE project_id = ? AND date = ?
|
|
69
|
-
`).get(t,e)}searchSessions(t,e){const s=
|
|
69
|
+
`).get(t,e)}searchSessions(t,e){const s=m(t);if(!s)return[];try{return e?this.db.prepare(`
|
|
70
70
|
SELECT s.* FROM sessions s
|
|
71
71
|
JOIN sessions_fts fts ON s.id = fts.rowid
|
|
72
72
|
WHERE sessions_fts MATCH ? AND s.project_id = ?
|
|
@@ -92,13 +92,31 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
92
92
|
`).run(t,t,e).changes}getSessionTokenUsage(t){return this.db.prepare(`
|
|
93
93
|
SELECT COALESCE(SUM(tokens_estimate), 0) as total
|
|
94
94
|
FROM sessions WHERE project_id = ? AND is_archived = 0
|
|
95
|
-
`).get(t).total}addQuest(t,e,s,r="medium",i){const n=
|
|
95
|
+
`).get(t).total}addQuest(t,e,s,r="medium",i){const n=D(),a=this.db.prepare(`
|
|
96
96
|
INSERT INTO quests (project_id, title, description, priority, tags, agent_id, run_id, updated_at)
|
|
97
97
|
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
98
98
|
RETURNING *
|
|
99
|
-
`).get(t,
|
|
99
|
+
`).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(`
|
|
100
100
|
UPDATE quests SET status = ?, completed_at = ?, updated_at = datetime('now') WHERE id = ? RETURNING *
|
|
101
|
-
`).get(e,s,t);return r&&
|
|
101
|
+
`).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(`
|
|
102
|
+
UPDATE quests SET status = 'parked', updated_at = datetime('now')
|
|
103
|
+
WHERE id = ? AND status IN ('pending', 'in_progress') RETURNING *
|
|
104
|
+
`).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(`
|
|
105
|
+
UPDATE quests SET status = 'pending', updated_at = datetime('now')
|
|
106
|
+
WHERE id = ? AND status = 'parked' RETURNING *
|
|
107
|
+
`).get(t);return e?(E(this.db,{projectId:e.project_id,kind:"quest",refTable:"quests",refId:t}),e):null}getAllParkedQuests(){return this.db.prepare(`
|
|
108
|
+
SELECT q.*, p.name as project_name FROM quests q
|
|
109
|
+
JOIN projects p ON q.project_id = p.id
|
|
110
|
+
WHERE q.status = 'parked'
|
|
111
|
+
ORDER BY
|
|
112
|
+
CASE q.priority
|
|
113
|
+
WHEN 'critical' THEN 1
|
|
114
|
+
WHEN 'high' THEN 2
|
|
115
|
+
WHEN 'medium' THEN 3
|
|
116
|
+
WHEN 'low' THEN 4
|
|
117
|
+
END,
|
|
118
|
+
q.id ASC
|
|
119
|
+
`).all()}getPendingQuests(t){return this.db.prepare(`
|
|
102
120
|
SELECT * FROM quests
|
|
103
121
|
WHERE project_id = ? AND status IN ('pending', 'in_progress')
|
|
104
122
|
ORDER BY
|
|
@@ -121,7 +139,7 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
121
139
|
WHEN 'low' THEN 4
|
|
122
140
|
END,
|
|
123
141
|
q.created_at ASC
|
|
124
|
-
`).all()}searchQuests(t){const e=
|
|
142
|
+
`).all()}searchQuests(t){const e=m(t);if(!e)return[];try{return this.db.prepare(`
|
|
125
143
|
SELECT q.* FROM quests q
|
|
126
144
|
JOIN quests_fts fts ON q.id = fts.rowid
|
|
127
145
|
WHERE quests_fts MATCH ?
|
|
@@ -142,7 +160,7 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
142
160
|
SELECT value FROM context WHERE project_id = ? AND key = ?
|
|
143
161
|
`).get(t,e)?.value}getAllContext(t){const e=this.db.prepare(`
|
|
144
162
|
SELECT key, value FROM context WHERE project_id = ?
|
|
145
|
-
`).all(t),s={};for(const r of e)s[r.key]=r.value;return s}liveMemoryEnabled(){return
|
|
163
|
+
`).all(t),s={};for(const r of e)s[r.key]=r.value;return s}liveMemoryEnabled(){return Q()}eventsSince(t,e=0,s=100){return V(this.db,t,e,s)}subscribeEvents(t,e=20){return J(this.db,t,e)}publishEvent(t){E(this.db,t)}pruneEvents(t={}){return Z(this.db,t)}ingestRemoteEvent(t,e){return z(this.db,t,e)}eventsForPush(t,e=0,s=200){return K(this.db,t,e,s)}getMeta(t){return X(this.db,t)}setMeta(t,e){ee(this.db,t,e)}setGlobalContext(t,e){this.db.prepare(`
|
|
146
164
|
INSERT INTO global_context (key, value)
|
|
147
165
|
VALUES (?, ?)
|
|
148
166
|
ON CONFLICT(key) DO UPDATE SET
|
|
@@ -150,7 +168,7 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
150
168
|
updated_at = datetime('now')
|
|
151
169
|
`).run(t,e)}getGlobalContext(t){return this.db.prepare(`
|
|
152
170
|
SELECT value FROM global_context WHERE key = ?
|
|
153
|
-
`).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",l=JSON.stringify(Array.isArray(o?.governs)?o.governs:[]),p=JSON.stringify(Array.isArray(o?.composes)?o.composes:[]),_=
|
|
171
|
+
`).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",l=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,S=_?.sha256??null,R=_?new Date().toISOString():null,h=this.resilience.withRetrySync(()=>this.db.prepare(`
|
|
154
172
|
INSERT INTO skills (name, description, skill_path, category, author, version, tags, tier, governs, composes, content, content_sha256, content_updated_at, is_active, usage_count)
|
|
155
173
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0)
|
|
156
174
|
ON CONFLICT(name) DO UPDATE SET
|
|
@@ -172,13 +190,13 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
172
190
|
updated_at = datetime('now'),
|
|
173
191
|
is_active = 1
|
|
174
192
|
RETURNING *
|
|
175
|
-
`).get(t,e,s,r||null,i||null,n||null,a||null,c,l,p,g,S,R),"registerSkill");if(!
|
|
193
|
+
`).get(t,e,s,r||null,i||null,n||null,a||null,c,l,p,g,S,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 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=se(c.name);if(!l){n++;continue}o.has(l)&&(l=`${l}-${re(c.name).slice(0,8)}`,a++),o.add(l);const p=d(t,l);O(p,{recursive:!0}),j(d(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=oe[t];if(!r)throw new Error(`Unknown visibility kind: ${String(t)} (use truth|memory|quest)`);if(!ae.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(`
|
|
176
194
|
SELECT s.* FROM skills s
|
|
177
195
|
JOIN skills_fts ON s.id = skills_fts.rowid
|
|
178
196
|
WHERE skills_fts MATCH ?${i}
|
|
179
197
|
ORDER BY rank
|
|
180
198
|
LIMIT ?
|
|
181
|
-
`);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 p=r(l.governs),_=r(l.composes),g={name:l.name,tier:l.tier??"atomic",description:l.description,governs:p,composes:_,children:[]},S=[];if(!c.has(o)){const R=new Set(c).add(o);for(const
|
|
199
|
+
`);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 p=r(l.governs),_=r(l.composes),g={name:l.name,tier:l.tier??"atomic",description:l.description,governs:p,composes:_,children:[]},S=[];if(!c.has(o)){const R=new Set(c).add(o);for(const h of p){const N=i(h,R);N?g.children.push(N):S.push(h)}}return S.length&&(g.missing=S),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(`
|
|
182
200
|
UPDATE skills SET ${s.join(", ")} WHERE name = ? RETURNING *
|
|
183
201
|
`).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(`
|
|
184
202
|
SELECT category, COUNT(*) as count FROM skills WHERE category IS NOT NULL GROUP BY category
|
|
@@ -196,7 +214,7 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
196
214
|
WHERE project_id = ?
|
|
197
215
|
AND (tags = ? OR tags LIKE ? OR tags LIKE ? OR tags LIKE ?)
|
|
198
216
|
LIMIT 1
|
|
199
|
-
`).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&&
|
|
217
|
+
`).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(`
|
|
200
218
|
INSERT INTO data_lake (project_id, category, key, value, metadata)
|
|
201
219
|
VALUES (?, ?, ?, ?, ?)
|
|
202
220
|
RETURNING *
|
|
@@ -213,7 +231,7 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
213
231
|
WHERE project_id = ?
|
|
214
232
|
ORDER BY created_at DESC
|
|
215
233
|
LIMIT ? OFFSET ?
|
|
216
|
-
`).all(t,s,r)}searchData(t,e){const s=
|
|
234
|
+
`).all(t,s,r)}searchData(t,e){const s=m(t);if(!s)return[];try{return e?this.db.prepare(`
|
|
217
235
|
SELECT d.* FROM data_lake d
|
|
218
236
|
JOIN data_lake_fts fts ON d.id = fts.rowid
|
|
219
237
|
WHERE data_lake_fts MATCH ? AND d.project_id = ?
|
|
@@ -225,7 +243,7 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
225
243
|
WHERE data_lake_fts MATCH ?
|
|
226
244
|
ORDER BY bm25(data_lake_fts), d.created_at DESC
|
|
227
245
|
LIMIT 100
|
|
228
|
-
`).all(s)}catch{return[]}}searchArtifacts(t,e){const s=
|
|
246
|
+
`).all(s)}catch{return[]}}searchArtifacts(t,e){const s=m(t);if(!s)return[];try{return e?this.db.prepare(`
|
|
229
247
|
SELECT a.* FROM memory_artifacts a
|
|
230
248
|
JOIN memory_artifacts_fts fts ON a.id = fts.rowid
|
|
231
249
|
WHERE memory_artifacts_fts MATCH ? AND a.project_id = ?
|
|
@@ -245,8 +263,8 @@ import{openDatabase as L}from"./storage/index.js";import{existsSync as u,mkdirSy
|
|
|
245
263
|
WHERE project_id = ?
|
|
246
264
|
GROUP BY category
|
|
247
265
|
ORDER BY count DESC
|
|
248
|
-
`).all(t)}forgetData(t){const e=[],s=[];if(t.match){e.push("(key LIKE ? OR value LIKE ?)");const
|
|
266
|
+
`).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=te(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(`
|
|
249
267
|
DELETE FROM data_lake WHERE project_id = ? AND category = ?
|
|
250
268
|
`).run(t,e).changes}*streamSessions(t){const e=this.db.prepare(`
|
|
251
269
|
SELECT * FROM sessions WHERE project_id = ? ORDER BY date DESC
|
|
252
|
-
`);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{
|
|
270
|
+
`);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{ye as WyrmDB,De as describeStoreError,Oe as resolveDbPath,ne as tightenStorePermissions};
|
package/dist/design.js
CHANGED
|
@@ -20,7 +20,7 @@ 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?`
|
|
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
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(`
|
|
25
25
|
INSERT INTO design_references (project_id, kind, location, title, notes, tags, updated_at)
|
|
26
26
|
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{c,formatTable as l,printSection as d,printSuccess as u,printWarning as g}from"./cli.js";import{enforceIngestRetention as m,ingestedCategories as h,RETENTION_GRACE_DAYS as y}from"./retention.js";import{redactBackfill as f}from"./redact-backfill.js";import{scanBackupHygiene as w,listBackups as b,defaultBackupDir as k}from"./backup.js";import{fmtBytes as R}from"./backup-cli.js";function $(a,o=" "){for(const[n,t]of Object.entries(a).sort((e,s)=>s[1]-e[1]))console.log(`${o}${String(t).padStart(6)} ${n}`)}function A(a,o){const n=typeof o.days=="string"?Number(o.days):void 0,t=o.yes===!0&&o["dry-run"]!==!0,e=m(a,{days:n,dryRun:!t,skipGrace:t,actor:"cli"});if(e.status==="disabled"){console.log("Ingest retention is off (WYRM_INGEST_RETAIN_DAYS=0), so nothing ages out.");return}d(t?`Ingest retention: erased (limit ${e.days}d)`:`Ingest retention (dry run, limit ${e.days}d)`),console.log(l(["Category",t?"Erased":"Rows past the limit"],e.categories.map(r=>[r.category,String(t?r.deleted:r.matched)])));const s={};for(const r of e.categories)for(const[i,p]of Object.entries(r.counts))s[i]=(s[i]??0)+p;if(Object.keys(s).length>0&&(console.log(t?" Removed, per table:":" Would remove, per table:"),$(s," ")),e.graceEndsAt){const r=Date.parse(e.graceEndsAt)<=Date.now();console.log(` Automatic enforcement by maintenance ${r?"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 ${y} days.`);t?(u(`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(c.dim(`
|
|
2
|
+
Dry run: nothing was deleted. --yes erases these now, without waiting for the grace period.`)),console.log(c.dim(" Change the limit with WYRM_INGEST_RETAIN_DAYS=<days> (0 turns retention off).")))}function _(a,o){const n=o.apply===!0&&o["dry-run"]!==!0,t=a.getDatabase(),e=f(t,{apply:n,ingestedCategories:h(a)});d(n?"Redaction back-fill":"Redaction back-fill (dry run)"),console.log(l(["Table","Scanned","Rows with credentials","Fields"],e.tables.map(r=>[r.table,String(r.scanned),String(r.rows),String(r.fields)])));const s=e.tables.reduce((r,i)=>r+i.rows,0);if(!n){console.log(c.dim(`
|
|
3
|
+
Dry run: nothing changed. ${s>0?"Re-run with --apply (add --vacuum to rewrite the file).":"Nothing to redact."}`));return}u(`Redacted ${s} 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?(a.vacuum(),a.checkpoint(),console.log(" Vacuumed: the file is rewritten, so pages that held the old text are gone.")):s>0&&console.log(c.dim(" Freed pages may still hold the old text: wyrm maintenance redact --apply --vacuum rewrites the file.")),s>0&&g("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).")}function T(a){const o=w({dbPath:a}),n=[];if(o.exposed.length>0&&n.push({ok:!1,label:"Backup permissions",detail:`${o.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)"}),o.legacy.length>0){const t=o.legacy.reduce((e,s)=>e+s.bytes,0);n.push({ok:!1,label:"Stray backups",detail:`${o.legacy.length} database copy(ies) outside rotation (${R(t)}); 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"})}if(n.length===0){const t=b(k(a));n.push(t.length>0?{ok:!0,label:"Backups",detail:`${t.length} rotated snapshot(s), newest ${Math.floor((Date.now()-t[0].at.getTime())/864e5)}d old, no stray or exposed copies`}:{ok:!0,label:"Backups",detail:"no snapshots yet (optional): wyrm backup create --encrypt"})}return n}export{A as cmdForgetRetention,_ as cmdMaintenanceRedact,T as doctorBackupRows,$ as printErasureCounts};
|
|
@@ -0,0 +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(`
|
|
2
|
+
INSERT INTO erasure_log (mode, reason, category, predicate_json, counts_json, total, actor, agent_id, run_id)
|
|
3
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
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(`
|
|
6
|
+
SELECT id, erased_at, mode, reason, category, predicate_json, counts_json, total, actor, agent_id, run_id
|
|
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};
|
package/dist/erasure.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
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 +1 @@
|
|
|
1
|
-
import{logger as f}from"./logger.js";import{guardedFetchJson as
|
|
1
|
+
import{logger as f}from"./logger.js";import{guardedFetchJson as g,checkPeerUrl as P}from"./repl-guard.js";const u=500,m=e=>`repl:push:${e.baseUrl}:${e.localProjectId}`,_=e=>`repl:pull:${e.baseUrl}:${e.localProjectId}`,y=e=>Number.parseInt(e||"0",10)||0;function v(e){const r={"Content-Type":"application/json"};return e&&(r.Authorization=`Bearer ${e}`),r}function j(e){return{origin_device:e.origin_device,origin_epoch:e.origin_epoch??0,origin_seq:e.origin_seq,cloud_cursor:e.cursor,kind:e.kind,ref_table:e.ref_table,ref_id:e.ref_id,actor:e.actor,is_shared:e.is_shared,payload_json:e.payload_json,created_at:e.created_at}}async function $(e,r,a){const o=P(r.baseUrl);if(!o.ok)return{pushed:0,error:`push: ${o.reason}`};const s=y(e.getMeta(m(r))),t=e.eventsForPush(r.localProjectId,s,u);if(t.length===0)return{pushed:0};try{return await g(a,r.baseUrl,"/events",{method:"POST",headers:v(r.token),body:JSON.stringify({project:r.remoteProject,events:t.map(j)})}),e.setMeta(m(r),String(t[t.length-1].cursor)),{pushed:t.length}}catch(c){const i=c instanceof Error?c.message:String(c);return f.warn("live-memory: push failed",{peer:r.baseUrl,err:i}),{pushed:0,error:`push: ${i}`}}}async function U(e,r,a){const o=y(e.getMeta(_(r)));try{const s=`/events?project=${encodeURIComponent(r.remoteProject)}&since=${o}&limit=${u}&shared=1`,t=await g(a,r.baseUrl,s,{method:"GET",headers:v(r.token)}),c=(Array.isArray(t.events)?t.events:[]).filter(n=>!!n&&typeof n=="object"&&Number.isFinite(n.cursor)).slice(0,u);let i=0,d=0,h=0,l=o;for(const n of c){const p=e.ingestRemoteEvent(r.localProjectId,{...j(n),cloud_cursor:n.cursor});p==="inserted"?i++:p==="echo"?h++:d++,n.cursor>l&&(l=n.cursor)}return e.setMeta(_(r),String(l)),{pulled:i,duplicates:d,echoes:h}}catch(s){const t=s instanceof Error?s.message:String(s);return f.warn("live-memory: pull failed",{peer:r.baseUrl,err:t}),{pulled:0,duplicates:0,echoes:0,error:`pull: ${t}`}}}async function S(e,r,a){const o=await U(e,r,a),s=await $(e,r,a),t=[o.error,s.error].filter(c=>!!c);return{pulled:o.pulled,pushed:s.pushed,duplicates:o.duplicates,echoes:o.echoes,errors:t}}export{U as pullPeerEvents,$ as pushSharedEvents,S as replicateOnce};
|
package/dist/events.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import{logger as _}from"./logger.js";import{getActor as m}from"./handlers/boundary.js";import{getDeviceId as R,getLineage as f,nextOriginSeq as h}from"./lineage.js";const I=new Set(["capture","session_update","quest","truth","presence","tool_call","failure","decision"]),T=64*1024,y=80,E=256,g=864e5,l=r=>typeof r=="number"&&Number.isInteger(r)&&r>=0&&r<=Number.MAX_SAFE_INTEGER,p=(r,t)=>typeof r=="string"?r.slice(0,t):null;function M(){const r=(process.env.WYRM_LIVE_MEMORY||"").toLowerCase();return r!=="0"&&r!=="false"&&r!=="off"}function a(r,t,e,n){const o=typeof r=="number"&&Number.isFinite(r)?Math.floor(r):n;return Math.max(t,Math.min(e,o))}function D(r,t){if(M())try{const{deviceId:e,epoch:n}=f(r),o=t.isShared?1:0;let i=null;if(o&&t.payload!==void 0)try{i=JSON.stringify(t.payload)}catch{i=null}const c=m(),s=t.agentId!==void 0?t.agentId:c.agent_id,u=t.runId!==void 0?t.runId:c.run_id;r.transaction(()=>{const d=h(r,e);r.prepare(`
|
|
2
|
+
INSERT INTO events
|
|
3
|
+
(origin_device, origin_epoch, origin_seq, project_id, kind, ref_table, ref_id, actor, is_shared, payload_json, created_at, agent_id, run_id)
|
|
4
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5
|
+
`).run(e,n,d,String(t.projectId),t.kind,t.refTable??null,t.refId!=null?String(t.refId):null,t.actor??null,o,i,Date.now(),s,u)})()}catch(e){_.warn("live-memory: emitEvent dropped",{err:e instanceof Error?e.message:String(e)})}}function C(r,t,e=0,n=100){const o=a(n,1,1e3,100),i=a(e,0,Number.MAX_SAFE_INTEGER,0);return r.prepare("SELECT * FROM events WHERE project_id = ? AND cursor > ? ORDER BY cursor ASC LIMIT ?").all(String(t),i,o)}function F(r,t,e=20){const n=a(e,1,200,20),o=r.prepare("SELECT * FROM events WHERE project_id = ? ORDER BY cursor DESC LIMIT ?").all(String(t),n);return o.reverse(),{cursor:r.prepare("SELECT COALESCE(MAX(cursor), 0) AS c FROM events WHERE project_id = ?").get(String(t)).c,recent:o}}function j(r,t,e){try{if(!e||typeof e.origin_device!="string"||!l(e.origin_seq))return"duplicate";const n=p(e.origin_device,y);if(!n)return"duplicate";const o=l(e.origin_epoch)?e.origin_epoch:0,i=f(r);if(n===i.deviceId&&o===i.epoch)return"echo";if(typeof e.kind!="string"||!I.has(e.kind))return"duplicate";const c=e.is_shared?1:0;let s=c&&typeof e.payload_json=="string"?e.payload_json:null;s&&s.length>T&&(s=null);const u=Date.now(),d=l(e.created_at)&&e.created_at<=u+g?e.created_at:u,S=l(e.cloud_cursor)?e.cloud_cursor:null;return r.prepare(`
|
|
6
6
|
INSERT OR IGNORE INTO events
|
|
7
|
-
(origin_device, origin_seq, cloud_cursor, project_id, kind, ref_table, ref_id, actor, is_shared, payload_json, created_at, agent_id, run_id)
|
|
8
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9
|
-
`).run(n,
|
|
7
|
+
(origin_device, origin_epoch, origin_seq, cloud_cursor, project_id, kind, ref_table, ref_id, actor, is_shared, payload_json, created_at, agent_id, run_id)
|
|
8
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9
|
+
`).run(n,o,e.origin_seq,S,String(t),e.kind,p(e.ref_table,E),p(e.ref_id,E),p(e.actor,E),c,s,d,p(e.agent_id,E),p(e.run_id,E)).changes>0?"inserted":"duplicate"}catch(n){return _.warn("live-memory: ingestRemoteEvent dropped",{err:n instanceof Error?n.message:String(n)}),"duplicate"}}function x(r,t,e=0,n=200){const o=a(n,1,1e3,200),i=a(e,0,Number.MAX_SAFE_INTEGER,0);try{const c=r.prepare("SELECT sync_policy AS p FROM projects WHERE CAST(id AS TEXT) = ? OR path = ? LIMIT 1").get(String(t),String(t));if(c&&c.p&&c.p!=="team")return[]}catch{}return r.prepare(`
|
|
10
10
|
SELECT * FROM events
|
|
11
11
|
WHERE project_id = ? AND is_shared = 1 AND cursor > ?
|
|
12
12
|
ORDER BY cursor ASC LIMIT ?
|
|
13
|
-
`).all(String(t),i,o)}function w(
|
|
13
|
+
`).all(String(t),i,o)}function w(r,t={}){const e=a(t.olderThanDays,1,3650,90),n=a(t.maxPerProject,1,1e6,5e3);try{if(!r.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'events'").get())return{deleted:0};let i=0;const c=Date.now()-e*g;i+=r.prepare("DELETE FROM events WHERE created_at < ?").run(c).changes;const s=r.prepare("SELECT project_id FROM events GROUP BY project_id HAVING COUNT(*) > ?").all(n),u=r.prepare(`
|
|
14
14
|
DELETE FROM events WHERE project_id = ? AND cursor <= (
|
|
15
15
|
SELECT cursor FROM events WHERE project_id = ? ORDER BY cursor DESC LIMIT 1 OFFSET ?
|
|
16
16
|
)
|
|
17
|
-
`);for(const{project_id:
|
|
18
|
-
ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(t,
|
|
17
|
+
`);for(const{project_id:d}of s)i+=u.run(d,d,n).changes;return{deleted:i}}catch(o){return _.warn("live-memory: pruneEvents failed",{db:r.name,err:o instanceof Error?o.message:String(o)}),{deleted:0}}}function W(r,t){return r.prepare("SELECT value FROM wyrm_meta WHERE key = ?").get(t)?.value}function v(r,t,e){r.prepare(`INSERT INTO wyrm_meta (key, value) VALUES (?, ?)
|
|
18
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(t,e)}export{D as emitEvent,x as eventsForPush,C as eventsSince,R as getDeviceId,W as getMeta,j as ingestRemoteEvent,M as isLiveMemoryEnabled,w as pruneEvents,v as setMeta,F as subscribeEvents};
|