wyrm-mcp 7.2.1 → 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 (150) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.js +1 -60
  4. package/dist/agent-daemon.js +4 -281
  5. package/dist/agent-loop.js +7 -332
  6. package/dist/analytics.js +13 -236
  7. package/dist/attribution.js +1 -49
  8. package/dist/audit.js +2 -457
  9. package/dist/auto-capture.js +3 -138
  10. package/dist/auto-orchestrator.js +1 -325
  11. package/dist/autoconfig.js +39 -840
  12. package/dist/buddy-runner.js +1 -109
  13. package/dist/buddy.js +14 -564
  14. package/dist/build-flags.js +1 -17
  15. package/dist/capabilities.js +3 -183
  16. package/dist/capture.js +1 -56
  17. package/dist/causality.js +6 -107
  18. package/dist/cli.js +20 -281
  19. package/dist/cloud/cli.js +5 -541
  20. package/dist/cloud/client.js +1 -221
  21. package/dist/cloud/crypto.js +1 -85
  22. package/dist/cloud/machine-id.js +2 -113
  23. package/dist/cloud/recovery.js +1 -60
  24. package/dist/cloud/sync-engine.js +7 -543
  25. package/dist/cloud-backup.js +5 -579
  26. package/dist/cloud-profile.js +1 -138
  27. package/dist/cloud-sync-entrypoint.js +1 -47
  28. package/dist/cloud-sync.js +2 -309
  29. package/dist/constellation.js +12 -168
  30. package/dist/context-build-budgeted.js +4 -144
  31. package/dist/context-ranking.js +1 -69
  32. package/dist/crypto.js +1 -179
  33. package/dist/daemon-write-endpoint.js +1 -290
  34. package/dist/daemon-writer.js +2 -406
  35. package/dist/database.js +43 -1110
  36. package/dist/deprecations.js +2 -162
  37. package/dist/design.js +13 -141
  38. package/dist/event-replication.js +1 -112
  39. package/dist/events-sse.js +7 -43
  40. package/dist/events.js +6 -238
  41. package/dist/failure-patterns.js +42 -659
  42. package/dist/federation.js +12 -236
  43. package/dist/goals.js +13 -101
  44. package/dist/golden.js +3 -355
  45. package/dist/handlers/agent.js +4 -165
  46. package/dist/handlers/alias-adapters.js +1 -129
  47. package/dist/handlers/aliases.js +1 -171
  48. package/dist/handlers/audit.js +1 -87
  49. package/dist/handlers/boundary.js +1 -221
  50. package/dist/handlers/capture.js +73 -1109
  51. package/dist/handlers/causality.js +7 -114
  52. package/dist/handlers/cloud.js +85 -382
  53. package/dist/handlers/companion.js +28 -459
  54. package/dist/handlers/datalake.js +7 -187
  55. package/dist/handlers/dispatch-context.js +0 -22
  56. package/dist/handlers/entity.js +25 -256
  57. package/dist/handlers/events.js +16 -335
  58. package/dist/handlers/failure.js +13 -340
  59. package/dist/handlers/goals.js +4 -296
  60. package/dist/handlers/intelligence.js +126 -674
  61. package/dist/handlers/invoicing.js +1 -70
  62. package/dist/handlers/mcpclient.js +6 -137
  63. package/dist/handlers/orchestration.js +40 -125
  64. package/dist/handlers/output-schemas.js +1 -24
  65. package/dist/handlers/presence.js +3 -99
  66. package/dist/handlers/project.js +28 -182
  67. package/dist/handlers/prompts.js +6 -157
  68. package/dist/handlers/quest.js +4 -224
  69. package/dist/handlers/recall.js +11 -218
  70. package/dist/handlers/registry.js +1 -167
  71. package/dist/handlers/resources.js +1 -288
  72. package/dist/handlers/review.js +11 -74
  73. package/dist/handlers/run.js +17 -487
  74. package/dist/handlers/search.js +15 -326
  75. package/dist/handlers/session.js +28 -615
  76. package/dist/handlers/share.js +8 -184
  77. package/dist/handlers/shims.js +1 -464
  78. package/dist/handlers/skill.js +67 -449
  79. package/dist/handlers/survivors.js +1 -120
  80. package/dist/handlers/symbols.js +8 -109
  81. package/dist/handlers/syncops.js +4 -302
  82. package/dist/handlers/types.js +1 -27
  83. package/dist/harvest.js +5 -191
  84. package/dist/hours.js +7 -156
  85. package/dist/http-auth.js +3 -321
  86. package/dist/http-fast.js +21 -1137
  87. package/dist/icons.js +1 -47
  88. package/dist/index.js +2 -924
  89. package/dist/indexer.js +4 -145
  90. package/dist/intelligence.js +31 -261
  91. package/dist/internal-dispatch.js +3 -212
  92. package/dist/keyset.js +1 -110
  93. package/dist/knowledge-graph.js +12 -176
  94. package/dist/license.js +2 -441
  95. package/dist/logger.js +2 -199
  96. package/dist/maintenance.js +2 -148
  97. package/dist/mcp-client.js +6 -262
  98. package/dist/memory-artifacts.js +30 -449
  99. package/dist/migrate-prompt.js +2 -124
  100. package/dist/migrations.js +40 -655
  101. package/dist/performance.js +1 -228
  102. package/dist/presence.js +11 -140
  103. package/dist/priority-embed.js +5 -164
  104. package/dist/providers/embedding-provider.js +1 -196
  105. package/dist/readonly-gate.js +1 -29
  106. package/dist/rehydration.js +9 -157
  107. package/dist/reindex.js +1 -88
  108. package/dist/render-target.js +21 -514
  109. package/dist/render.js +4 -280
  110. package/dist/repl-guard.js +1 -173
  111. package/dist/replication-daemon-entrypoint.js +1 -31
  112. package/dist/replication-daemon.js +2 -262
  113. package/dist/resilience.js +1 -591
  114. package/dist/reverse-bridge.js +5 -360
  115. package/dist/security.js +1 -244
  116. package/dist/session-seen.js +3 -51
  117. package/dist/setup.js +1 -260
  118. package/dist/skill-author.js +5 -168
  119. package/dist/spec-kit.js +1 -191
  120. package/dist/sqlite-busy.js +1 -154
  121. package/dist/statusline.js +11 -315
  122. package/dist/sub-agent.js +13 -262
  123. package/dist/summarizer.js +13 -139
  124. package/dist/symbols.js +7 -283
  125. package/dist/sync.js +5 -359
  126. package/dist/tasks-dispatch.js +1 -84
  127. package/dist/tasks.js +1 -282
  128. package/dist/token-budget.js +1 -143
  129. package/dist/tool-analytics.js +7 -129
  130. package/dist/tool-annotations.js +1 -365
  131. package/dist/tool-manifest-v2.json +1 -1
  132. package/dist/tool-manifest.json +1 -1
  133. package/dist/tool-profiles.js +1 -75
  134. package/dist/trace-harvest.js +6 -244
  135. package/dist/types.js +1 -30
  136. package/dist/ui-dashboard.js +41 -50
  137. package/dist/ulid.js +1 -81
  138. package/dist/validate.js +1 -129
  139. package/dist/vault.js +1 -534
  140. package/dist/vectors.js +3 -184
  141. package/dist/version-check.js +4 -136
  142. package/dist/visibility.js +19 -155
  143. package/dist/wyrm-cli.js +98 -2464
  144. package/dist/wyrm-guard.js +14 -424
  145. package/dist/wyrm-loop.js +3 -150
  146. package/dist/wyrm-manifest.json +1 -1
  147. package/dist/wyrm-statusline-daemon.js +1 -11
  148. package/dist/wyrm-statusline.js +4 -56
  149. package/dist/wyrm-ui.js +9 -77
  150. 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};