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.
- package/dist/agent-loop.js +3 -3
- package/dist/analytics.js +13 -13
- package/dist/audit.js +2 -2
- package/dist/brief-cache.js +1 -0
- package/dist/causality.js +5 -5
- package/dist/cloud/cli.js +14 -6
- package/dist/cloud/sync-engine.js +11 -8
- package/dist/connectors/index.js +1 -1
- package/dist/connectors/ingest.js +2 -2
- package/dist/content-signature.js +1 -1
- package/dist/daemon-write-endpoint.js +1 -1
- package/dist/database.js +43 -43
- package/dist/design.js +6 -6
- package/dist/entity-populate.js +3 -3
- package/dist/erasure-cli.js +3 -3
- package/dist/erasure-log.js +3 -3
- package/dist/erasure.js +15 -1
- package/dist/events.js +6 -6
- package/dist/failure-patterns.js +52 -42
- package/dist/federation.js +13 -13
- package/dist/fts-health.js +1 -1
- package/dist/goals.js +9 -9
- package/dist/governance.js +1 -1
- package/dist/handlers/arg-keys.js +1 -1
- package/dist/handlers/boundary.js +1 -1
- package/dist/handlers/capture.js +60 -58
- package/dist/handlers/datalake.js +2 -2
- package/dist/handlers/failure.js +13 -11
- package/dist/handlers/intelligence.js +57 -57
- package/dist/handlers/project.js +23 -22
- package/dist/handlers/prompts.js +3 -3
- package/dist/handlers/run.js +10 -10
- package/dist/handlers/session.js +9 -9
- package/dist/handlers/shims.js +1 -1
- package/dist/handlers/skill.js +44 -44
- package/dist/http-auth.js +2 -2
- package/dist/http-fast.js +18 -18
- package/dist/inbound-redact.js +1 -0
- package/dist/index.js +1 -2
- package/dist/intelligence.js +35 -31
- package/dist/internal-dispatch.js +2 -2
- package/dist/knowledge-graph.js +9 -9
- package/dist/logger.js +2 -2
- package/dist/mcp-client.js +4 -4
- package/dist/memory-artifacts.js +14 -14
- package/dist/metabolize.js +6 -6
- package/dist/performance.js +1 -1
- package/dist/presence.js +5 -5
- package/dist/providers/wordpiece.js +1 -1
- package/dist/read-memo.js +5 -5
- package/dist/receipts.js +4 -4
- package/dist/redact-backfill.js +1 -1
- package/dist/rerank.js +1 -1
- package/dist/reverse-bridge.js +4 -4
- package/dist/security.js +1 -1
- package/dist/skill-author.js +2 -2
- package/dist/sub-agent.js +6 -6
- package/dist/text-columns.js +1 -0
- package/dist/thread-store.js +2 -2
- package/dist/tool-analytics.js +4 -4
- package/dist/tool-manifest-v2.json +1 -1
- package/dist/tool-manifest.json +1 -1
- package/dist/trace-harvest.js +5 -5
- package/dist/ui-dashboard.js +30 -6
- package/dist/ui-login-open.js +2 -0
- package/dist/validate.js +1 -1
- package/dist/vectors.js +4 -4
- package/dist/wyrm-cli.js +88 -88
- package/dist/wyrm-manifest.json +1 -1
- package/dist/wyrm-ui.js +19 -11
- package/package.json +6 -2
- package/scripts/hooks/wyrm-session-capture.mjs +15 -2
- package/scripts/postinstall.cjs +26 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
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
|
|
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
|
|
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 = ?"}},
|
|
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){
|
|
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=
|
|
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,
|
|
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,
|
|
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=
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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=
|
|
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=
|
|
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,
|
|
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&&
|
|
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?(
|
|
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?(
|
|
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=
|
|
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
|
|
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(
|
|
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,
|
|
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(
|
|
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&&
|
|
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,
|
|
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
|
|
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
|
|
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=
|
|
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=
|
|
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
|
|
283
|
-
DELETE FROM data_lake WHERE project_id = ? AND category
|
|
284
|
-
`).run(t
|
|
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
|
|
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
|
|
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
|
|
24
|
-
`)}addReference(e){if(!
|
|
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{
|
|
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};
|
package/dist/entity-populate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{extractEntityCandidates as y}from"./entity-extract.js";const
|
|
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=
|
|
4
|
-
${p.validated_fix??""}`);s+=r.created,d+=r.linked,l+=r.entities}})(a),{created:s,linked:d,entities:l,artifactsScanned:a.length}}export{
|
|
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};
|
package/dist/erasure-cli.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{c as
|
|
2
|
-
Dry run: nothing was deleted. --yes erases these now, without waiting for the grace period.`)),console.log(
|
|
3
|
-
Dry run: nothing changed. ${t
|
|
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};
|
package/dist/erasure-log.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{getActor as
|
|
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(
|
|
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(
|
|
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
|
|
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};
|