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
package/dist/analytics.js CHANGED
@@ -1,43 +1,7 @@
1
- /**
2
- * Wyrm Analytics — Persistent Usage Tracking & Cost Monitoring
3
- *
4
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
5
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
6
- * @module analytics
7
- * @version 3.2.0
8
- */
9
- // ==================== CONSTANTS ====================
10
- /** Claude Sonnet pricing per 1M tokens */
11
- const COST_PER_MILLION_INPUT = 3;
12
- const COST_PER_MILLION_OUTPUT = 15;
13
- const BUFFER_FLUSH_THRESHOLD = 50;
14
- const FLUSH_INTERVAL_MS = 30_000;
15
- const DEFAULT_RETAIN_DAYS = 90;
16
- // ==================== ANALYTICS CLASS ====================
17
- export class WyrmAnalytics {
18
- db;
19
- insertStmt;
20
- batchBuffer;
21
- flushInterval;
22
- constructor(db) {
23
- this.db = db;
24
- this.batchBuffer = [];
25
- this.initTables();
26
- this.insertStmt = this.db.prepare(`
1
+ const S=3,p=15,h=50,A=3e4,N=90;class k{db;insertStmt;batchBuffer;flushInterval;constructor(_){this.db=_,this.batchBuffer=[],this.initTables(),this.insertStmt=this.db.prepare(`
27
2
  INSERT INTO usage_events (tool_name, tokens_in, tokens_out, cached, response_ms, success, error_message, timestamp)
28
3
  VALUES (@tool_name, @tokens_in, @tokens_out, @cached, @response_ms, @success, @error_message, @timestamp)
29
- `);
30
- this.flushInterval = setInterval(() => {
31
- this.flush();
32
- }, FLUSH_INTERVAL_MS);
33
- // Don't let the timer keep the process alive
34
- if (this.flushInterval.unref) {
35
- this.flushInterval.unref();
36
- }
37
- }
38
- // ==================== SCHEMA ====================
39
- initTables() {
40
- this.db.exec(`
4
+ `),this.flushInterval=setInterval(()=>{this.flush()},3e4),this.flushInterval.unref&&this.flushInterval.unref()}initTables(){this.db.exec(`
41
5
  CREATE TABLE IF NOT EXISTS usage_events (
42
6
  id INTEGER PRIMARY KEY AUTOINCREMENT,
43
7
  tool_name TEXT NOT NULL,
@@ -68,59 +32,7 @@ export class WyrmAnalytics {
68
32
  CREATE INDEX IF NOT EXISTS idx_usage_events_tool ON usage_events(tool_name);
69
33
  CREATE INDEX IF NOT EXISTS idx_usage_events_ts ON usage_events(timestamp);
70
34
  CREATE INDEX IF NOT EXISTS idx_cost_tracking_period ON cost_tracking(period);
71
- `);
72
- }
73
- // ==================== RECORDING ====================
74
- /** Record a single tool usage event */
75
- record(event) {
76
- this.batchBuffer.push(event);
77
- if (this.batchBuffer.length >= BUFFER_FLUSH_THRESHOLD) {
78
- this.flush();
79
- }
80
- }
81
- /** Flush buffered events to disk */
82
- flush() {
83
- if (this.batchBuffer.length === 0)
84
- return;
85
- const events = this.batchBuffer.splice(0);
86
- const insertMany = this.db.transaction((batch) => {
87
- for (const event of batch) {
88
- this.insertStmt.run({
89
- tool_name: event.tool,
90
- tokens_in: event.tokens_in,
91
- tokens_out: event.tokens_out,
92
- cached: event.cached ? 1 : 0,
93
- response_ms: event.ms,
94
- success: event.success !== false ? 1 : 0,
95
- error_message: event.error ?? null,
96
- timestamp: event.timestamp,
97
- });
98
- }
99
- });
100
- insertMany(events);
101
- this.updateCostTracking(events);
102
- }
103
- /** Update cost_tracking aggregates from a batch of events */
104
- updateCostTracking(events) {
105
- const groups = new Map();
106
- for (const event of events) {
107
- const period = event.timestamp.slice(0, 7); // YYYY-MM
108
- const key = `${period}:${event.tool}`;
109
- let group = groups.get(key);
110
- if (!group) {
111
- group = { tool: event.tool, period, calls: 0, tokens_in: 0, tokens_out: 0, cached: 0, total_ms: 0, errors: 0 };
112
- groups.set(key, group);
113
- }
114
- group.calls++;
115
- group.tokens_in += event.tokens_in;
116
- group.tokens_out += event.tokens_out;
117
- if (event.cached)
118
- group.cached++;
119
- group.total_ms += event.ms;
120
- if (event.success === false)
121
- group.errors++;
122
- }
123
- const upsertStmt = this.db.prepare(`
35
+ `)}record(_){this.batchBuffer.push(_),this.batchBuffer.length>=50&&this.flush()}flush(){if(this.batchBuffer.length===0)return;const _=this.batchBuffer.splice(0);this.db.transaction(n=>{for(const o of n)this.insertStmt.run({tool_name:o.tool,tokens_in:o.tokens_in,tokens_out:o.tokens_out,cached:o.cached?1:0,response_ms:o.ms,success:o.success!==!1?1:0,error_message:o.error??null,timestamp:o.timestamp})})(_),this.updateCostTracking(_)}updateCostTracking(_){const s=new Map;for(const t of _){const e=t.timestamp.slice(0,7),r=`${e}:${t.tool}`;let a=s.get(r);a||(a={tool:t.tool,period:e,calls:0,tokens_in:0,tokens_out:0,cached:0,total_ms:0,errors:0},s.set(r,a)),a.calls++,a.tokens_in+=t.tokens_in,a.tokens_out+=t.tokens_out,t.cached&&a.cached++,a.total_ms+=t.ms,t.success===!1&&a.errors++}const n=this.db.prepare(`
124
36
  INSERT INTO cost_tracking (period, tool_name, call_count, total_tokens_in, total_tokens_out, cached_count, avg_response_ms, error_count, estimated_cost_usd, updated_at)
125
37
  VALUES (@period, @tool_name, @call_count, @total_tokens_in, @total_tokens_out, @cached_count, @avg_response_ms, @error_count, @estimated_cost_usd, datetime('now'))
126
38
  ON CONFLICT(period, tool_name) DO UPDATE SET
@@ -132,35 +44,7 @@ export class WyrmAnalytics {
132
44
  error_count = error_count + @error_count,
133
45
  estimated_cost_usd = estimated_cost_usd + @estimated_cost_usd,
134
46
  updated_at = datetime('now')
135
- `);
136
- const upsertMany = this.db.transaction((entries) => {
137
- for (const group of entries.values()) {
138
- const cost = this.estimateCost(group.tokens_in, group.tokens_out);
139
- upsertStmt.run({
140
- period: group.period,
141
- tool_name: group.tool,
142
- call_count: group.calls,
143
- total_tokens_in: group.tokens_in,
144
- total_tokens_out: group.tokens_out,
145
- cached_count: group.cached,
146
- avg_response_ms: group.calls > 0 ? group.total_ms / group.calls : 0,
147
- error_count: group.errors,
148
- estimated_cost_usd: cost,
149
- });
150
- }
151
- });
152
- upsertMany(groups);
153
- }
154
- // ==================== QUERIES ====================
155
- /** Get dashboard summary for a time period */
156
- dashboard(days = 30) {
157
- // Ensure buffered data is included
158
- this.flush();
159
- const start = new Date();
160
- start.setDate(start.getDate() - days);
161
- const startStr = start.toISOString().slice(0, 10);
162
- const endStr = new Date().toISOString().slice(0, 10);
163
- const summary = this.db.prepare(`
47
+ `);this.db.transaction(t=>{for(const e of t.values()){const r=this.estimateCost(e.tokens_in,e.tokens_out);n.run({period:e.period,tool_name:e.tool,call_count:e.calls,total_tokens_in:e.tokens_in,total_tokens_out:e.tokens_out,cached_count:e.cached,avg_response_ms:e.calls>0?e.total_ms/e.calls:0,error_count:e.errors,estimated_cost_usd:r})}})(s)}dashboard(_=30){this.flush();const s=new Date;s.setDate(s.getDate()-_);const n=s.toISOString().slice(0,10),o=new Date().toISOString().slice(0,10),t=this.db.prepare(`
164
48
  SELECT
165
49
  COUNT(*) AS total_calls,
166
50
  COUNT(DISTINCT tool_name) AS unique_tools,
@@ -177,8 +61,7 @@ export class WyrmAnalytics {
177
61
  END AS error_rate
178
62
  FROM usage_events
179
63
  WHERE timestamp >= @start
180
- `).get({ start: startStr });
181
- const top_tools = this.db.prepare(`
64
+ `).get({start:n}),e=this.db.prepare(`
182
65
  SELECT
183
66
  tool_name AS tool,
184
67
  COUNT(*) AS calls,
@@ -188,8 +71,7 @@ export class WyrmAnalytics {
188
71
  GROUP BY tool_name
189
72
  ORDER BY calls DESC
190
73
  LIMIT 10
191
- `).all({ start: startStr });
192
- const daily = this.db.prepare(`
74
+ `).all({start:n}),r=this.db.prepare(`
193
75
  SELECT
194
76
  DATE(timestamp) AS date,
195
77
  COUNT(*) AS calls,
@@ -199,31 +81,7 @@ export class WyrmAnalytics {
199
81
  WHERE timestamp >= @start
200
82
  GROUP BY DATE(timestamp)
201
83
  ORDER BY date ASC
202
- `).all({ start: startStr });
203
- const estimated_cost_usd = this.estimateCost(summary.total_tokens_in, summary.total_tokens_out);
204
- return {
205
- period: { start: startStr, end: endStr },
206
- summary: {
207
- total_calls: summary.total_calls,
208
- unique_tools: summary.unique_tools,
209
- total_tokens_in: summary.total_tokens_in,
210
- total_tokens_out: summary.total_tokens_out,
211
- cache_hit_rate: summary.cache_hit_rate,
212
- avg_response_ms: summary.avg_response_ms,
213
- error_rate: summary.error_rate,
214
- estimated_cost_usd,
215
- },
216
- top_tools,
217
- daily,
218
- };
219
- }
220
- /** Get per-tool breakdown */
221
- toolBreakdown(toolName, days = 30) {
222
- this.flush();
223
- const start = new Date();
224
- start.setDate(start.getDate() - days);
225
- const startStr = start.toISOString().slice(0, 10);
226
- const stats = this.db.prepare(`
84
+ `).all({start:n}),a=this.estimateCost(t.total_tokens_in,t.total_tokens_out);return{period:{start:n,end:o},summary:{total_calls:t.total_calls,unique_tools:t.unique_tools,total_tokens_in:t.total_tokens_in,total_tokens_out:t.total_tokens_out,cache_hit_rate:t.cache_hit_rate,avg_response_ms:t.avg_response_ms,error_rate:t.error_rate,estimated_cost_usd:a},top_tools:e,daily:r}}toolBreakdown(_,s=30){this.flush();const n=new Date;n.setDate(n.getDate()-s);const o=n.toISOString().slice(0,10),t=this.db.prepare(`
227
85
  SELECT
228
86
  COUNT(*) AS total_calls,
229
87
  COALESCE(ROUND(AVG(tokens_in), 2), 0) AS avg_tokens_in,
@@ -239,8 +97,7 @@ export class WyrmAnalytics {
239
97
  END AS error_rate
240
98
  FROM usage_events
241
99
  WHERE tool_name = @tool AND timestamp >= @start
242
- `).get({ tool: toolName, start: startStr });
243
- const daily = this.db.prepare(`
100
+ `).get({tool:_,start:o}),e=this.db.prepare(`
244
101
  SELECT
245
102
  DATE(timestamp) AS date,
246
103
  COUNT(*) AS calls
@@ -248,23 +105,7 @@ export class WyrmAnalytics {
248
105
  WHERE tool_name = @tool AND timestamp >= @start
249
106
  GROUP BY DATE(timestamp)
250
107
  ORDER BY date ASC
251
- `).all({ tool: toolName, start: startStr });
252
- return {
253
- tool: toolName,
254
- total_calls: stats.total_calls,
255
- avg_tokens_in: stats.avg_tokens_in,
256
- avg_tokens_out: stats.avg_tokens_out,
257
- avg_response_ms: stats.avg_response_ms,
258
- cache_hit_rate: stats.cache_hit_rate,
259
- error_rate: stats.error_rate,
260
- daily,
261
- };
262
- }
263
- /** Get cost estimate for a period */
264
- costReport(period) {
265
- this.flush();
266
- const targetPeriod = period ?? new Date().toISOString().slice(0, 7);
267
- const tools = this.db.prepare(`
108
+ `).all({tool:_,start:o});return{tool:_,total_calls:t.total_calls,avg_tokens_in:t.avg_tokens_in,avg_tokens_out:t.avg_tokens_out,avg_response_ms:t.avg_response_ms,cache_hit_rate:t.cache_hit_rate,error_rate:t.error_rate,daily:e}}costReport(_){this.flush();const s=_??new Date().toISOString().slice(0,7),n=this.db.prepare(`
268
109
  SELECT
269
110
  tool_name AS tool,
270
111
  call_count AS calls,
@@ -274,35 +115,7 @@ export class WyrmAnalytics {
274
115
  FROM cost_tracking
275
116
  WHERE period = @period
276
117
  ORDER BY estimated_cost_usd DESC
277
- `).all({ period: targetPeriod });
278
- const total_cost_usd = tools.reduce((sum, t) => sum + t.cost_usd, 0);
279
- // Project monthly cost based on days elapsed in the period
280
- const now = new Date();
281
- const periodYear = parseInt(targetPeriod.slice(0, 4), 10);
282
- const periodMonth = parseInt(targetPeriod.slice(5, 7), 10) - 1;
283
- const periodStart = new Date(periodYear, periodMonth, 1);
284
- const periodEnd = new Date(periodYear, periodMonth + 1, 0); // last day of month
285
- const totalDaysInMonth = periodEnd.getDate();
286
- const elapsed = Math.max(1, Math.ceil((now.getTime() - periodStart.getTime()) / (1000 * 60 * 60 * 24)));
287
- const daysToProject = Math.min(elapsed, totalDaysInMonth);
288
- const projected_monthly_usd = totalDaysInMonth > 0
289
- ? Math.round(((total_cost_usd / daysToProject) * totalDaysInMonth) * 100) / 100
290
- : total_cost_usd;
291
- return {
292
- period: targetPeriod,
293
- tools,
294
- total_cost_usd: Math.round(total_cost_usd * 100) / 100,
295
- projected_monthly_usd,
296
- };
297
- }
298
- /** Clean up old events (retention policy) */
299
- cleanup(retainDays = DEFAULT_RETAIN_DAYS) {
300
- this.flush();
301
- const cutoff = new Date();
302
- cutoff.setDate(cutoff.getDate() - retainDays);
303
- const cutoffStr = cutoff.toISOString().slice(0, 10);
304
- // Aggregate old events into cost_tracking before deleting
305
- const oldEvents = this.db.prepare(`
118
+ `).all({period:s}),o=n.reduce((d,T)=>d+T.cost_usd,0),t=new Date,e=parseInt(s.slice(0,4),10),r=parseInt(s.slice(5,7),10)-1,a=new Date(e,r,1),l=new Date(e,r+1,0).getDate(),u=Math.max(1,Math.ceil((t.getTime()-a.getTime())/(1e3*60*60*24))),E=Math.min(u,l),i=l>0?Math.round(o/E*l*100)/100:o;return{period:s,tools:n,total_cost_usd:Math.round(o*100)/100,projected_monthly_usd:i}}cleanup(_=90){this.flush();const s=new Date;s.setDate(s.getDate()-_);const n=s.toISOString().slice(0,10),o=this.db.prepare(`
306
119
  SELECT
307
120
  STRFTIME('%Y-%m', timestamp) AS period,
308
121
  tool_name,
@@ -315,8 +128,7 @@ export class WyrmAnalytics {
315
128
  FROM usage_events
316
129
  WHERE timestamp < @cutoff
317
130
  GROUP BY STRFTIME('%Y-%m', timestamp), tool_name
318
- `).all({ cutoff: cutoffStr });
319
- const upsertStmt = this.db.prepare(`
131
+ `).all({cutoff:n}),t=this.db.prepare(`
320
132
  INSERT INTO cost_tracking (period, tool_name, call_count, total_tokens_in, total_tokens_out, cached_count, avg_response_ms, error_count, estimated_cost_usd, updated_at)
321
133
  VALUES (@period, @tool_name, @call_count, @total_tokens_in, @total_tokens_out, @cached_count, @avg_response_ms, @error_count, @estimated_cost_usd, datetime('now'))
322
134
  ON CONFLICT(period, tool_name) DO UPDATE SET
@@ -328,41 +140,6 @@ export class WyrmAnalytics {
328
140
  error_count = error_count + @error_count,
329
141
  estimated_cost_usd = estimated_cost_usd + @estimated_cost_usd,
330
142
  updated_at = datetime('now')
331
- `);
332
- const cleanupTxn = this.db.transaction(() => {
333
- for (const row of oldEvents) {
334
- const cost = this.estimateCost(row.total_tokens_in, row.total_tokens_out);
335
- upsertStmt.run({
336
- period: row.period,
337
- tool_name: row.tool_name,
338
- call_count: row.call_count,
339
- total_tokens_in: row.total_tokens_in,
340
- total_tokens_out: row.total_tokens_out,
341
- cached_count: row.cached_count,
342
- avg_response_ms: row.avg_response_ms,
343
- error_count: row.error_count,
344
- estimated_cost_usd: cost,
345
- });
346
- }
347
- const result = this.db.prepare(`
143
+ `);return{deleted:this.db.transaction(()=>{for(const c of o){const l=this.estimateCost(c.total_tokens_in,c.total_tokens_out);t.run({period:c.period,tool_name:c.tool_name,call_count:c.call_count,total_tokens_in:c.total_tokens_in,total_tokens_out:c.total_tokens_out,cached_count:c.cached_count,avg_response_ms:c.avg_response_ms,error_count:c.error_count,estimated_cost_usd:l})}return this.db.prepare(`
348
144
  DELETE FROM usage_events WHERE timestamp < @cutoff
349
- `).run({ cutoff: cutoffStr });
350
- return result.changes;
351
- });
352
- const deleted = cleanupTxn();
353
- return { deleted };
354
- }
355
- /** Shutdown: flush remaining buffer and clear timer */
356
- shutdown() {
357
- clearInterval(this.flushInterval);
358
- this.flush();
359
- }
360
- // ==================== HELPERS ====================
361
- /** Estimate USD cost from token counts (Claude Sonnet rates) */
362
- estimateCost(tokensIn, tokensOut) {
363
- const inputCost = (tokensIn / 1_000_000) * COST_PER_MILLION_INPUT;
364
- const outputCost = (tokensOut / 1_000_000) * COST_PER_MILLION_OUTPUT;
365
- return Math.round((inputCost + outputCost) * 1_000_000) / 1_000_000;
366
- }
367
- }
368
- //# sourceMappingURL=analytics.js.map
145
+ `).run({cutoff:n}).changes})()}}shutdown(){clearInterval(this.flushInterval),this.flush()}estimateCost(_,s){const n=_/1e6*3,o=s/1e6*15;return Math.round((n+o)*1e6)/1e6}}export{k as WyrmAnalytics};
@@ -1,49 +1 @@
1
- /**
2
- * Wyrm attribution read helpers (v7 F2, T008).
3
- *
4
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
5
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
6
- *
7
- * Migration 20 adds nullable `agent_id`/`run_id` attribution columns to 9
8
- * tables. Every row written before v7 — and any v7 row written outside a fleet
9
- * run — carries NULL attribution. Article VI: a v6.x DB must open under v7
10
- * with 100% of its rows, and that history must stay legibly attributed, so at
11
- * every READ SITE that surfaces attribution, NULL reads as actor='legacy'.
12
- *
13
- * Precedence when rendering "who did this" (the same order is documented in
14
- * migration 20 next to the events.agent_id column):
15
- * 1. agent_id — machine identity (v7 fleet writes)
16
- * 2. actor — display identity (the existing 6.x column on
17
- * events/audit_log; it is EXTENDED, never replaced)
18
- * 3. LEGACY_ACTOR — 'legacy' (neither set: a pre-v7 row)
19
- *
20
- * Wire paths (eventsForPush, ingestRemoteEvent, replication) intentionally do
21
- * NOT coalesce: NULL stays NULL in storage and on the wire so replication
22
- * round-trips are byte-faithful. 'legacy' is a READ-TIME presentation value
23
- * only — it is never written back to the database.
24
- */
25
- /** What NULL attribution reads as at attribution-surfacing read sites. */
26
- export const LEGACY_ACTOR = 'legacy';
27
- /**
28
- * Read a display actor from a nullable actor column.
29
- * NULL/undefined/blank -> 'legacy'; anything else passes through trimmed.
30
- */
31
- export function readActor(actor) {
32
- const a = typeof actor === 'string' ? actor.trim() : '';
33
- return a !== '' ? a : LEGACY_ACTOR;
34
- }
35
- /**
36
- * Resolve the full attribution for one row using the documented precedence.
37
- * Use this at any read site that surfaces who/which-run produced a row
38
- * (failure_check verdicts, wyrm_stats view=failures, event displays).
39
- */
40
- export function resolveAttribution(row) {
41
- const agentId = row.agent_id ?? null;
42
- const runId = row.run_id ?? null;
43
- return {
44
- actor: agentId ?? readActor(row.actor),
45
- agent_id: agentId,
46
- run_id: runId,
47
- };
48
- }
49
- //# sourceMappingURL=attribution.js.map
1
+ const e="legacy";function o(t){const n=typeof t=="string"?t.trim():"";return n!==""?n:e}function i(t){const n=t.agent_id??null,r=t.run_id??null;return{actor:n??o(t.actor),agent_id:n,run_id:r}}export{e as LEGACY_ACTOR,o as readActor,i as resolveAttribution};