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/failure-patterns.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import{readFileSync as
|
|
1
|
+
import{readFileSync as j,statSync as D}from"node:fs";import{isAbsolute as W,relative as H,resolve as g,sep as y}from"node:path";import{sanitizeFtsQuery as F}from"./security.js";import{WyrmCrypto as q}from"./crypto.js";import{emitEvent as O,isLiveMemoryEnabled as $}from"./events.js";import{getActor as R,hasExplicitAttribution as m}from"./handlers/boundary.js";import{readActor as v,resolveAttribution as B}from"./attribution.js";import{recordGovernanceEvent as L}from"./governance.js";import{normalizeContentSignature as w,containsFailedSignature as x}from"./content-signature.js";const Y=new Set(["a","an","the","this","that","these","those","and","or","but","if","then","than","so","to","of","in","on","at","by","for","from","with","without","no","not","as","is","are","was","were","be","been","being","will","would","can","could","should","it","its","i","we","you","my","our","your","again","just","now","please","let","lets","do","does","did","into","over","about"]),M={run:0,project:1,global:2},z=8,oe="source changed since this failure was recorded \u2014 re-verify",P="no hits in the quiet period, so it warns instead of blocking; recording it again re-arms the block",G=60,V=44;function ae(d=process.env){const e=(d.WYRM_FAILURE_QUIET_DAYS??"").trim();if(e==="")return G;if(/^(off|false|no)$/i.test(e))return 0;const t=Number(e);return Number.isFinite(t)&&t>0?t:0}const T=200,A=5;function se(d){const e=r=>Object.entries(r).filter(([,n])=>n>0).map(([n,o])=>`${n} ${o}`).join(" \xB7 ")||"none",t=r=>r.map(n=>`${"who"in n?n.who:n.run_id} \xD7${n.count}`).join(" \xB7 ")||"none",i=d.blocked.this_run.run_id===null?"- This run: (no run identity; export WYRM_RUN_ID or pass run_id)":`- This run (${d.blocked.this_run.run_id}): ${d.blocked.this_run.blocked} repeat(s) blocked`;return`\u{F115D} **Failure Analytics** (${d.project_id===null?"all projects":`project #${d.project_id}`})
|
|
2
2
|
|
|
3
|
-
**Recorded:** ${
|
|
4
|
-
- By quarantine: ${e(
|
|
5
|
-
- By scope: ${e(
|
|
6
|
-
- By agent: ${t(
|
|
7
|
-
- By run: ${t(
|
|
3
|
+
**Recorded:** ${d.recorded.total} failure(s), ${d.recorded.unresolved} unresolved
|
|
4
|
+
- By quarantine: ${e(d.recorded.by_quarantine_scope)}
|
|
5
|
+
- By scope: ${e(d.recorded.by_scope)}
|
|
6
|
+
- By agent: ${t(d.recorded.by_agent)}
|
|
7
|
+
- By run: ${t(d.recorded.by_run)}
|
|
8
8
|
|
|
9
|
-
**Confirmations:** ${
|
|
9
|
+
**Confirmations:** ${d.confirmations.total} from ${d.confirmations.distinct_agents} distinct agent(s)
|
|
10
10
|
|
|
11
|
-
**Repeats blocked:** ${
|
|
12
|
-
${
|
|
13
|
-
- By agent: ${t(
|
|
14
|
-
- By run: ${t(
|
|
15
|
-
- Top blockers: ${
|
|
11
|
+
**Repeats blocked:** ${d.blocked.total} total
|
|
12
|
+
${i}
|
|
13
|
+
- By agent: ${t(d.blocked.by_agent)}
|
|
14
|
+
- By run: ${t(d.blocked.by_run)}
|
|
15
|
+
- Top blockers: ${d.blocked.top_blocked.map(r=>`#${r.failure_id} ${r.scope}:${r.target} \xD7${r.blocks}`).join(" \xB7 ")||"none"}`}function Q(d,e){if(d==="exact")return 1;if(d==="target")return .95;if(d==="canonical")return .9;if(d==="related")return .4;const t=Math.max(0,-(e??0));return Number((.05+.85*(t/(t+2))).toFixed(4))}function b(d){return d.toLowerCase().replace(/\s+/g," ").trim()}function U(d){return b(d).slice(0,200)}function I(d){const e=b(d).split(" ").filter(Boolean);let t=0;const i=[];for(;t<e.length&&!e[t].startsWith("-");)i.push(e[t]),t++;const r=[],n=[];for(;t<e.length;t++){const o=e[t];o.startsWith("-")?!o.includes("=")&&t+1<e.length&&!e[t+1].startsWith("-")?(n.push(`${o}=${e[t+1]}`),t++):n.push(o):r.push(o)}return n.sort(),[...i,...r,...n].join(" ")}function k(d){const e=b(d).split(" ").filter(Boolean),t=[];for(const i of e){if(i.startsWith("-"))break;t.push(i)}return t.length>=2?t.join(" "):""}class N{db;constructor(e){this.db=e}static signature(e,t,i){return`${e}:${U(t)}:${U(i).slice(0,80)}`}record(e){const t=N.signature(e.scope,e.target,e.description),i=e.project_id??null,r=e.content&&w(e.content)||null,n=e.scope==="command"&&I(e.target)||null,o=e.scope==="command"&&k(e.target)||null,s=R(),p=this.db.transaction(()=>{const u=this.db.prepare(`
|
|
16
16
|
SELECT * FROM failure_patterns
|
|
17
17
|
WHERE signature = ? AND (project_id IS ? OR project_id = ?)
|
|
18
|
-
`).get(t,
|
|
18
|
+
`).get(t,i,i);if(u)return this.db.prepare(`
|
|
19
19
|
UPDATE failure_patterns
|
|
20
20
|
SET occurrences = occurrences + 1,
|
|
21
21
|
last_seen = datetime('now'),
|
|
@@ -24,29 +24,32 @@ ${o}
|
|
|
24
24
|
content_sig = COALESCE(?, content_sig),
|
|
25
25
|
resolved = 0
|
|
26
26
|
WHERE id = ?
|
|
27
|
-
`).run(e.why_failed??null,e.severity??
|
|
27
|
+
`).run(e.why_failed??null,e.severity??u.severity,r,u.id),u.downgraded_at&&(this.db.prepare("UPDATE failure_patterns SET downgraded_at = NULL WHERE id = ?").run(u.id),this.db.prepare(`
|
|
28
|
+
INSERT INTO failure_pattern_history (failure_id, event, detail) VALUES (?, 'rearmed', ?)
|
|
29
|
+
`).run(u.id,`recorded again after a quiet-period downgrade (downgraded ${u.downgraded_at}): warn -> block`)),this.noteConfirmation(u.id,s),this.get(u.id);const _=e.quarantine_scope??(s.run_id!==null?"run":i===null?"global":"project"),a=this.db.prepare(`
|
|
28
30
|
INSERT INTO failure_patterns
|
|
29
31
|
(project_id, signature, scope, target, description, why_failed, severity, agent_id, run_id, quarantine_scope, content_sig, canonical_target, command_head)
|
|
30
32
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
31
|
-
`).run(
|
|
33
|
+
`).run(i,t,e.scope,e.target,e.description,e.why_failed??null,e.severity??"medium",s.agent_id,s.run_id,_,r,n,o),l=this.get(a.lastInsertRowid);return this.noteConfirmation(l.id,s),l}).immediate();return O(this.db,{projectId:p.project_id??0,kind:"failure",refTable:"failure_patterns",refId:p.id}),p}contentMatch(e,t,i){const r=this.db.prepare(`
|
|
32
34
|
SELECT * FROM failure_patterns
|
|
33
35
|
WHERE content_sig IS NOT NULL AND scope = ? AND resolved = 0
|
|
34
36
|
AND (project_id IS ? OR project_id = ?)
|
|
35
37
|
ORDER BY occurrences DESC, last_seen DESC
|
|
36
|
-
`).all(t,
|
|
38
|
+
`).all(t,i,i);for(const n of r)if(n.content_sig&&x(e,n.content_sig))return n;return null}get(e){return this.db.prepare("SELECT * FROM failure_patterns WHERE id = ?").get(e)??null}static anchorSha(e){return q.hash(w(e))}recordAnchors(e,t,i,r=!0){try{const n=g(t),o=new Set(this.db.prepare("SELECT DISTINCT rel_path FROM failure_anchors WHERE failure_id = ?").all(e).map(a=>a.rel_path));let s=z-o.size;const c=this.db.prepare("DELETE FROM failure_anchors WHERE failure_id = ? AND rel_path = ?"),p=this.db.prepare(`
|
|
37
39
|
INSERT INTO failure_anchors (failure_id, rel_path, sha, size, mtime_ns)
|
|
38
40
|
VALUES (?, ?, ?, ?, ?)
|
|
39
|
-
`),
|
|
41
|
+
`),u=new Set;let _=0;for(const a of i){if(typeof a!="string"||a.length===0)continue;const l=W(a)?g(a):g(n,a);if(!l.startsWith(n+y)||u.has(l))continue;u.add(l);const h=H(n,l),S=o.has(h);if(!(!S&&(!r||s<=0)))try{const f=D(l,{bigint:!0});if(!f.isFile())continue;const E=N.anchorSha(j(l,"utf-8"));c.run(e,h),p.run(e,h,E,f.size,f.mtimeNs),_++,S||s--}catch{}}return _}catch{return 0}}getAnchors(e){try{const t=this.db.prepare("SELECT rel_path, sha, size, mtime_ns, recorded_at FROM failure_anchors WHERE failure_id = ?");return t.safeIntegers(!0),t.all(e)}catch{return[]}}anchorFreshness(e,t){try{if(!t)return"unverifiable";const i=this.getAnchors(e);if(i.length===0)return"unanchored";const r=g(t);let n=0;for(const o of i)try{const s=g(r,o.rel_path);if(!s.startsWith(r+y))continue;const c=D(s,{bigint:!0});if(!c.isFile())continue;if(c.size===o.size&&c.mtimeNs===o.mtime_ns){n++;continue}if(N.anchorSha(j(s,"utf-8"))===o.sha){n++;continue}return"stale"}catch{}return n>0?"fresh":"unverifiable"}catch{return"unverifiable"}}staleAnchorIds(e,t){const i=new Set,r=new Map;for(const n of e)try{if(n.match==="exact"||n.scope!=="file"&&n.scope!=="symbol"&&n.scope!=="edit")continue;let o=r.get(n.id);o===void 0&&(o=t?this.anchorFreshness(n.id,t):this.getAnchors(n.id).length===0?"unanchored":"unverifiable",r.set(n.id,o)),(o==="unanchored"||o==="stale")&&i.add(n.id)}catch{}return i}noteConfirmation(e,t){if(t.agent_id===null)return;this.db.prepare(`
|
|
40
42
|
INSERT OR IGNORE INTO failure_confirmations (failure_id, agent_id, run_id)
|
|
41
43
|
VALUES (?, ?, ?)
|
|
42
|
-
`).run(e,t.agent_id,t.run_id);const
|
|
44
|
+
`).run(e,t.agent_id,t.run_id);const i=this.get(e);if(!i||i.quarantine_scope!=="run")return;const{n:r}=this.db.prepare("SELECT COUNT(*) AS n FROM failure_confirmations WHERE failure_id = ?").get(e);r>=2&&this.applyPromotion(e,"project")}applyPromotion(e,t){const i=this.get(e);if(!i)return!1;const r=t==="project"&&i.project_id===null?"global":t;return M[r]<=M[i.quarantine_scope]?!1:(this.db.prepare("UPDATE failure_patterns SET quarantine_scope = ? WHERE id = ?").run(r,e),O(this.db,{projectId:i.project_id??0,kind:"failure",refTable:"failure_patterns",refId:e}),!0)}promote(e,t){if(!m(R()))return{row:this.get(e),authorized:!1,changed:!1};const i=this.applyPromotion(e,t);return{row:this.get(e),authorized:!0,changed:i}}sweepRunQuarantine(e){const t=Math.max(1,Math.floor(e?.runInactiveHours??24)),i=`-${t} hours`,r=Date.now()-t*36e5,n=$(),o=this.db.transaction(()=>{const s=this.db.prepare(`
|
|
43
45
|
UPDATE failure_patterns
|
|
44
46
|
SET resolved = 1,
|
|
45
47
|
resolution_note = COALESCE(resolution_note || ' \xB7 ', '')
|
|
46
48
|
|| 'auto-expired by run-quarantine sweep: run ' || run_id || ' was abandoned'
|
|
47
49
|
WHERE resolved = 0 AND quarantine_scope = 'run' AND run_id IS NOT NULL
|
|
48
50
|
AND run_id IN (SELECT run_id FROM runs WHERE status = 'abandoned')
|
|
49
|
-
|
|
51
|
+
RETURNING id
|
|
52
|
+
`).all().map(a=>a.id),c=s.length;c>0&&L(this.db,"failure.resolve",{bulk:!0,reason:"run_abandoned",count:c,failure_ids:s},{system:"run-quarantine-sweep"});const p=this.db.prepare(`
|
|
50
53
|
SELECT fp.id FROM failure_patterns fp
|
|
51
54
|
WHERE fp.resolved = 0 AND fp.quarantine_scope = 'run' AND fp.run_id IS NOT NULL
|
|
52
55
|
AND (
|
|
@@ -72,18 +75,38 @@ ${o}
|
|
|
72
75
|
)
|
|
73
76
|
)
|
|
74
77
|
)
|
|
75
|
-
`).all({cut:
|
|
78
|
+
`).all({cut:i,cutMs:r,ttl:n?1:0}).map(a=>a.id),u=this.db.prepare(`
|
|
76
79
|
UPDATE failure_patterns
|
|
77
80
|
SET quarantine_scope = CASE WHEN project_id IS NULL THEN 'global' ELSE 'project' END
|
|
78
81
|
WHERE id = ?
|
|
79
|
-
`);for(const
|
|
82
|
+
`);for(const a of p)u.run(a);const _=n?this.db.prepare(`
|
|
80
83
|
UPDATE runs SET status = 'abandoned', updated_at = datetime('now')
|
|
81
84
|
WHERE status = 'running' AND updated_at < datetime('now', @cut)
|
|
82
85
|
AND NOT EXISTS (
|
|
83
86
|
SELECT 1 FROM events e
|
|
84
87
|
WHERE e.run_id = runs.run_id AND e.created_at >= @cutMs
|
|
85
88
|
)
|
|
86
|
-
`).run({cut:
|
|
89
|
+
`).run({cut:i,cutMs:r}).changes:0;return{promotedIds:p,expired:c,abandoned:_}})();for(const s of o.promotedIds){const c=this.get(s);O(this.db,{projectId:c?.project_id??0,kind:"failure",refTable:"failure_patterns",refId:s,agentId:null,runId:null})}return{promoted:o.promotedIds.length,expired:o.expired,runs_abandoned:o.abandoned,...n?{}:{degraded:"live memory off (WYRM_LIVE_MEMORY): run-tagged events do not exist, so the run-inactivity TTL paths were skipped; only explicit run verdicts acted (completed/failed promote, abandoned expires)"}}}check(e,t,i,r,n){return this.query(e,t,i,r??null,n).map(({_match:o,_rank:s,...c})=>c)}checkVerdict(e,t,i,r,n){const o=new Set,s=[],c=(a,l,h=null)=>{o.has(a.id)||(o.add(a.id),s.push(this.toVerdictMatch(a,l,h)))};for(const a of this.query(e,t,i,r??null,n))c(a,a._match,a._rank);const p=n?.runScope??"any",u=a=>p==="any"||a.quarantine_scope===p;for(const a of this.targetIdentityMatches(e,t,r??null,n))u(a)&&c(a,"target");for(const a of this.canonicalMatches(e,t,r??null,n))u(a)&&c(a,"canonical");const _=s.find(a=>!a.quiet_downgrade);return{blocked:_!==void 0,matches:s,recorded_by_agent:(_??s[0])?.recorded_by_agent??null,run_id:(_??s[0])?.run_id??null,confidence:s.reduce((a,l)=>Math.max(a,l.confidence),0)}}toVerdictMatch(e,t,i=null){const r=B(e);return{id:e.id,pattern:e.signature,scope:e.scope,quarantine_scope:e.quarantine_scope,target:e.target,description:e.description,why_failed:e.why_failed,severity:e.severity,occurrences:e.occurrences,last_seen:e.last_seen,recorded_by_agent:r.actor,run_id:r.run_id,match:t,confidence:Q(t,i),...e.downgraded_at?{quiet_downgrade:P}:{}}}sweepQuiet(e,t={}){if(!(e>0))return{downgraded:0,ids:[]};const i=this.db.transaction(()=>{const r=this.quietCandidates(e,t),n=this.db.prepare(`
|
|
90
|
+
UPDATE failure_patterns SET downgraded_at = datetime('now')
|
|
91
|
+
WHERE id = ? AND resolved = 0 AND downgraded_at IS NULL
|
|
92
|
+
`),o=this.db.prepare(`
|
|
93
|
+
INSERT INTO failure_pattern_history (failure_id, event, detail) VALUES (?, 'downgraded', ?)
|
|
94
|
+
`),s=[];for(const c of r)n.run(c.id).changes!==0&&(o.run(c.id,c.quiet_since===c.last_hit?`no hits for ${e}d (last hit ${c.last_hit}): block -> warn`:`no hits for ${e}d since the quiet clock started ${c.quiet_since} (last hit ${c.last_hit}): block -> warn`),s.push(c.id));return s})();for(const r of i){const n=this.get(r);O(this.db,{projectId:n?.project_id??0,kind:"failure",refTable:"failure_patterns",refId:r,agentId:null,runId:null})}return{downgraded:i.length,ids:i}}quietClockStart(){try{return this.db.prepare("SELECT applied_at FROM schema_versions WHERE version = ?").get(V)?.applied_at??null}catch{return null}}quietCandidates(e,t={}){if(!(e>0))return[];const i=t.ignoreUpgradeGrace?null:this.quietClockStart();return!t.ignoreUpgradeGrace&&i===null?[]:this.db.prepare(`
|
|
95
|
+
SELECT id, scope, target, datetime(last_hit_jd) AS last_hit, datetime(quiet_from_jd) AS quiet_since FROM (
|
|
96
|
+
SELECT id, scope, target, last_hit_jd,
|
|
97
|
+
CASE WHEN @since IS NULL THEN last_hit_jd ELSE MAX(last_hit_jd, julianday(@since)) END AS quiet_from_jd
|
|
98
|
+
FROM (
|
|
99
|
+
SELECT fp.id AS id, fp.scope AS scope, fp.target AS target,
|
|
100
|
+
MAX(julianday(COALESCE(fp.last_seen, fp.first_seen)),
|
|
101
|
+
COALESCE((SELECT MAX(julianday(b.blocked_at)) FROM failure_blocks b WHERE b.failure_id = fp.id),
|
|
102
|
+
julianday(COALESCE(fp.last_seen, fp.first_seen)))) AS last_hit_jd
|
|
103
|
+
FROM failure_patterns fp
|
|
104
|
+
WHERE fp.resolved = 0 AND fp.downgraded_at IS NULL
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
WHERE last_hit_jd IS NOT NULL AND quiet_from_jd IS NOT NULL AND julianday('now') - quiet_from_jd > @days
|
|
108
|
+
ORDER BY id
|
|
109
|
+
`).all({days:e,since:i})}history(e){try{return this.db.prepare("SELECT * FROM failure_pattern_history WHERE failure_id = ? ORDER BY id").all(e)}catch{return[]}}targetIdentityMatches(e,t,i,r){const n=b(t),o=n.slice(0,200);if(o.length===0)return[];const s=r?.callerRunId!==void 0?r.callerRunId:R().run_id,c=`${e}:${o}:`,p=`${e}:${o};`;return this.db.prepare(`
|
|
87
110
|
SELECT * FROM failure_patterns
|
|
88
111
|
WHERE resolved = 0
|
|
89
112
|
AND signature >= @lo AND signature < @hi
|
|
@@ -91,65 +114,65 @@ ${o}
|
|
|
91
114
|
AND (quarantine_scope <> 'run' OR run_id = @run)
|
|
92
115
|
ORDER BY occurrences DESC, last_seen DESC
|
|
93
116
|
LIMIT @cap
|
|
94
|
-
`).all({lo:
|
|
117
|
+
`).all({lo:c,hi:p,project:i??null,run:s,cap:T}).filter(_=>b(_.target)===n).slice(0,A)}canonicalMatches(e,t,i,r){if(e!=="command")return[];const n=I(t);if(n.length===0)return[];const o=r?.callerRunId!==void 0?r.callerRunId:R().run_id;return this.db.prepare(`
|
|
95
118
|
SELECT * FROM failure_patterns
|
|
96
119
|
WHERE resolved = 0 AND scope = 'command' AND canonical_target = @canon
|
|
97
120
|
AND (project_id IS @project OR project_id = @project OR project_id IS NULL)
|
|
98
121
|
AND (quarantine_scope <> 'run' OR run_id = @run)
|
|
99
122
|
ORDER BY occurrences DESC, last_seen DESC
|
|
100
123
|
LIMIT @cap
|
|
101
|
-
`).all({canon:
|
|
124
|
+
`).all({canon:n,project:i??null,run:o,cap:T}).filter(c=>I(c.target)===n).slice(0,A)}commandHeadFamily(e,t,i,r){if(e!=="command")return[];const n=k(t);if(n.length===0)return[];const o=r?.callerRunId!==void 0?r.callerRunId:R().run_id;return this.db.prepare(`
|
|
102
125
|
SELECT * FROM failure_patterns
|
|
103
126
|
WHERE resolved = 0 AND scope = 'command' AND command_head = @head
|
|
104
127
|
AND (project_id IS @project OR project_id = @project OR project_id IS NULL)
|
|
105
128
|
AND (quarantine_scope <> 'run' OR run_id = @run)
|
|
106
129
|
ORDER BY occurrences DESC, last_seen DESC
|
|
107
130
|
LIMIT @cap
|
|
108
|
-
`).all({head:
|
|
131
|
+
`).all({head:n,project:i??null,run:o,cap:T}).slice(0,A)}recordBlock(e,t){if(!(!e.blocked||e.matches.length===0))try{const i=R(),r=e.matches.find(n=>!n.stale_anchor)??e.matches[0];this.db.prepare(`
|
|
109
132
|
INSERT INTO failure_blocks (failure_id, project_id, agent_id, run_id)
|
|
110
133
|
VALUES (?, ?, ?, ?)
|
|
111
|
-
`).run(
|
|
134
|
+
`).run(r.id,t??null,i.agent_id,i.run_id)}catch{}}pruneBlocks(e){const t=Math.max(1,Math.floor(e));try{return this.db.prepare("DELETE FROM failure_blocks WHERE blocked_at < datetime('now', @cut)").run({cut:`-${t} days`}).changes}catch{return 0}}failureStats(e){const t=e?.projectId??null,i=e?.callerRunId!==void 0?e.callerRunId:R().run_id,r={project:t},n=this.db.prepare(`
|
|
112
135
|
SELECT COUNT(*) AS total, COALESCE(SUM(resolved = 0), 0) AS unresolved
|
|
113
136
|
FROM failure_patterns WHERE (@project IS NULL OR project_id = @project)
|
|
114
|
-
`).get(
|
|
137
|
+
`).get(r),o={run:0,project:0,global:0};for(const E of this.db.prepare(`
|
|
115
138
|
SELECT quarantine_scope AS k, COUNT(*) AS n FROM failure_patterns
|
|
116
139
|
WHERE (@project IS NULL OR project_id = @project) GROUP BY quarantine_scope
|
|
117
|
-
`).all(
|
|
140
|
+
`).all(r))o[E.k]=E.n;const s={file:0,symbol:0,command:0,prompt:0,edit:0};for(const E of this.db.prepare(`
|
|
118
141
|
SELECT scope AS k, COUNT(*) AS n FROM failure_patterns
|
|
119
142
|
WHERE (@project IS NULL OR project_id = @project) GROUP BY scope
|
|
120
|
-
`).all(
|
|
143
|
+
`).all(r))s[E.k]=E.n;const c=E=>E.map(C=>({who:v(C.agent_id),count:C.count})),p=c(this.db.prepare(`
|
|
121
144
|
SELECT agent_id, COUNT(*) AS count FROM failure_patterns
|
|
122
145
|
WHERE (@project IS NULL OR project_id = @project)
|
|
123
146
|
GROUP BY agent_id ORDER BY count DESC, agent_id ASC LIMIT 10
|
|
124
|
-
`).all(
|
|
147
|
+
`).all(r)),u=this.db.prepare(`
|
|
125
148
|
SELECT run_id, COUNT(*) AS count FROM failure_patterns
|
|
126
149
|
WHERE run_id IS NOT NULL AND (@project IS NULL OR project_id = @project)
|
|
127
150
|
GROUP BY run_id ORDER BY count DESC, run_id ASC LIMIT 10
|
|
128
|
-
`).all(
|
|
151
|
+
`).all(r),_=c(this.db.prepare(`
|
|
129
152
|
SELECT agent_id, COUNT(*) AS count FROM failure_blocks
|
|
130
153
|
WHERE (@project IS NULL OR project_id = @project)
|
|
131
154
|
GROUP BY agent_id ORDER BY count DESC, agent_id ASC LIMIT 10
|
|
132
|
-
`).all(
|
|
155
|
+
`).all(r)),a=this.db.prepare(`
|
|
133
156
|
SELECT run_id, COUNT(*) AS count FROM failure_blocks
|
|
134
157
|
WHERE run_id IS NOT NULL AND (@project IS NULL OR project_id = @project)
|
|
135
158
|
GROUP BY run_id ORDER BY count DESC, run_id ASC LIMIT 10
|
|
136
|
-
`).all(
|
|
159
|
+
`).all(r),l=this.db.prepare(`
|
|
137
160
|
SELECT COUNT(*) AS total, COUNT(DISTINCT fc.agent_id) AS distinct_agents
|
|
138
161
|
FROM failure_confirmations fc
|
|
139
162
|
JOIN failure_patterns fp ON fp.id = fc.failure_id
|
|
140
163
|
WHERE (@project IS NULL OR fp.project_id = @project)
|
|
141
|
-
`).get(
|
|
164
|
+
`).get(r),h=this.db.prepare(`
|
|
142
165
|
SELECT COUNT(*) AS n FROM failure_blocks WHERE (@project IS NULL OR project_id = @project)
|
|
143
|
-
`).get(
|
|
166
|
+
`).get(r).n,S=this.db.prepare(`
|
|
144
167
|
SELECT COUNT(*) AS n FROM failure_blocks
|
|
145
168
|
WHERE run_id = @run AND (@project IS NULL OR project_id = @project)
|
|
146
|
-
`).get({...
|
|
169
|
+
`).get({...r,run:i}).n,f=this.db.prepare(`
|
|
147
170
|
SELECT fb.failure_id, fp.scope, fp.target, COUNT(*) AS blocks
|
|
148
171
|
FROM failure_blocks fb
|
|
149
172
|
JOIN failure_patterns fp ON fp.id = fb.failure_id
|
|
150
173
|
WHERE (@project IS NULL OR fb.project_id = @project)
|
|
151
174
|
GROUP BY fb.failure_id ORDER BY blocks DESC, fb.failure_id ASC LIMIT 5
|
|
152
|
-
`).all(
|
|
175
|
+
`).all(r);return{view:"failures",project_id:t,recorded:{total:n.total,unresolved:n.unresolved,by_quarantine_scope:o,by_scope:s,by_agent:p,by_run:u},confirmations:{total:l.total,distinct_agents:l.distinct_agents},blocked:{total:h,by_agent:_,by_run:a,this_run:{run_id:i??null,blocked:S},top_blocked:f}}}query(e,t,i,r,n){const o=n?.callerRunId!==void 0?n.callerRunId:R().run_id,s=n?.runScope??"any",c={project:r,run:o,tier:s==="any"?null:s},p=N.signature(e,t,i),u=this.db.prepare(`
|
|
153
176
|
SELECT *, NULL AS _rank FROM failure_patterns
|
|
154
177
|
WHERE resolved = 0 AND signature = @sig
|
|
155
178
|
AND (project_id IS @project OR project_id = @project OR project_id IS NULL)
|
|
@@ -157,7 +180,7 @@ ${o}
|
|
|
157
180
|
AND (@tier IS NULL OR quarantine_scope = @tier)
|
|
158
181
|
ORDER BY occurrences DESC, last_seen DESC
|
|
159
182
|
LIMIT 5
|
|
160
|
-
`).all({...
|
|
183
|
+
`).all({...c,sig:p});if(u.length>0)return u.map(f=>({...f,_match:"exact",_rank:null}));const _=F(`${t} ${i}`.slice(0,100));if(!_)return[];const a=_.split(" ").filter(f=>/[\p{L}\p{N}]/u.test(f)),l=a.filter(f=>!Y.has(f.toLowerCase())),h=l.length?l:a;if(h.length===0)return[];const S=h.map(f=>`"${f.replace(/"/g,"")}"`).join(" ");try{return this.db.prepare(`
|
|
161
184
|
SELECT fp.*, bm25(failure_patterns_fts) AS _rank FROM failure_patterns fp
|
|
162
185
|
JOIN failure_patterns_fts fts ON fts.rowid = fp.id
|
|
163
186
|
WHERE failure_patterns_fts MATCH @q
|
|
@@ -168,11 +191,11 @@ ${o}
|
|
|
168
191
|
AND (@tier IS NULL OR fp.quarantine_scope = @tier)
|
|
169
192
|
ORDER BY fp.occurrences DESC, fp.last_seen DESC
|
|
170
193
|
LIMIT 5
|
|
171
|
-
`).all({...
|
|
194
|
+
`).all({...c,q:S,scope:e}).map(E=>({...E,_match:"fuzzy"}))}catch{return[]}}list(e,t=20){return e==null?this.db.prepare(`SELECT * FROM failure_patterns WHERE resolved = 0
|
|
172
195
|
ORDER BY last_seen DESC LIMIT ?`).all(t):this.db.prepare(`SELECT * FROM failure_patterns
|
|
173
196
|
WHERE resolved = 0 AND project_id = ?
|
|
174
|
-
ORDER BY last_seen DESC LIMIT ?`).all(e,t)}resolve(e,t){if(!
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
197
|
+
ORDER BY last_seen DESC LIMIT ?`).all(e,t)}resolve(e,t){if(!m(R()))return{row:this.get(e),authorized:!1,changed:!1};const i=this.db.transaction(()=>{const r=this.db.prepare(`
|
|
198
|
+
UPDATE failure_patterns
|
|
199
|
+
SET resolved = 1, resolution_note = ?
|
|
200
|
+
WHERE id = ?
|
|
201
|
+
`).run(t??null,e);return r.changes>0&&L(this.db,"failure.resolve",{failure_id:e}),r})();return{row:this.get(e),authorized:!0,changed:i.changes>0}}delete(e){return m(R())?this.db.transaction(()=>{try{this.db.prepare("DELETE FROM failure_anchors WHERE failure_id = ?").run(e)}catch{}const t=this.db.prepare("DELETE FROM failure_patterns WHERE id = ?").run(e);return t.changes>0&&L(this.db,"failure.delete",{failure_id:e}),t.changes>0})():!1}}export{z as ANCHOR_CAP,G as DEFAULT_FAILURE_QUIET_DAYS,N as FailurePatterns,V as QUIET_CLOCK_MIGRATION,P as QUIET_DOWNGRADE_FLAG,oe as STALE_ANCHOR_FLAG,I as canonicalizeCommand,k as commandHead,ae as failureQuietDays,Q as matchConfidence,b as normalizeIdentity,U as normalizeSignal,se as renderFailureStats};
|
package/dist/federation.js
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
const
|
|
2
|
-
FROM ${
|
|
1
|
+
import{recordGovernanceEvent as _}from"./governance.js";const h={sessions:["date","objectives","completed","issues","commits","files_changed","notes","summary","tokens_estimate","is_archived","created_at"],quests:["title","description","priority","status","tags","created_at","completed_at"],ground_truths:["category","key","value","rationale","source","is_current","confidence","last_verified_at","created_at","ttl_days"],memory_artifacts:["kind","problem","constraints","validated_fix","why_it_worked","outcome","tags","confidence","last_validated_at","created_at"],decision_edges:["from_kind","from_id","to_kind","to_id","relation","rationale","confidence","created_at","invalidated_at"]},d={session:{table:"sessions",ts:"created_at"},quest:{table:"quests",ts:"created_at"},truth:{table:"ground_truths",ts:"updated_at"},artifact:{table:"memory_artifacts",ts:"last_accessed_at"},edge:{table:"decision_edges",ts:"created_at"}},E={truth:"ground_truths",memory_artifact:"memory_artifacts",quest:"quests",design_token:"design_tokens",design_reference:"design_references",skill:"skills"};class b{db;constructor(e){this.db=e}share(e,s){const r=d[e];return!r||this.isPrivateGroveRow(r.table,s)?!1:this.db.transaction(()=>{const t=this.db.prepare(`UPDATE ${r.table} SET is_shared = 1 WHERE id = ?`).run(s);return t.changes>0&&_(this.db,"visibility.share",{kind:e,row_id:s,shared:!0}),t.changes>0})()}safeRemoteColumns(e,s){if(!new Set(Object.values(d).map(n=>n.table)).has(e))return[];let t;try{const n=this.db.prepare(`PRAGMA table_info(${e})`).all();t=new Set(n.map(a=>a.name))}catch{return[]}return(h[e]??[]).filter(n=>/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&t.has(n)&&n in s)}isPrivateGroveRow(e,s){const r=this.groveOf(e,s);return!r||r.policy==null?!1:r.policy==="private"}groveOf(e,s){try{return this.db.prepare(`SELECT t.project_id AS project_id, (SELECT sync_policy FROM projects WHERE id = t.project_id) AS policy
|
|
2
|
+
FROM ${e} t WHERE t.id = ?`).get(s)}catch{return}}shareOutcome(e,s){const r=d[e];if(!r)return{ok:!1,reason:"not_found"};const t=this.groveOf(r.table,s);return t&&t.policy==="private"?{ok:!1,reason:"private_grove",projectId:t.project_id}:this.share(e,s)?{ok:!0}:{ok:!1,reason:"not_found"}}projectsHasSyncPolicy(){try{return this.db.prepare("PRAGMA table_info(projects)").all().some(e=>e.name==="sync_policy")}catch{return!1}}unshare(e,s){const r=d[e];return r?this.db.transaction(()=>{const t=this.db.prepare(`UPDATE ${r.table} SET is_shared = 0 WHERE id = ?`).run(s);return t.changes>0&&_(this.db,"visibility.share",{kind:e,row_id:s,shared:!1}),t.changes>0})():!1}collectForPush(e,s=500){const r=e??"1970-01-01",t=[],c=this.projectsHasSyncPolicy()?"AND (project_id IS NULL OR project_id IN (SELECT id FROM projects WHERE sync_policy = 'team'))":"";for(const[n,a]of Object.entries(d)){if(t.length>=s)break;const l=n,o=this.db.prepare(`SELECT * FROM ${a.table}
|
|
3
3
|
WHERE is_shared = 1 ${c} AND ${a.ts} > ?
|
|
4
4
|
ORDER BY ${a.ts} ASC
|
|
5
|
-
LIMIT ?`).all(
|
|
5
|
+
LIMIT ?`).all(r,Math.max(1,s-t.length));for(const i of o)t.push({kind:l,id:i.id,project_id:i.project_id??null,payload:i,updated_at:i[a.ts]||new Date().toISOString()})}return t}applyRemote(e,s){const r=d[e.kind];if(!r)return{kind:"skipped"};const t=this.db.prepare(`SELECT ${r.ts} as ts FROM ${r.table} WHERE id = ?`).get(e.id);if(!t){const o=this.safeRemoteColumns(r.table,e.payload);if(o.length===0)return{kind:"skipped"};if(e.project_id==null)return{kind:"skipped"};const i=["id","project_id",...o],u=i.map(()=>"?").join(","),p=[e.id,e.project_id,...o.map(f=>e.payload[f])];try{return this.db.prepare(`INSERT INTO ${r.table} (${i.join(",")}) VALUES (${u})`).run(...p),{kind:"inserted"}}catch{return{kind:"skipped"}}}if(t.ts>e.updated_at){const i=this.db.prepare(`
|
|
6
6
|
INSERT INTO sync_conflicts
|
|
7
7
|
(local_kind, local_id, remote_payload, remote_origin)
|
|
8
8
|
VALUES (?, ?, ?, ?)
|
|
9
|
-
`).run(
|
|
9
|
+
`).run(e.kind,e.id,JSON.stringify(e.payload),s).lastInsertRowid;if(e.project_id!=null)try{const u=this.db.prepare(`
|
|
10
10
|
INSERT INTO quests (project_id, title, description, priority, status, tags)
|
|
11
11
|
VALUES (?, ?, ?, 'high', 'pending', 'sync-conflict')
|
|
12
|
-
`).run(
|
|
12
|
+
`).run(e.project_id,`[sync] Resolve conflict on ${e.kind} #${e.id}`,`A remote ${s} pushed a ${e.kind} that conflicts with a newer local edit. See sync_conflicts row #${i} for the remote payload. Resolve with wyrm_sync_resolve (kept_local | kept_remote | merged).`);this.db.prepare("UPDATE sync_conflicts SET quest_id = ? WHERE id = ?").run(u.lastInsertRowid,i)}catch{}return{kind:"conflict",conflict_id:i}}const n=this.safeRemoteColumns(r.table,e.payload);if(n.length===0)return{kind:"skipped"};const a=n.map(o=>`${o} = ?`).join(", "),l=n.map(o=>e.payload[o]);try{return this.db.prepare(`UPDATE ${r.table} SET ${a} WHERE id = ?`).run(...l,e.id),{kind:"overwritten"}}catch{return{kind:"skipped"}}}recordPush(e,s,r,t){const c=new Date().toISOString();return this.db.prepare(`
|
|
13
13
|
INSERT INTO sync_log
|
|
14
14
|
(direction, remote_origin, kind, count, since_cursor, new_cursor, duration_ms)
|
|
15
15
|
VALUES ('push', ?, 'mixed', ?, ?, ?, ?)
|
|
16
|
-
`).run(
|
|
16
|
+
`).run(s,e.length,t??null,c,r),{pushed:e.length,conflicts:0,new_cursor:c,conflict_ids:[]}}applyPull(e,s,r,t){const c={};let n=0;for(const l of e){const o=this.applyRemote(l,s);(o.kind==="inserted"||o.kind==="overwritten")&&(n++,c[l.kind]=(c[l.kind]??0)+1)}const a=new Date().toISOString();return this.db.prepare(`
|
|
17
17
|
INSERT INTO sync_log
|
|
18
18
|
(direction, remote_origin, kind, count, since_cursor, new_cursor, duration_ms)
|
|
19
19
|
VALUES ('pull', ?, 'mixed', ?, ?, ?, ?)
|
|
20
|
-
`).run(
|
|
20
|
+
`).run(s,n,t??null,a,r),{pulled:n,new_cursor:a,by_kind:c}}latestCursor(e,s){return this.db.prepare(`
|
|
21
21
|
SELECT new_cursor FROM sync_log
|
|
22
22
|
WHERE direction = ? AND remote_origin = ?
|
|
23
23
|
ORDER BY id DESC LIMIT 1
|
|
24
|
-
`).get(
|
|
24
|
+
`).get(e,s)?.new_cursor??null}unresolvedConflicts(e=50){return this.db.prepare(`
|
|
25
25
|
SELECT id, local_kind, local_id, remote_origin, detected_at, quest_id
|
|
26
26
|
FROM sync_conflicts
|
|
27
27
|
WHERE resolved_at IS NULL
|
|
28
28
|
ORDER BY detected_at DESC LIMIT ?
|
|
29
|
-
`).all(
|
|
29
|
+
`).all(e)}resolveConflict(e,s,r){const t=this.db.prepare("SELECT * FROM sync_conflicts WHERE id = ? AND resolved_at IS NULL").get(e);if(!t)return!1;const c=t.source==="cloud"?E[t.local_kind]:d[t.local_kind]?.table;let n=null;if(s==="merged"?n=r??null:s==="kept_remote"&&t.winner!=="remote"?n=JSON.parse(t.remote_payload):s==="kept_local"&&t.winner==="remote"&&t.local_payload&&(n=JSON.parse(t.local_payload)),c&&n){let a=null;if(t.source==="cloud")try{a=new Set(this.db.prepare(`PRAGMA table_info(${c})`).all().map(i=>i.name))}catch{a=new Set}const l=n,o=Object.keys(l).filter(i=>i!=="id"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&(!a||a.has(i)));if(o.length>0){const i=o.map(p=>`${p} = ?`).join(", "),u=o.map(p=>l[p]);try{this.db.prepare(`UPDATE ${c} SET ${i} WHERE id = ?`).run(...u,t.local_id)}catch{}}}if(this.db.prepare(`
|
|
30
30
|
UPDATE sync_conflicts
|
|
31
31
|
SET resolved_at = datetime('now'), resolution = ?
|
|
32
32
|
WHERE id = ?
|
|
33
|
-
`).run(
|
|
33
|
+
`).run(s,e),t.quest_id!=null)try{this.db.prepare(`UPDATE quests SET status = 'completed', completed_at = COALESCE(completed_at, datetime('now')), updated_at = datetime('now')
|
|
34
|
+
WHERE id = ? AND status != 'completed'`).run(t.quest_id)}catch{}return!0}}export{b as Federation};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{basename as m}from"node:path";import{AuditLog as p,loadAuditSignerFromEnv as g}from"./audit.js";import{getActor as y,sanitizeActorId as a}from"./handlers/boundary.js";import{onErasureRecorded as _}from"./erasure-log.js";const b="governance.";let c=null;function I(e){c=e?a(e)??null:null}function A(){if(c)return c;const e=m(process.argv[1]??"").replace(/\.(c|m)?js$/,"");return e==="index"||e==="wyrm-mcp"?"mcp":e==="http-fast"||e==="wyrm-ui"?"http":e==="wyrm"||e==="wyrm-cli"?"cli":"local"}function E(){const e=y();return e.agent_id&&e.source==="writer"?`writer:${e.agent_id}`:e.agent_id?`agent:${e.agent_id}`:e.run_id?`run:${e.run_id}`:`operator:${a(process.env.WYRM_OPERATOR)??A()}`}const f=/^[A-Za-z0-9_.:@-]{1,64}$/,$=/^[a-z][a-z0-9_]{0,39}$/,l=100;function h(e){if(e===null)return null;if(typeof e=="boolean")return e;if(typeof e=="number")return Number.isFinite(e)?e:void 0;if(typeof e=="string")return f.test(e)?e:void 0}function N(e){const t={};for(const[r,n]of Object.entries(e)){if(!$.test(r))continue;if(Array.isArray(n)){const o=n.filter(i=>typeof i=="number"&&Number.isFinite(i));if(o.length===0&&n.length>0)continue;t[r]=o.slice(0,l),o.length>l&&(t[`${r}_total`]=o.length);continue}if(n&&typeof n=="object"){const o={};for(const[i,u]of Object.entries(n))f.test(i)&&typeof u=="number"&&Number.isFinite(u)&&(o[i]=u);t[r]=o;continue}const s=h(n);s!==void 0&&(t[r]=s)}return t}const d=new WeakMap;function j(e){let t=d.get(e);return t||(t=new p(e,g()),d.set(e,t)),t}function w(e,t,r,n={}){j(e).log({event_kind:`${b}${t}`,actor:n.system?`system:${n.system}`:n.actor??E(),project_id:n.projectId??null,payload:N(r),...n.system?{agent_id:null,run_id:null}:{}})}_((e,t)=>{e.mode==="erase"&&w(t,"erasure",{erasure_id:e.id,reason:e.reason,category:e.category,total:e.total,counts:e.counts})});export{b as GOVERNANCE_EVENT_PREFIX,E as governanceActor,w as recordGovernanceEvent,I as setGovernanceSurface,N as shapePayload};
|
package/dist/handlers/agent.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{TOOL_ANNOTATIONS as l}from"../tool-annotations.js";const y=[{name:"wyrm_agent_init",description:"Bootstrap Wyrm's autonomous agent loop in one call. Starts the wyrm-loop daemon as a detached background process so subsequent ticks happen automatically every interval_seconds. Optionally seeds a first goal in the same call. Idempotent
|
|
2
|
-
Seeded goal #${d.id}: ${d.title}`}const r=i.status;return{content:[{type:"text",text:[`\u{F115D} Agent ${r.pid!=null&&r.pid!==i.pid?"already running":"started"}
|
|
3
|
-
`)}]}}},{name:"wyrm_agent_status",description:"Inspect the autonomous agent: is the wyrm-loop daemon running, what was the last action, how many active goals + total iterations. Use to confirm bootstrap worked.",inputSchema:{type:"object",properties:{include_log:{type:"boolean",description:"Include last ~40 lines of the daemon log"}}},annotations:l.wyrm_agent_status,aliases:[],handler:async(s,o)=>{const{agentDaemon:a,goals:p}=o,n=s,t=a.status(),e=[];if(t.running){if(e.push(`\u{F115D} Agent RUNNING
|
|
4
|
-
`)}]}}},{name:"wyrm_agent_stop",description:"Stop the autonomous agent daemon (SIGTERM with grace, then SIGKILL). Goals remain in DB and can be resumed by calling wyrm_agent_init again.",inputSchema:{type:"object",properties:{grace_ms:{type:"number",description:"Wait time before SIGKILL (default 3000, max 30000)"}}},annotations:l.wyrm_agent_stop,aliases:[],handler:async(s,o)=>{const{agentDaemon:a}=o,p=s,n=await a.stop({grace_ms:p.grace_ms});return n.ok?{content:[{type:"text",text:n.was_running?`\u{F115D} Agent stopped (was pid ${n.pid}). Goals remain in DB
|
|
1
|
+
import{TOOL_ANNOTATIONS as l}from"../tool-annotations.js";const y=[{name:"wyrm_agent_init",description:"Bootstrap Wyrm's autonomous agent loop in one call. Starts the wyrm-loop daemon as a detached background process so subsequent ticks happen automatically every interval_seconds. Optionally seeds a first goal in the same call. Idempotent: if already running, returns status without spawning a second instance.",inputSchema:{type:"object",properties:{interval_seconds:{type:"number",description:"Seconds between OODA ticks (default 600 = 10 min, min 10)"},max_steps:{type:"number",description:"Max iterations per goal per tick (default 3, max 20)"},project_path:{type:"string",description:"Scope the daemon to a project root (optional)"},verbose:{type:"boolean",description:"Log every tick to ~/.wyrm/wyrm-loop.log"},seed_goal:{type:"object",description:"Optional first goal to set in the same call",properties:{title:{type:"string"},description:{type:"string"},success_criteria:{type:"string"},priority:{type:"string",enum:["critical","high","medium","low"]},deadline:{type:"string"},max_iterations:{type:"number"}},required:["title"]}}},annotations:l.wyrm_agent_init,aliases:[],handler:async(s,o)=>{const{agentDaemon:a,db:p,goals:n}=o,t=s,e=Math.max(10,Math.min(t.interval_seconds??600,86400)),i=a.start({interval_seconds:e,max_steps:t.max_steps,project_path:t.project_path,verbose:t.verbose??!1});if(!i.ok)return{content:[{type:"text",text:`\u{F115D} Agent init failed: ${i.error}`}],isError:!0};let c="";if(t.seed_goal){const m=t.project_path?p.getProject(t.project_path):null,d=n.set({project_id:m?.id??null,title:t.seed_goal.title,description:t.seed_goal.description,success_criteria:t.seed_goal.success_criteria,deadline:t.seed_goal.deadline,priority:t.seed_goal.priority,max_iterations:t.seed_goal.max_iterations});c=`
|
|
2
|
+
Seeded goal #${d.id}: ${d.title}`}const r=i.status;return{content:[{type:"text",text:[`\u{F115D} Agent ${r.pid!=null&&r.pid!==i.pid?"already running":"started"}, pid ${r.pid}`,` Interval: ${e}s \xB7 Active goals: ${r.active_goals} \xB7 Total iterations: ${r.total_iterations}`,` Log: ${r.log_file}`+c].join(`
|
|
3
|
+
`)}]}}},{name:"wyrm_agent_status",description:"Inspect the autonomous agent: is the wyrm-loop daemon running, what was the last action, how many active goals + total iterations. Use to confirm bootstrap worked.",inputSchema:{type:"object",properties:{include_log:{type:"boolean",description:"Include last ~40 lines of the daemon log"}}},annotations:l.wyrm_agent_status,aliases:[],handler:async(s,o)=>{const{agentDaemon:a,goals:p}=o,n=s,t=a.status(),e=[];if(t.running){if(e.push(`\u{F115D} Agent RUNNING, pid ${t.pid}${t.started_at?` (since ${t.started_at})`:""}`),t.uptime_seconds!=null){const i=Math.floor(t.uptime_seconds/60),c=t.uptime_seconds%60;e.push(` Uptime: ${i}m ${c}s`)}}else e.push("\u{F115D} Agent NOT RUNNING. Call wyrm_agent_init to start it.");return e.push(` Active goals: ${t.active_goals} \xB7 Total iterations: ${t.total_iterations}`),t.last_action&&e.push(` Last action (${t.last_action.ran_at}): ${t.last_action.summary} [${t.last_action.result_status??"?"}]`),n.include_log&&(e.push(""),e.push("=== Recent log ==="),e.push(a.recentLog(40))),{content:[{type:"text",text:e.join(`
|
|
4
|
+
`)}]}}},{name:"wyrm_agent_stop",description:"Stop the autonomous agent daemon (SIGTERM with grace, then SIGKILL). Goals remain in DB and can be resumed by calling wyrm_agent_init again.",inputSchema:{type:"object",properties:{grace_ms:{type:"number",description:"Wait time before SIGKILL (default 3000, max 30000)"}}},annotations:l.wyrm_agent_stop,aliases:[],handler:async(s,o)=>{const{agentDaemon:a}=o,p=s,n=await a.stop({grace_ms:p.grace_ms});return n.ok?{content:[{type:"text",text:n.was_running?`\u{F115D} Agent stopped (was pid ${n.pid}). Goals remain in DB; wyrm_agent_init resumes.`:"\u{F115D} Agent was not running. (No-op.)"}]}:{content:[{type:"text",text:`\u{F115D} Stop failed: ${n.error}`}],isError:!0}}},{name:"wyrm_agent_restart",description:"Stop the daemon and start it again with optional new settings. Useful when changing interval_seconds or max_steps without manually stopping first.",inputSchema:{type:"object",properties:{interval_seconds:{type:"number"},max_steps:{type:"number"},project_path:{type:"string"},verbose:{type:"boolean"}}},annotations:l.wyrm_agent_restart,aliases:[],handler:async(s,o)=>{const{agentDaemon:a,goals:p}=o,n=s,t=await a.restart({interval_seconds:n.interval_seconds,max_steps:n.max_steps,project_path:n.project_path,verbose:n.verbose});if(!t.ok)return{content:[{type:"text",text:`\u{F115D} Restart failed: ${t.error}`}],isError:!0};const e=t.status;return{content:[{type:"text",text:`\u{F115D} Agent restarted, pid ${e.pid}. Active goals: ${e.active_goals}.`}]}}}];export{y as agentToolSpecs};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SHIM_ROUTES as c}from"./shims.js";const p=["project_path","persona","persona_name","mood","federate"];function f(o){return p.some(e=>o[e]!==void 0)}function u(o){const e={...o};return e.project_path===void 0&&(e.project_path=process.cwd()),e}function m(){const o={},e=(t,r)=>{if(o[t]!==void 0)throw new Error(`alias-adapters: ${t} absorbed by two shims (${o[t].target} and ${r.target})`);o[t]=r};for(const[t,r]of Object.entries(c))for(const[a,n]of Object.entries(r.routes))if(typeof n=="string"){if(n===t)continue;e(n,{target:t,adapt:i=>({...i,[r.discriminator]:a}),note:`T022: absorbed by ${t} ${r.discriminator}=${a}; resolveShimCall strips the discriminator back onto the original ${n} dispatcher case
|
|
1
|
+
import{SHIM_ROUTES as c}from"./shims.js";const p=["project_path","persona","persona_name","mood","federate"];function f(o){return p.some(e=>o[e]!==void 0)}function u(o){const e={...o};return e.project_path===void 0&&(e.project_path=process.cwd()),e}function m(){const o={},e=(t,r)=>{if(o[t]!==void 0)throw new Error(`alias-adapters: ${t} absorbed by two shims (${o[t].target} and ${r.target})`);o[t]=r};for(const[t,r]of Object.entries(c))for(const[a,n]of Object.entries(r.routes))if(typeof n=="string"){if(n===t)continue;e(n,{target:t,adapt:i=>({...i,[r.discriminator]:a}),note:`T022: absorbed by ${t} ${r.discriminator}=${a}; resolveShimCall strips the discriminator back onto the original ${n} dispatcher case: same code path by construction (roundtrip-locked).`})}else for(const[i,d]of Object.entries(n.routes))e(d,{target:t,adapt:s=>({...s,[r.discriminator]:a,[n.key]:i}),note:`T022: absorbed by ${t} ${r.discriminator}=${a} ${n.key}=${i}; resolveShimCall strips both back onto the original ${d} dispatcher case: same code path by construction (roundtrip-locked).`});return o}const h={...m(),wyrm_buddy:{target:"buddy",adapt:u,note:"T021 buddy fold: companion params (persona/mood/size/federation) merged into the well-known buddy tool as additive optional args; both names run runCompanionBuddy (index.ts): one code path, not a duplicate."}};export{h as ALIAS_ROUTE_OVERRIDES,p as COMPANION_BUDDY_PARAMS,u as adaptWyrmBuddyToBuddy,f as isCompanionBuddyCall};
|
package/dist/handlers/audit.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{TOOL_ANNOTATIONS as p}from"../tool-annotations.js";import{resolveAttribution as _}from"../attribution.js";import{parseAuditSignature as h}from"../audit.js";const b=[{name:"wyrm_audit_log",description:"Append a hash-chained audit event. Each entry references the previous entry's sha-256, so tampering breaks the chain. Optional Ed25519 signature layer. Used for SOC2/HIPAA-grade audit.",inputSchema:{type:"object",properties:{event_kind:{type:"string",description:"Type of event: 'tool_call', 'key_export', 'truth_change', 'sync', 'migration', ..."},actor:{type:"string",description:"agent_id, 'system', operator email, etc."},projectPath:{type:"string",description:"Project path (optional)"},payload:{type:"object",description:"Structured event details (will be JSON-serialized)"},signature:{type:"string",description:"Optional explicit Ed25519 signature stored as-is (legacy v1 = base64 over the row_hash). Omit to auto-sign with the configured WYRM_AUDIT_SIGNING_KEY: v2 format 'v2:<key_fp>:<base64>' binding agent_id/run_id into the signed payload."}},required:["event_kind","payload"]},annotations:p.wyrm_audit_log,aliases:[],handler:async(i,n)=>{const{auditLog:r,db:e}=n,{event_kind:t,actor:a,projectPath:o,payload:l,signature:u}=i,y=o?e.getProject(o):null,s=r.log({event_kind:t,actor:a,project_id:y?.id??null,payload:l,signature:u}),d=_(s),c=h(s.signature),g=c?c.version===2?` \xB7 signed v2 key=${c.key_fp??"?"}`:" \xB7 signed v1":"";return{content:[{type:"text",text:`\u{F115D} Audit #${s.id} logged
|
|
1
|
+
import{TOOL_ANNOTATIONS as p}from"../tool-annotations.js";import{resolveAttribution as _}from"../attribution.js";import{parseAuditSignature as h}from"../audit.js";const b=[{name:"wyrm_audit_log",description:"Append a hash-chained audit event. Each entry references the previous entry's sha-256, so tampering breaks the chain. Optional Ed25519 signature layer. Used for SOC2/HIPAA-grade audit.",inputSchema:{type:"object",properties:{event_kind:{type:"string",description:"Type of event: 'tool_call', 'key_export', 'truth_change', 'sync', 'migration', ..."},actor:{type:"string",description:"agent_id, 'system', operator email, etc."},projectPath:{type:"string",description:"Project path (optional)"},payload:{type:"object",description:"Structured event details (will be JSON-serialized)"},signature:{type:"string",description:"Optional explicit Ed25519 signature stored as-is (legacy v1 = base64 over the row_hash). Omit to auto-sign with the configured WYRM_AUDIT_SIGNING_KEY: v2 format 'v2:<key_fp>:<base64>' binding agent_id/run_id into the signed payload."}},required:["event_kind","payload"]},annotations:p.wyrm_audit_log,aliases:[],handler:async(i,n)=>{const{auditLog:r,db:e}=n,{event_kind:t,actor:a,projectPath:o,payload:l,signature:u}=i,y=o?e.getProject(o):null,s=r.log({event_kind:t,actor:a,project_id:y?.id??null,payload:l,signature:u}),d=_(s),c=h(s.signature),g=c?c.version===2?` \xB7 signed v2 key=${c.key_fp??"?"}`:" \xB7 signed v1":"";return{content:[{type:"text",text:`\u{F115D} Audit #${s.id} logged: row_hash ${s.row_hash.slice(0,16)}\u2026 \xB7 actor=${d.actor}${d.run_id?` \xB7 run=${d.run_id}`:""}${g}`}]}}},{name:"wyrm_audit_verify",description:"Walk the entire audit_log chain and verify every prev_hash / row_hash. Returns ok=true if intact, or first_invalid_id when tampering is detected. Optional public_keys also verify Ed25519 signatures.",inputSchema:{type:"object",properties:{public_keys:{type:"array",items:{type:"string"},description:"Optional PEM-encoded Ed25519 public keys. When given, every signed row is checked: v2 rows against the key matching their fingerprint, v1 rows against all keys. Fail-closed."}}},annotations:p.wyrm_audit_verify,aliases:[],handler:async(i,n)=>{const{auditLog:r}=n,{public_keys:e}=i,t=Array.isArray(e)?e.filter(o=>typeof o=="string"):[],a=r.verify(t.length>0?{publicKeys:t}:void 0);return{content:[{type:"text",text:JSON.stringify(a,null,2)}]}}},{name:"wyrm_audit_export",description:"Export a date-ranged tamper-evident audit bundle (JSON). Includes full chain so an external auditor can verify offline without DB access. Use range_start='2026-01-01' for SOC2 audit windows.",inputSchema:{type:"object",properties:{range_start:{type:"string",description:"ISO timestamp, inclusive lower bound (omit for genesis)"},range_end:{type:"string",description:"ISO timestamp, inclusive upper bound (omit for latest)"}}},annotations:p.wyrm_audit_export,aliases:[],handler:async(i,n)=>{const{auditLog:r}=n,{range_start:e,range_end:t}=i,a=r.export({range_start:e,range_end:t});return{content:[{type:"text",text:JSON.stringify(a,null,2)}]}}}];export{b as auditToolSpecs};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AsyncLocalStorage as
|
|
1
|
+
import{AsyncLocalStorage as d}from"node:async_hooks";const f=Object.freeze({agent_id:null,run_id:null,source:"legacy"}),h=64,x=160;function i(r){if(typeof r!="string")return null;const n=r.trim();return n.length===0||n.length>h||!/^[\x20-\x7E]+$/.test(n)||n.includes(";")?null:n}function g(r){const n=Array.isArray(r)?r[0]:r;if(typeof n!="string"||n.length===0||n.length>x)return null;const e=n.indexOf(";"),u=i(e===-1?n:n.slice(0,e));if(!u)return null;if(e===-1)return{agent_id:u,run_id:null};const s=n.slice(e+1);if(s.trim()==="")return{agent_id:u,run_id:null};const l=i(s);return l?{agent_id:u,run_id:l}:null}let c=null;function y(r){return r==="cli"||r==="http"||r==="mcp"||r.startsWith("mcp:")}function E(r){c=r===null?null:i(r)}function I(){return c}function D(r={}){let n=null,e=null;const u=r.meta?.["wyrm/actor"];if(typeof u=="string"){const t=g(u);t&&({agent_id:n,run_id:e}=t)}else if(u!==null&&typeof u=="object"){const t=u;n=i(t.agent_id),e=i(t.run_id)}if(r.header!==void 0){const t=g(r.header);t&&(n=n??t.agent_id,e=e??t.run_id)}const s=[{source:"param",agent:i(r.args?.actor),run:i(r.args?.run_id)},{source:"meta",agent:n,run:e},{source:"env",agent:i(process.env.WYRM_AGENT_ID),run:i(process.env.WYRM_RUN_ID)},{source:"client",agent:i(r.clientName),run:null}];let l=null,o=null,a=null,_=null;for(const t of s)l===null&&t.agent!==null&&(l=t.agent,a=t.source),o===null&&t.run!==null&&(o=t.run,_=t.source);if(r.transport==="mcp"){const t=i(r.clientName);if(E(t?i(`mcp:${t}`)??"mcp":"mcp"),l===null)return{agent_id:"mcp",run_id:o,source:"writer"}}return r.transport==="http"&&l===null?{agent_id:"http",run_id:o,source:"writer"}:l===null&&o===null?f:{agent_id:l,run_id:o,source:a??_??"legacy"}}function N(r){return r.run_id!==null?!0:r.agent_id!==null&&r.source!=="client"&&r.source!=="writer"&&r.source!=="legacy"}const p=new d;function W(r,n){return p.run(r,n)}const m=new d;function v(r,n){return m.run(r,n)}function M(){return m.getStore()??null}function A(){const r=p.getStore();if(r)return r;const n=i(process.env.WYRM_AGENT_ID),e=i(process.env.WYRM_RUN_ID),u=i(process.env.WYRM_WRITER_ID);return n!==null?{agent_id:n,run_id:e,source:"env"}:u!==null?{agent_id:u,run_id:e,source:"writer"}:c!==null?{agent_id:c,run_id:e,source:"writer"}:e!==null?{agent_id:null,run_id:e,source:"env"}:f}function w(r){const n=A(),e=n.source==="writer"&&n.agent_id!==null&&(n.agent_id===c||y(n.agent_id));if(n.agent_id!==null&&!e)return n;const u=i(r);return u===null?n:{agent_id:u,run_id:n.run_id,source:"writer"}}function S(r,n){const e=w(r);return e===A()?n():W(e,n)}export{f as LEGACY_ENVELOPE,M as currentReadCacheKey,A as getActor,I as getDefaultWriter,N as hasExplicitAttribution,g as parseWyrmActorHeader,D as resolveActorEnvelope,S as runAsWriter,W as runWithActor,v as runWithReadCacheKey,i as sanitizeActorId,E as setDefaultWriter,w as writerEnvelope};
|