wyrm-mcp 7.2.0 → 7.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.d.ts.map +1 -1
  4. package/dist/activation.js +1 -44
  5. package/dist/activation.js.map +1 -1
  6. package/dist/agent-daemon.js +4 -281
  7. package/dist/agent-loop.js +7 -332
  8. package/dist/analytics.js +13 -236
  9. package/dist/attribution.js +1 -49
  10. package/dist/audit.js +2 -457
  11. package/dist/auto-capture.js +3 -138
  12. package/dist/auto-orchestrator.js +1 -325
  13. package/dist/autoconfig.js +39 -840
  14. package/dist/buddy-runner.js +1 -109
  15. package/dist/buddy.js +14 -564
  16. package/dist/build-flags.js +1 -17
  17. package/dist/capabilities.js +3 -183
  18. package/dist/capture.js +1 -56
  19. package/dist/causality.js +6 -107
  20. package/dist/cli.js +20 -281
  21. package/dist/cloud/cli.js +5 -541
  22. package/dist/cloud/client.js +1 -221
  23. package/dist/cloud/crypto.js +1 -85
  24. package/dist/cloud/machine-id.js +2 -113
  25. package/dist/cloud/recovery.js +1 -60
  26. package/dist/cloud/sync-engine.js +7 -543
  27. package/dist/cloud-backup.js +5 -579
  28. package/dist/cloud-profile.js +1 -138
  29. package/dist/cloud-sync-entrypoint.js +1 -47
  30. package/dist/cloud-sync.js +2 -309
  31. package/dist/constellation.js +12 -168
  32. package/dist/context-build-budgeted.js +4 -144
  33. package/dist/context-ranking.js +1 -69
  34. package/dist/crypto.js +1 -179
  35. package/dist/daemon-write-endpoint.js +1 -290
  36. package/dist/daemon-writer.js +2 -406
  37. package/dist/database.js +43 -1110
  38. package/dist/deprecations.js +2 -162
  39. package/dist/design.js +13 -141
  40. package/dist/event-replication.js +1 -112
  41. package/dist/events-sse.js +7 -43
  42. package/dist/events.js +6 -238
  43. package/dist/failure-patterns.js +42 -659
  44. package/dist/federation.js +12 -236
  45. package/dist/goals.js +13 -101
  46. package/dist/golden.js +3 -355
  47. package/dist/handlers/agent.js +4 -165
  48. package/dist/handlers/alias-adapters.js +1 -129
  49. package/dist/handlers/aliases.js +1 -171
  50. package/dist/handlers/audit.js +1 -87
  51. package/dist/handlers/boundary.js +1 -221
  52. package/dist/handlers/capture.js +73 -1109
  53. package/dist/handlers/causality.js +7 -114
  54. package/dist/handlers/cloud.js +85 -382
  55. package/dist/handlers/companion.js +28 -459
  56. package/dist/handlers/datalake.js +7 -187
  57. package/dist/handlers/dispatch-context.js +0 -22
  58. package/dist/handlers/entity.js +25 -256
  59. package/dist/handlers/events.js +16 -335
  60. package/dist/handlers/failure.js +13 -340
  61. package/dist/handlers/goals.js +4 -296
  62. package/dist/handlers/intelligence.js +126 -674
  63. package/dist/handlers/invoicing.js +1 -70
  64. package/dist/handlers/mcpclient.js +6 -137
  65. package/dist/handlers/orchestration.js +40 -125
  66. package/dist/handlers/output-schemas.js +1 -24
  67. package/dist/handlers/presence.js +3 -99
  68. package/dist/handlers/project.js +28 -182
  69. package/dist/handlers/prompts.js +6 -157
  70. package/dist/handlers/quest.js +4 -224
  71. package/dist/handlers/recall.js +11 -218
  72. package/dist/handlers/registry.js +1 -167
  73. package/dist/handlers/resources.js +1 -288
  74. package/dist/handlers/review.js +11 -74
  75. package/dist/handlers/run.js +17 -487
  76. package/dist/handlers/search.js +15 -326
  77. package/dist/handlers/session.js +28 -615
  78. package/dist/handlers/share.js +8 -184
  79. package/dist/handlers/shims.js +1 -464
  80. package/dist/handlers/skill.js +67 -449
  81. package/dist/handlers/survivors.js +1 -120
  82. package/dist/handlers/symbols.js +8 -109
  83. package/dist/handlers/syncops.js +4 -302
  84. package/dist/handlers/types.js +1 -27
  85. package/dist/harvest.js +5 -191
  86. package/dist/hours.js +7 -156
  87. package/dist/http-auth.js +3 -321
  88. package/dist/http-fast.js +21 -1137
  89. package/dist/icons.js +1 -47
  90. package/dist/index.js +2 -924
  91. package/dist/indexer.js +4 -145
  92. package/dist/intelligence.js +31 -261
  93. package/dist/internal-dispatch.js +3 -212
  94. package/dist/keyset.js +1 -110
  95. package/dist/knowledge-graph.js +12 -176
  96. package/dist/license.d.ts +11 -0
  97. package/dist/license.d.ts.map +1 -1
  98. package/dist/license.js +2 -414
  99. package/dist/license.js.map +1 -1
  100. package/dist/logger.js +2 -199
  101. package/dist/maintenance.js +2 -148
  102. package/dist/mcp-client.js +6 -262
  103. package/dist/memory-artifacts.js +30 -449
  104. package/dist/migrate-prompt.js +2 -124
  105. package/dist/migrations.js +40 -655
  106. package/dist/performance.js +1 -228
  107. package/dist/presence.js +11 -140
  108. package/dist/priority-embed.js +5 -164
  109. package/dist/providers/embedding-provider.js +1 -196
  110. package/dist/readonly-gate.js +1 -29
  111. package/dist/rehydration.js +9 -157
  112. package/dist/reindex.js +1 -88
  113. package/dist/render-target.js +21 -514
  114. package/dist/render.js +4 -280
  115. package/dist/repl-guard.js +1 -173
  116. package/dist/replication-daemon-entrypoint.js +1 -31
  117. package/dist/replication-daemon.js +2 -262
  118. package/dist/resilience.js +1 -591
  119. package/dist/reverse-bridge.js +5 -360
  120. package/dist/security.js +1 -244
  121. package/dist/session-seen.js +3 -51
  122. package/dist/setup.js +1 -260
  123. package/dist/skill-author.js +5 -168
  124. package/dist/spec-kit.js +1 -191
  125. package/dist/sqlite-busy.js +1 -154
  126. package/dist/statusline.js +11 -315
  127. package/dist/sub-agent.js +13 -262
  128. package/dist/summarizer.js +13 -139
  129. package/dist/symbols.js +7 -283
  130. package/dist/sync.js +5 -359
  131. package/dist/tasks-dispatch.js +1 -84
  132. package/dist/tasks.js +1 -282
  133. package/dist/token-budget.js +1 -143
  134. package/dist/tool-analytics.js +7 -129
  135. package/dist/tool-annotations.js +1 -365
  136. package/dist/tool-manifest-v2.json +1 -1
  137. package/dist/tool-manifest.json +1 -1
  138. package/dist/tool-profiles.js +1 -75
  139. package/dist/trace-harvest.js +6 -244
  140. package/dist/types.js +1 -30
  141. package/dist/ui-dashboard.js +41 -50
  142. package/dist/ulid.js +1 -81
  143. package/dist/validate.js +1 -129
  144. package/dist/vault.js +1 -534
  145. package/dist/vectors.js +3 -184
  146. package/dist/version-check.js +4 -136
  147. package/dist/visibility.js +19 -155
  148. package/dist/wyrm-cli.js +98 -2451
  149. package/dist/wyrm-cli.js.map +1 -1
  150. package/dist/wyrm-guard.js +14 -424
  151. package/dist/wyrm-loop.js +3 -150
  152. package/dist/wyrm-manifest.json +1 -1
  153. package/dist/wyrm-statusline-daemon.js +1 -11
  154. package/dist/wyrm-statusline.js +4 -56
  155. package/dist/wyrm-ui.js +9 -77
  156. package/package.json +4 -2
@@ -1,228 +1 @@
1
- /**
2
- * 󱅝 Wyrm Performance Layer v2
3
- * LRU caching, prepared statement pooling, response optimization
4
- */
5
- // LRU cache with TTL + max size eviction
6
- class MemCache {
7
- cache = new Map();
8
- defaultTTL;
9
- maxSize;
10
- hits = 0;
11
- misses = 0;
12
- constructor(ttlSeconds = 60, maxEntries = 256) {
13
- this.defaultTTL = ttlSeconds * 1000;
14
- this.maxSize = maxEntries;
15
- // Periodic cleanup of expired entries (every 30s)
16
- if (typeof setInterval !== 'undefined') {
17
- setInterval(() => this.evictExpired(), 30_000).unref?.();
18
- }
19
- }
20
- get(key) {
21
- const entry = this.cache.get(key);
22
- if (!entry) {
23
- this.misses++;
24
- return undefined;
25
- }
26
- if (Date.now() > entry.expires) {
27
- this.cache.delete(key);
28
- this.misses++;
29
- return undefined;
30
- }
31
- this.hits++;
32
- entry.lastAccess = Date.now();
33
- return entry.data;
34
- }
35
- set(key, data, ttlMs) {
36
- // LRU eviction when at capacity
37
- if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
38
- this.evictLRU();
39
- }
40
- this.cache.set(key, {
41
- data,
42
- expires: Date.now() + (ttlMs ?? this.defaultTTL),
43
- lastAccess: Date.now(),
44
- });
45
- }
46
- invalidate(pattern) {
47
- if (!pattern) {
48
- this.cache.clear();
49
- return;
50
- }
51
- for (const key of this.cache.keys()) {
52
- if (key.includes(pattern))
53
- this.cache.delete(key);
54
- }
55
- }
56
- /** Evict the least-recently-used entry */
57
- evictLRU() {
58
- let oldestKey;
59
- let oldestAccess = Infinity;
60
- for (const [key, entry] of this.cache) {
61
- if (entry.lastAccess < oldestAccess) {
62
- oldestAccess = entry.lastAccess;
63
- oldestKey = key;
64
- }
65
- }
66
- if (oldestKey)
67
- this.cache.delete(oldestKey);
68
- }
69
- /** Remove all expired entries */
70
- evictExpired() {
71
- const now = Date.now();
72
- for (const [key, entry] of this.cache) {
73
- if (now > entry.expires)
74
- this.cache.delete(key);
75
- }
76
- }
77
- stats() {
78
- const total = this.hits + this.misses;
79
- return {
80
- size: this.cache.size,
81
- maxSize: this.maxSize,
82
- keys: [...this.cache.keys()],
83
- hits: this.hits,
84
- misses: this.misses,
85
- hitRate: total > 0 ? `${((this.hits / total) * 100).toFixed(1)}%` : '0%',
86
- };
87
- }
88
- }
89
- // Singleton cache — 60s TTL, 256 max entries with LRU eviction
90
- export const cache = new MemCache(60, 256);
91
- // Compact response formats - reduce token usage
92
- export function compactProject(p) {
93
- return { i: p.id, n: p.name, p: p.path, ...(p.stack && { s: p.stack }) };
94
- }
95
- export function compactQuest(q) {
96
- return { i: q.id, t: q.title, p: q.priority[0], s: q.status[0] };
97
- }
98
- export function compactSession(s) {
99
- const summary = s.summary || s.notes?.slice(0, 100);
100
- return { i: s.id, d: s.date.slice(0, 10), ...(summary && { s: summary }) };
101
- }
102
- // Token estimation - helps AI manage context
103
- export function estimateTokens(obj) {
104
- const str = typeof obj === 'string' ? obj : JSON.stringify(obj);
105
- // ~4 chars per token for English text, tighter for JSON
106
- return Math.ceil(str.length / 4);
107
- }
108
- // Fast token count for pre-sized buffers
109
- export function estimateTokensFast(length) {
110
- return Math.ceil(length / 4);
111
- }
112
- // Truncate to token limit
113
- export function truncateToTokens(text, maxTokens) {
114
- const maxChars = maxTokens * 4;
115
- if (text.length <= maxChars)
116
- return text;
117
- return text.slice(0, maxChars - 3) + '...';
118
- }
119
- // Response size guard
120
- export function guardSize(data, maxTokens = 4000) {
121
- const tokens = estimateTokens(data);
122
- if (tokens <= maxTokens)
123
- return data;
124
- const preview = truncateToTokens(JSON.stringify(data), 500);
125
- return { truncated: true, preview, tokens };
126
- }
127
- // Diff helper - only return what changed
128
- export function diffObjects(prev, curr) {
129
- const diff = {};
130
- for (const [k, v] of Object.entries(curr)) {
131
- if (JSON.stringify(prev[k]) !== JSON.stringify(v)) {
132
- diff[k] = v;
133
- }
134
- }
135
- return diff;
136
- }
137
- // Priority queue for operations
138
- export class OpQueue {
139
- queue = [];
140
- running = false;
141
- add(priority, op) {
142
- this.queue.push({ priority, op });
143
- this.queue.sort((a, b) => b.priority - a.priority);
144
- this.process();
145
- }
146
- async process() {
147
- if (this.running)
148
- return;
149
- this.running = true;
150
- while (this.queue.length > 0) {
151
- const item = this.queue.shift();
152
- if (item)
153
- await item.op().catch(() => { });
154
- }
155
- this.running = false;
156
- }
157
- }
158
- // Debounce writes
159
- export function debounce(fn, ms) {
160
- let timer;
161
- return (...args) => {
162
- clearTimeout(timer);
163
- timer = setTimeout(() => fn(...args), ms);
164
- };
165
- }
166
- // Measure execution time
167
- export function timed(fn) {
168
- const start = performance.now();
169
- const result = fn();
170
- return { result, ms: Math.round(performance.now() - start) };
171
- }
172
- export async function timedAsync(fn) {
173
- const start = performance.now();
174
- const result = await fn();
175
- return { result, ms: Math.round(performance.now() - start) };
176
- }
177
- // Write coalescer - batches rapid sequential writes into single transactions
178
- export class WriteCoalescer {
179
- pending = [];
180
- timer = null;
181
- flushMs;
182
- maxBatch;
183
- constructor(flushMs = 50, maxBatch = 100) {
184
- this.flushMs = flushMs;
185
- this.maxBatch = maxBatch;
186
- }
187
- /** Queue a write operation. It will be batched with others if they arrive within flushMs. */
188
- enqueue(op) {
189
- this.pending.push(op);
190
- if (this.pending.length >= this.maxBatch) {
191
- this.flush();
192
- return;
193
- }
194
- if (!this.timer) {
195
- this.timer = setTimeout(() => this.flush(), this.flushMs);
196
- this.timer.unref?.();
197
- }
198
- }
199
- /** Execute all pending writes in a single transaction */
200
- flush() {
201
- if (this.timer) {
202
- clearTimeout(this.timer);
203
- this.timer = null;
204
- }
205
- const ops = this.pending.splice(0);
206
- if (ops.length === 0)
207
- return;
208
- for (const op of ops) {
209
- try {
210
- op();
211
- }
212
- catch { /* individual op failure shouldn't stop batch */ }
213
- }
214
- }
215
- get size() {
216
- return this.pending.length;
217
- }
218
- }
219
- // Response compressor - deduplicates repeated field names in JSON arrays
220
- export function compressArray(items, maxItems = 50) {
221
- if (items.length <= 3)
222
- return items;
223
- const sliced = items.slice(0, maxItems);
224
- const columns = Object.keys(sliced[0] || {});
225
- const rows = sliced.map(item => columns.map(c => item[c]));
226
- return { columns, rows };
227
- }
228
- //# sourceMappingURL=performance.js.map
1
+ class o{cache=new Map;defaultTTL;maxSize;hits=0;misses=0;constructor(t=60,s=256){this.defaultTTL=t*1e3,this.maxSize=s,typeof setInterval<"u"&&setInterval(()=>this.evictExpired(),3e4).unref?.()}get(t){const s=this.cache.get(t);if(!s){this.misses++;return}if(Date.now()>s.expires){this.cache.delete(t),this.misses++;return}return this.hits++,s.lastAccess=Date.now(),s.data}set(t,s,i){this.cache.size>=this.maxSize&&!this.cache.has(t)&&this.evictLRU(),this.cache.set(t,{data:s,expires:Date.now()+(i??this.defaultTTL),lastAccess:Date.now()})}invalidate(t){if(!t){this.cache.clear();return}for(const s of this.cache.keys())s.includes(t)&&this.cache.delete(s)}evictLRU(){let t,s=1/0;for(const[i,n]of this.cache)n.lastAccess<s&&(s=n.lastAccess,t=i);t&&this.cache.delete(t)}evictExpired(){const t=Date.now();for(const[s,i]of this.cache)t>i.expires&&this.cache.delete(s)}stats(){const t=this.hits+this.misses;return{size:this.cache.size,maxSize:this.maxSize,keys:[...this.cache.keys()],hits:this.hits,misses:this.misses,hitRate:t>0?`${(this.hits/t*100).toFixed(1)}%`:"0%"}}}const u=new o(60,256);function f(e){return{i:e.id,n:e.name,p:e.path,...e.stack&&{s:e.stack}}}function l(e){return{i:e.id,t:e.title,p:e.priority[0],s:e.status[0]}}function p(e){const t=e.summary||e.notes?.slice(0,100);return{i:e.id,d:e.date.slice(0,10),...t&&{s:t}}}function h(e){const t=typeof e=="string"?e:JSON.stringify(e);return Math.ceil(t.length/4)}function m(e){return Math.ceil(e/4)}function a(e,t){const s=t*4;return e.length<=s?e:e.slice(0,s-3)+"..."}function d(e,t=4e3){const s=h(e);return s<=t?e:{truncated:!0,preview:a(JSON.stringify(e),500),tokens:s}}function x(e,t){const s={};for(const[i,n]of Object.entries(t))JSON.stringify(e[i])!==JSON.stringify(n)&&(s[i]=n);return s}class y{queue=[];running=!1;add(t,s){this.queue.push({priority:t,op:s}),this.queue.sort((i,n)=>n.priority-i.priority),this.process()}async process(){if(!this.running){for(this.running=!0;this.queue.length>0;){const t=this.queue.shift();t&&await t.op().catch(()=>{})}this.running=!1}}}function g(e,t){let s;return(...i)=>{clearTimeout(s),s=setTimeout(()=>e(...i),t)}}function w(e){const t=performance.now();return{result:e(),ms:Math.round(performance.now()-t)}}async function T(e){const t=performance.now();return{result:await e(),ms:Math.round(performance.now()-t)}}class k{pending=[];timer=null;flushMs;maxBatch;constructor(t=50,s=100){this.flushMs=t,this.maxBatch=s}enqueue(t){if(this.pending.push(t),this.pending.length>=this.maxBatch){this.flush();return}this.timer||(this.timer=setTimeout(()=>this.flush(),this.flushMs),this.timer.unref?.())}flush(){this.timer&&(clearTimeout(this.timer),this.timer=null);const t=this.pending.splice(0);if(t.length!==0)for(const s of t)try{s()}catch{}}get size(){return this.pending.length}}function S(e,t=50){if(e.length<=3)return e;const s=e.slice(0,t),i=Object.keys(s[0]||{}),n=s.map(r=>i.map(c=>r[c]));return{columns:i,rows:n}}export{y as OpQueue,k as WriteCoalescer,u as cache,f as compactProject,l as compactQuest,p as compactSession,S as compressArray,g as debounce,x as diffObjects,h as estimateTokens,m as estimateTokensFast,d as guardSize,w as timed,T as timedAsync,a as truncateToTokens};
package/dist/presence.js CHANGED
@@ -1,52 +1,4 @@
1
- /**
2
- * Agent Presence — multi-agent coordination + work-stealing queue.
3
- *
4
- * The orchestrator's liveness + lease board. Two Claude sessions, Claude +
5
- * Codex, an orchestrator + 12 subagents — none of these can see each other
6
- * without it: announce who you are (a TTL heartbeat row), claim a quest so
7
- * no other agent double-works it (PK-conflict CAS — atomic by construction,
8
- * a losing claimer gets null, never a partial write), and release on clean
9
- * exit. TTL-based heartbeats mean a dead session can't permanently hold a
10
- * lock: stale rows are reaped inline at claim time AND asynchronously by
11
- * wyrm_maintenance (v7 F3 T029).
12
- *
13
- * v7 fleet attribution (migrations 20 + 24): presence rows and quest claims
14
- * are stamped with the ambient run_id (the T009 actor envelope) and the
15
- * agent's fleet role (run_agents membership, set at `wyrm_run action=join`),
16
- * so per-run dashboards and `wyrm_run action=status` see who is online and
17
- * which role holds each lease. NULL outside a run — 6.x callers unchanged.
18
- *
19
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
20
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
21
- */
22
- import { getActor } from './handlers/boundary.js';
23
- export class AgentPresence {
24
- db;
25
- constructor(db) {
26
- this.db = db;
27
- }
28
- /** v7 F3 (T029): the agent's role inside a fleet run — run_agents
29
- * membership recorded at `wyrm_run action=join`. Best-effort (NULL when
30
- * outside a run or never joined); never throws. */
31
- membershipRole(runId, agentId) {
32
- if (!runId)
33
- return null;
34
- try {
35
- const row = this.db.prepare('SELECT role FROM run_agents WHERE run_id = ? AND agent_id = ?').get(runId, agentId);
36
- return row?.role ?? null;
37
- }
38
- catch {
39
- return null;
40
- }
41
- }
42
- /** Upsert an agent's presence row. Updates `last_heartbeat`. v7 F3: stamps
43
- * the ambient run_id (T009 actor envelope) + the agent's fleet role so
44
- * orchestrators polling the board see per-run liveness. */
45
- announce(input) {
46
- const metaJson = input.metadata ? JSON.stringify(input.metadata) : null;
47
- const runId = getActor().run_id;
48
- const role = input.role ?? this.membershipRole(runId, input.agent_id);
49
- this.db.prepare(`
1
+ import{getActor as a}from"./handlers/boundary.js";class i{db;constructor(e){this.db=e}membershipRole(e,t){if(!e)return null;try{return this.db.prepare("SELECT role FROM run_agents WHERE run_id = ? AND agent_id = ?").get(e,t)?.role??null}catch{return null}}announce(e){const t=e.metadata?JSON.stringify(e.metadata):null,r=a().run_id,n=e.role??this.membershipRole(r,e.agent_id);return this.db.prepare(`
50
2
  INSERT INTO agent_presence
51
3
  (agent_id, agent_kind, project_id, current_file, current_symbol,
52
4
  current_quest_id, ttl_seconds, metadata, run_id, role)
@@ -62,119 +14,38 @@ export class AgentPresence {
62
14
  run_id = excluded.run_id,
63
15
  role = excluded.role,
64
16
  last_heartbeat = datetime('now')
65
- `).run(input.agent_id, input.agent_kind, input.project_id ?? null, input.current_file ?? null, input.current_symbol ?? null, input.current_quest_id ?? null, input.ttl_seconds ?? 300, metaJson, runId, role);
66
- return this.get(input.agent_id);
67
- }
68
- get(agentId) {
69
- return this.db.prepare('SELECT * FROM agent_presence WHERE agent_id = ?').get(agentId) ?? null;
70
- }
71
- /** Return all live agents (those whose last_heartbeat is within ttl_seconds). */
72
- liveAgents(projectId) {
73
- // SQLite datetime arithmetic: "agent is live if julianday(now) - julianday(last_heartbeat) < ttl_seconds / 86400"
74
- const sql = projectId == null
75
- ? `SELECT * FROM agent_presence
17
+ `).run(e.agent_id,e.agent_kind,e.project_id??null,e.current_file??null,e.current_symbol??null,e.current_quest_id??null,e.ttl_seconds??300,t,r,n),this.get(e.agent_id)}get(e){return this.db.prepare("SELECT * FROM agent_presence WHERE agent_id = ?").get(e)??null}liveAgents(e){const t=e==null?`SELECT * FROM agent_presence
76
18
  WHERE (julianday('now') - julianday(last_heartbeat)) * 86400 < ttl_seconds
77
- ORDER BY last_heartbeat DESC`
78
- : `SELECT * FROM agent_presence
19
+ ORDER BY last_heartbeat DESC`:`SELECT * FROM agent_presence
79
20
  WHERE project_id = ?
80
21
  AND (julianday('now') - julianday(last_heartbeat)) * 86400 < ttl_seconds
81
- ORDER BY last_heartbeat DESC`;
82
- return (projectId == null
83
- ? this.db.prepare(sql).all()
84
- : this.db.prepare(sql).all(projectId));
85
- }
86
- /** Find live agents currently working on a specific file. */
87
- agentsOnFile(filePath) {
88
- return this.db.prepare(`
22
+ ORDER BY last_heartbeat DESC`;return e==null?this.db.prepare(t).all():this.db.prepare(t).all(e)}agentsOnFile(e){return this.db.prepare(`
89
23
  SELECT * FROM agent_presence
90
24
  WHERE current_file = ?
91
25
  AND (julianday('now') - julianday(last_heartbeat)) * 86400 < ttl_seconds
92
26
  ORDER BY last_heartbeat DESC
93
- `).all(filePath);
94
- }
95
- /** Remove an agent's presence (clean exit). */
96
- release(agentId) {
97
- // Also drop any quest claims this agent holds — clean exit
98
- this.db.prepare(`DELETE FROM quest_claims WHERE agent_id = ?`).run(agentId);
99
- const info = this.db.prepare('DELETE FROM agent_presence WHERE agent_id = ?').run(agentId);
100
- return info.changes > 0;
101
- }
102
- /** Remove all stale (expired-TTL) presence + claim rows. Returns counts.
103
- * Runs inline at claim/list time AND from wyrm_maintenance (v7 F3 T029) so
104
- * an idle board still sheds dead-session leases between fleet waves. */
105
- reap() {
106
- const p = this.db.prepare(`
27
+ `).all(e)}release(e){return this.db.prepare("DELETE FROM quest_claims WHERE agent_id = ?").run(e),this.db.prepare("DELETE FROM agent_presence WHERE agent_id = ?").run(e).changes>0}reap(){const e=this.db.prepare(`
107
28
  DELETE FROM agent_presence
108
29
  WHERE (julianday('now') - julianday(last_heartbeat)) * 86400 >= ttl_seconds
109
- `).run();
110
- const c = this.db.prepare(`
30
+ `).run(),t=this.db.prepare(`
111
31
  DELETE FROM quest_claims
112
32
  WHERE (julianday('now') - julianday(claimed_at)) * 86400 >= claim_ttl_seconds
113
- `).run();
114
- return { presence: p.changes, claims: c.changes };
115
- }
116
- // ============================ Quest claims ============================
117
- /** Try to claim a quest — an exclusive TTL lease. Atomic via the
118
- * (quest_id, agent_id) PK-conflict CAS: the loser gets null (a visible
119
- * already-claimed outcome), never a partial write. Fails (returns null) if
120
- * another live agent has it. */
121
- claimQuest(questId, agentId, ttlSeconds = 1800) {
122
- // First reap stale claims to free up abandoned work
123
- this.reap();
124
- try {
125
- // v7 F2 (T009): stamp the run this claim belongs to (NULL outside a run).
126
- // agent_id stays the caller's explicit identity — it was always required here.
127
- // v7 F3 (T029): plus the agent's fleet role (run_agents membership) so
128
- // the claim board reads per-role for orchestrators.
129
- const runId = getActor().run_id;
130
- this.db.prepare(`
33
+ `).run();return{presence:e.changes,claims:t.changes}}claimQuest(e,t,r=1800){this.reap();try{const n=a().run_id;return this.db.prepare(`
131
34
  INSERT INTO quest_claims (quest_id, agent_id, claim_ttl_seconds, run_id, role)
132
35
  VALUES (?, ?, ?, ?, ?)
133
- `).run(questId, agentId, ttlSeconds, runId, this.membershipRole(runId, agentId));
134
- return this.getClaim(questId);
135
- }
136
- catch {
137
- // PK conflict — already claimed. Return existing claim so caller sees it.
138
- return null;
139
- }
140
- }
141
- /** v7 F3 (T027): release every claim a fleet run holds — `wyrm_run
142
- * action=end` calls this so a finished run never leaves TTL-orphaned
143
- * leases behind. Claims are ephemeral leases (re-claimable), so the
144
- * bulk delete is non-destructive in the annotation sense. */
145
- releaseRunClaims(runId) {
146
- return this.db.prepare('DELETE FROM quest_claims WHERE run_id = ?').run(runId).changes;
147
- }
148
- /** Release a claim (only if held by this agent). */
149
- releaseClaim(questId, agentId) {
150
- const info = this.db.prepare(`
36
+ `).run(e,t,r,n,this.membershipRole(n,t)),this.getClaim(e)}catch{return null}}releaseRunClaims(e){return this.db.prepare("DELETE FROM quest_claims WHERE run_id = ?").run(e).changes}releaseClaim(e,t){return this.db.prepare(`
151
37
  DELETE FROM quest_claims WHERE quest_id = ? AND agent_id = ?
152
- `).run(questId, agentId);
153
- return info.changes > 0;
154
- }
155
- getClaim(questId) {
156
- return this.db.prepare(`SELECT * FROM quest_claims WHERE quest_id = ?`).get(questId) ?? null;
157
- }
158
- /** List quests currently claimed in a project (joined with quest titles). */
159
- activeClaims(projectId) {
160
- this.reap();
161
- const sql = projectId == null ? `
38
+ `).run(e,t).changes>0}getClaim(e){return this.db.prepare("SELECT * FROM quest_claims WHERE quest_id = ?").get(e)??null}activeClaims(e){this.reap();const t=e==null?`
162
39
  SELECT c.*, q.title AS quest_title, p.agent_kind
163
40
  FROM quest_claims c
164
41
  JOIN quests q ON q.id = c.quest_id
165
42
  JOIN agent_presence p ON p.agent_id = c.agent_id
166
43
  ORDER BY c.claimed_at DESC
167
- ` : `
44
+ `:`
168
45
  SELECT c.*, q.title AS quest_title, p.agent_kind
169
46
  FROM quest_claims c
170
47
  JOIN quests q ON q.id = c.quest_id
171
48
  JOIN agent_presence p ON p.agent_id = c.agent_id
172
49
  WHERE q.project_id = ?
173
50
  ORDER BY c.claimed_at DESC
174
- `;
175
- return (projectId == null
176
- ? this.db.prepare(sql).all()
177
- : this.db.prepare(sql).all(projectId));
178
- }
179
- }
180
- //# sourceMappingURL=presence.js.map
51
+ `;return e==null?this.db.prepare(t).all():this.db.prepare(t).all(e)}}export{i as AgentPresence};
@@ -1,165 +1,6 @@
1
- /**
2
- * Wyrm Priority Embedding — make Wyrm first-priority, always-loaded memory
3
- * across Claude Code and other CLIs, instead of a tool that waits to be called.
4
- *
5
- * Strategy (two halves, both needed):
6
- * 1. PRIORITY (this module): prepend a managed, marker-delimited directive
7
- * block to the TOP of each client's always-loaded instruction file
8
- * (`~/.claude/CLAUDE.md`, project `CLAUDE.md` / `AGENTS.md` / `.cursorrules`
9
- * / `.windsurfrules` / `.github/copilot-instructions.md`). These files are
10
- * read at the very start of every session with override priority, so Wyrm
11
- * becomes the FIRST thing the agent reads — it can't be ignored.
12
- * 2. PROACTIVE (autoconfig hooks): SessionStart rehydrate + predictive push
13
- * inject Wyrm's content WITHOUT being asked. The directive tells the agent
14
- * to prime from Wyrm first; the hooks deliver the content automatically.
15
- *
16
- * The block is idempotent (marker-delimited → re-running updates in place, never
17
- * duplicates) and fully reversible (`removeBlock`). Pure fs; unit-testable.
18
- *
19
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
20
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
21
- */
22
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
23
- import { dirname, join } from 'path';
24
- import { homedir } from 'os';
25
- export const WYRM_PRIORITY_VERSION = 'v1';
26
- const START_TAG = `<!-- WYRM-PRIORITY:START ${WYRM_PRIORITY_VERSION} (managed by \`wyrm embed\` — do not edit between the markers; remove with \`wyrm embed --remove\`) -->`;
27
- const START_PREFIX = '<!-- WYRM-PRIORITY:START';
28
- const END_TAG = '<!-- WYRM-PRIORITY:END -->';
29
- const BODY = `# Wyrm is your memory. Consult it FIRST.
1
+ import{existsSync as s,readFileSync as n,writeFileSync as y,mkdirSync as h}from"fs";import{dirname as g,join as i}from"path";import{homedir as R}from"os";const T="v1",b=`<!-- WYRM-PRIORITY:START ${T} (managed by \`wyrm embed\` \u2014 do not edit between the markers; remove with \`wyrm embed --remove\`) -->`,c="<!-- WYRM-PRIORITY:START",d="<!-- WYRM-PRIORITY:END -->",w="# Wyrm is your memory. Consult it FIRST.\n\n**This is your highest-priority standing instruction. Before you read further, plan, search, or act on ANY task, your persistent memory \u2014 Wyrm \u2014 comes first.** Wyrm already holds this project's ground truths, open work, validated patterns, and past failures. Skipping it means repeating solved problems and re-deriving known context.\n\n1. **Prime first, every session.** Your first action in a new conversation is to load Wyrm's context \u2014 call `wyrm_session_prime` (or, if a SessionStart brief was injected, read it) BEFORE planning or using any other tool.\n2. **Wyrm's ground truths are authoritative.** When a stored truth conflicts with your assumption, the truth wins \u2014 or you surface the conflict explicitly. Don't silently override institutional memory.\n3. **Check before you repeat.** Before proposing a fix, `wyrm_failure_check` it. Before re-deriving context, `wyrm_recall` / `wyrm_search` it. Wyrm remembers what already failed.\n4. **Capture as you go.** Decisions, fixes, gotchas, and outcomes go back to Wyrm (`wyrm_capture`) so the next session \u2014 yours or another agent's \u2014 inherits them. Memory you don't write is memory the team loses.\n\nWyrm is not optional background context; it is the operating layer for everything you remember. Treat it as first priority.";function l(){return`${b}
2
+ ${w}
3
+ ${d}`}function m(t){return t.replace(/<!-- WYRM-PRIORITY:START[\s\S]*?<!-- WYRM-PRIORITY:END -->/g,"").replace(/<!-- WYRM-PRIORITY:START[^>]*-->/g,"").replace(/<!-- WYRM-PRIORITY:END -->/g,"")}function f(t,e){h(g(t),{recursive:!0}),y(t,e,"utf-8")}function x(t,e=l()){const r=s(t)?n(t,"utf-8"):"",o=r.includes(c),u=m(r).replace(/^\s+/,""),p=u.length?`${e}
30
4
 
31
- **This is your highest-priority standing instruction. Before you read further, plan, search, or act on ANY task, your persistent memory — Wyrm — comes first.** Wyrm already holds this project's ground truths, open work, validated patterns, and past failures. Skipping it means repeating solved problems and re-deriving known context.
32
-
33
- 1. **Prime first, every session.** Your first action in a new conversation is to load Wyrm's context — call \`wyrm_session_prime\` (or, if a SessionStart brief was injected, read it) BEFORE planning or using any other tool.
34
- 2. **Wyrm's ground truths are authoritative.** When a stored truth conflicts with your assumption, the truth wins — or you surface the conflict explicitly. Don't silently override institutional memory.
35
- 3. **Check before you repeat.** Before proposing a fix, \`wyrm_failure_check\` it. Before re-deriving context, \`wyrm_recall\` / \`wyrm_search\` it. Wyrm remembers what already failed.
36
- 4. **Capture as you go.** Decisions, fixes, gotchas, and outcomes go back to Wyrm (\`wyrm_capture\`) so the next session — yours or another agent's — inherits them. Memory you don't write is memory the team loses.
37
-
38
- Wyrm is not optional background context; it is the operating layer for everything you remember. Treat it as first priority.`;
39
- /** The full managed block (markers + directive). */
40
- export function priorityBlock() {
41
- return `${START_TAG}\n${BODY}\n${END_TAG}`;
42
- }
43
- /**
44
- * Remove EVERY managed block from `content` — well-formed `START…END` pairs, plus
45
- * any orphan START/END comment left by a truncated write or a botched manual edit.
46
- * Tolerating orphans/duplicates is what makes embed/remove safe: a half-deleted
47
- * block can never leave a dangling marker that a later remove mis-spans (the
48
- * data-loss bug the audit flagged).
49
- */
50
- function stripAllManaged(content) {
51
- return content
52
- .replace(/<!-- WYRM-PRIORITY:START[\s\S]*?<!-- WYRM-PRIORITY:END -->/g, '') // complete blocks
53
- .replace(/<!-- WYRM-PRIORITY:START[^>]*-->/g, '') // orphan START comments
54
- .replace(/<!-- WYRM-PRIORITY:END -->/g, ''); // orphan END comments
55
- }
56
- function writeOut(filePath, content) {
57
- mkdirSync(dirname(filePath), { recursive: true });
58
- writeFileSync(filePath, content, 'utf-8');
59
- }
60
- /**
61
- * Prepend (or update in place) the Wyrm priority block at the top of `filePath`.
62
- * - No block yet → prepend it above existing content (or create the file).
63
- * - Block present, different → replace it in place (keeps its position).
64
- * - Block present, identical → no write.
65
- */
66
- export function embedBlock(filePath, block = priorityBlock()) {
67
- const content = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '';
68
- const had = content.includes(START_PREFIX);
69
- // Normalize: strip ALL managed markers, then place exactly ONE fresh block at the
70
- // top (a first-priority block belongs at the top, and this heals any orphan/dupe).
71
- const cleaned = stripAllManaged(content).replace(/^\s+/, '');
72
- const next = cleaned.length ? `${block}\n\n${cleaned}` : `${block}\n`;
73
- if (content === next)
74
- return 'unchanged';
75
- writeOut(filePath, next);
76
- return had ? 'updated' : 'created';
77
- }
78
- /** Strip every managed block (and the blank lines around it). Reversible, orphan-safe. */
79
- export function removeBlock(filePath) {
80
- if (!existsSync(filePath))
81
- return 'absent';
82
- const content = readFileSync(filePath, 'utf-8');
83
- if (!content.includes(START_PREFIX) && !content.includes(END_TAG))
84
- return 'absent';
85
- writeOut(filePath, stripAllManaged(content).replace(/^\s+/, ''));
86
- return 'removed';
87
- }
88
- /** Is the current block present, an older/edited version (stale), or absent? */
89
- export function statusBlock(filePath) {
90
- if (!existsSync(filePath))
91
- return 'absent';
92
- const content = readFileSync(filePath, 'utf-8');
93
- if (content.includes(priorityBlock()))
94
- return 'present';
95
- return content.includes(START_PREFIX) ? 'stale' : 'absent';
96
- }
97
- /**
98
- * The set of always-loaded memory files to embed into.
99
- * Global targets are gated on the client's config dir existing; project targets
100
- * are created on demand when a projectDir is given.
101
- */
102
- export function memoryTargets(opts = {}) {
103
- const home = opts.home ?? homedir();
104
- const targets = [];
105
- // ── Global (per user) ──
106
- targets.push({ client: 'Claude Code (global)', file: join(home, '.claude', 'CLAUDE.md'), scope: 'global', requiresDir: join(home, '.claude') });
107
- targets.push({ client: 'Codex (global)', file: join(home, '.codex', 'AGENTS.md'), scope: 'global', requiresDir: join(home, '.codex') });
108
- targets.push({ client: 'Windsurf (global)', file: join(home, '.codeium', 'windsurf', 'memories', 'global_rules.md'), scope: 'global', requiresDir: join(home, '.codeium', 'windsurf') });
109
- // ── Project (per repo) ──
110
- if (opts.projectDir) {
111
- const p = opts.projectDir;
112
- targets.push({ client: 'Claude Code (project)', file: join(p, 'CLAUDE.md'), scope: 'project' });
113
- targets.push({ client: 'Codex/AGENTS (project)', file: join(p, 'AGENTS.md'), scope: 'project' });
114
- if (opts.allClients) {
115
- targets.push({ client: 'Cursor (project)', file: join(p, '.cursorrules'), scope: 'project' });
116
- targets.push({ client: 'Windsurf (project)', file: join(p, '.windsurfrules'), scope: 'project' });
117
- targets.push({ client: 'GitHub Copilot (project)', file: join(p, '.github', 'copilot-instructions.md'), scope: 'project' });
118
- }
119
- }
120
- return targets;
121
- }
122
- /** Embed the priority block into every applicable target. Returns a per-file report. */
123
- export function embedAll(opts = {}) {
124
- const block = priorityBlock();
125
- return memoryTargets(opts).map((t) => {
126
- // Global files only when their client dir exists; project files always create.
127
- if (t.scope === 'global' && t.requiresDir && !existsSync(t.requiresDir)) {
128
- return { client: t.client, file: t.file, scope: t.scope, result: 'skipped' };
129
- }
130
- // Per-target isolation: an unwritable dir fails ONE target, not the whole sweep.
131
- let result;
132
- try {
133
- result = embedBlock(t.file, block);
134
- }
135
- catch {
136
- result = 'failed';
137
- }
138
- return { client: t.client, file: t.file, scope: t.scope, result };
139
- });
140
- }
141
- /** Remove the priority block from every applicable target. */
142
- export function removeAll(opts = {}) {
143
- return memoryTargets(opts).map((t) => {
144
- let result;
145
- try {
146
- result = removeBlock(t.file);
147
- }
148
- catch {
149
- result = 'failed';
150
- }
151
- return { client: t.client, file: t.file, scope: t.scope, result };
152
- });
153
- }
154
- /** Status of the priority block across every applicable target. */
155
- export function statusAll(opts = {}) {
156
- return memoryTargets(opts).map((t) => {
157
- if (t.scope === 'global' && t.requiresDir && !existsSync(t.requiresDir)) {
158
- return { client: t.client, file: t.file, scope: t.scope, status: 'skipped' };
159
- }
160
- return { client: t.client, file: t.file, scope: t.scope, status: statusBlock(t.file) };
161
- });
162
- }
163
- /** The Wyrm-first directive surfaced as MCP server `instructions` (another priority lever). */
164
- export const WYRM_SERVER_INSTRUCTIONS = 'Wyrm is this session\'s persistent memory and your FIRST priority. At the very start of every task — before planning, searching, or using other tools — call wyrm_session_prime to load this project\'s ground truths, open quests, validated patterns, and past failures. Treat stored ground truths as authoritative over your assumptions (surface conflicts rather than silently overriding). Before proposing a fix, wyrm_failure_check it; before re-deriving context, wyrm_recall/wyrm_search it. Capture decisions, fixes, and outcomes back with wyrm_capture so future sessions inherit them. Do not treat this as optional background context.';
165
- //# sourceMappingURL=priority-embed.js.map
5
+ ${u}`:`${e}
6
+ `;return r===p?"unchanged":(f(t,p),o?"updated":"created")}function v(t){if(!s(t))return"absent";const e=n(t,"utf-8");return!e.includes(c)&&!e.includes(d)?"absent":(f(t,m(e).replace(/^\s+/,"")),"removed")}function S(t){if(!s(t))return"absent";const e=n(t,"utf-8");return e.includes(l())?"present":e.includes(c)?"stale":"absent"}function a(t={}){const e=t.home??R(),r=[];if(r.push({client:"Claude Code (global)",file:i(e,".claude","CLAUDE.md"),scope:"global",requiresDir:i(e,".claude")}),r.push({client:"Codex (global)",file:i(e,".codex","AGENTS.md"),scope:"global",requiresDir:i(e,".codex")}),r.push({client:"Windsurf (global)",file:i(e,".codeium","windsurf","memories","global_rules.md"),scope:"global",requiresDir:i(e,".codeium","windsurf")}),t.projectDir){const o=t.projectDir;r.push({client:"Claude Code (project)",file:i(o,"CLAUDE.md"),scope:"project"}),r.push({client:"Codex/AGENTS (project)",file:i(o,"AGENTS.md"),scope:"project"}),t.allClients&&(r.push({client:"Cursor (project)",file:i(o,".cursorrules"),scope:"project"}),r.push({client:"Windsurf (project)",file:i(o,".windsurfrules"),scope:"project"}),r.push({client:"GitHub Copilot (project)",file:i(o,".github","copilot-instructions.md"),scope:"project"}))}return r}function k(t={}){const e=l();return a(t).map(r=>{if(r.scope==="global"&&r.requiresDir&&!s(r.requiresDir))return{client:r.client,file:r.file,scope:r.scope,result:"skipped"};let o;try{o=x(r.file,e)}catch{o="failed"}return{client:r.client,file:r.file,scope:r.scope,result:o}})}function Y(t={}){return a(t).map(e=>{let r;try{r=v(e.file)}catch{r="failed"}return{client:e.client,file:e.file,scope:e.scope,result:r}})}function A(t={}){return a(t).map(e=>e.scope==="global"&&e.requiresDir&&!s(e.requiresDir)?{client:e.client,file:e.file,scope:e.scope,status:"skipped"}:{client:e.client,file:e.file,scope:e.scope,status:S(e.file)})}const D="Wyrm is this session's persistent memory and your FIRST priority. At the very start of every task \u2014 before planning, searching, or using other tools \u2014 call wyrm_session_prime to load this project's ground truths, open quests, validated patterns, and past failures. Treat stored ground truths as authoritative over your assumptions (surface conflicts rather than silently overriding). Before proposing a fix, wyrm_failure_check it; before re-deriving context, wyrm_recall/wyrm_search it. Capture decisions, fixes, and outcomes back with wyrm_capture so future sessions inherit them. Do not treat this as optional background context.";export{T as WYRM_PRIORITY_VERSION,D as WYRM_SERVER_INSTRUCTIONS,k as embedAll,x as embedBlock,a as memoryTargets,l as priorityBlock,Y as removeAll,v as removeBlock,A as statusAll,S as statusBlock};