@a4hgehad/weave-mcp 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +262 -237
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -98,11 +98,12 @@ var BuiltInMcpSchema = z.object({
|
|
|
98
98
|
grep_app: z.boolean().optional()
|
|
99
99
|
});
|
|
100
100
|
var CustomMcpServerSchema = z.object({
|
|
101
|
-
type: z.enum(["
|
|
102
|
-
command: z.string().optional(),
|
|
101
|
+
type: z.enum(["local", "remote"]).optional(),
|
|
102
|
+
command: z.array(z.string()).optional(),
|
|
103
103
|
args: z.array(z.string()).optional(),
|
|
104
104
|
url: z.string().optional(),
|
|
105
|
-
env: z.record(z.string(), z.string()).optional()
|
|
105
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
106
|
+
enabled: z.boolean().optional()
|
|
106
107
|
});
|
|
107
108
|
var McpConfigSchema = z.object({
|
|
108
109
|
enabled: BuiltInMcpSchema.optional(),
|
|
@@ -241,19 +242,16 @@ function loadWeaveConfig(directory, _ctx, _homeDir) {
|
|
|
241
242
|
// src/mcp/types.ts
|
|
242
243
|
var BUILTIN_MCP_SERVERS = {
|
|
243
244
|
websearch: {
|
|
244
|
-
type: "
|
|
245
|
-
command: "npx",
|
|
246
|
-
args: ["-y", "@modelcontextprotocol/server-websearch"]
|
|
245
|
+
type: "local",
|
|
246
|
+
command: ["npx", "-y", "@modelcontextprotocol/server-websearch"]
|
|
247
247
|
},
|
|
248
248
|
context7: {
|
|
249
|
-
type: "
|
|
250
|
-
command: "npx",
|
|
251
|
-
args: ["-y", "context7-mcp-server"]
|
|
249
|
+
type: "local",
|
|
250
|
+
command: ["npx", "-y", "context7-mcp-server"]
|
|
252
251
|
},
|
|
253
252
|
grep_app: {
|
|
254
|
-
type: "
|
|
255
|
-
command: "npx",
|
|
256
|
-
args: ["-y", "grep-app-mcp"]
|
|
253
|
+
type: "local",
|
|
254
|
+
command: ["npx", "-y", "grep-app-mcp"]
|
|
257
255
|
}
|
|
258
256
|
};
|
|
259
257
|
var DEFAULT_MCPS = ["websearch", "context7", "grep_app"];
|
|
@@ -4634,221 +4632,6 @@ function getState(sessionId) {
|
|
|
4634
4632
|
function clearSession2(sessionId) {
|
|
4635
4633
|
sessionMap.delete(sessionId);
|
|
4636
4634
|
}
|
|
4637
|
-
// src/features/analytics/storage.ts
|
|
4638
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync5, appendFileSync as appendFileSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync4, statSync as statSync3 } from "fs";
|
|
4639
|
-
import { join as join11 } from "path";
|
|
4640
|
-
|
|
4641
|
-
// src/features/analytics/types.ts
|
|
4642
|
-
var ANALYTICS_DIR = ".weave/analytics";
|
|
4643
|
-
var SESSION_SUMMARIES_FILE = "session-summaries.jsonl";
|
|
4644
|
-
var FINGERPRINT_FILE = "fingerprint.json";
|
|
4645
|
-
var METRICS_REPORTS_FILE = "metrics-reports.jsonl";
|
|
4646
|
-
var MAX_METRICS_ENTRIES = 100;
|
|
4647
|
-
function zeroTokenUsage() {
|
|
4648
|
-
return { input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite: 0 };
|
|
4649
|
-
}
|
|
4650
|
-
|
|
4651
|
-
// src/features/analytics/storage.ts
|
|
4652
|
-
var MAX_SESSION_ENTRIES = 1000;
|
|
4653
|
-
function ensureAnalyticsDir(directory) {
|
|
4654
|
-
const dir = join11(directory, ANALYTICS_DIR);
|
|
4655
|
-
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
4656
|
-
return dir;
|
|
4657
|
-
}
|
|
4658
|
-
function appendSessionSummary(directory, summary) {
|
|
4659
|
-
try {
|
|
4660
|
-
const dir = ensureAnalyticsDir(directory);
|
|
4661
|
-
const filePath = join11(dir, SESSION_SUMMARIES_FILE);
|
|
4662
|
-
const line = JSON.stringify(summary) + `
|
|
4663
|
-
`;
|
|
4664
|
-
appendFileSync2(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
4665
|
-
try {
|
|
4666
|
-
const TYPICAL_ENTRY_BYTES = 200;
|
|
4667
|
-
const rotationSizeThreshold = MAX_SESSION_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
|
|
4668
|
-
const { size } = statSync3(filePath);
|
|
4669
|
-
if (size > rotationSizeThreshold) {
|
|
4670
|
-
const content = readFileSync10(filePath, "utf-8");
|
|
4671
|
-
const lines = content.split(`
|
|
4672
|
-
`).filter((l) => l.trim().length > 0);
|
|
4673
|
-
if (lines.length > MAX_SESSION_ENTRIES) {
|
|
4674
|
-
const trimmed = lines.slice(-MAX_SESSION_ENTRIES).join(`
|
|
4675
|
-
`) + `
|
|
4676
|
-
`;
|
|
4677
|
-
writeFileSync4(filePath, trimmed, { encoding: "utf-8", mode: 384 });
|
|
4678
|
-
}
|
|
4679
|
-
}
|
|
4680
|
-
} catch {}
|
|
4681
|
-
return true;
|
|
4682
|
-
} catch {
|
|
4683
|
-
return false;
|
|
4684
|
-
}
|
|
4685
|
-
}
|
|
4686
|
-
function readSessionSummaries(directory) {
|
|
4687
|
-
const filePath = join11(directory, ANALYTICS_DIR, SESSION_SUMMARIES_FILE);
|
|
4688
|
-
try {
|
|
4689
|
-
if (!existsSync12(filePath))
|
|
4690
|
-
return [];
|
|
4691
|
-
const content = readFileSync10(filePath, "utf-8");
|
|
4692
|
-
const lines = content.split(`
|
|
4693
|
-
`).filter((line) => line.trim().length > 0);
|
|
4694
|
-
const summaries = [];
|
|
4695
|
-
for (const line of lines) {
|
|
4696
|
-
try {
|
|
4697
|
-
summaries.push(JSON.parse(line));
|
|
4698
|
-
} catch {}
|
|
4699
|
-
}
|
|
4700
|
-
return summaries;
|
|
4701
|
-
} catch {
|
|
4702
|
-
return [];
|
|
4703
|
-
}
|
|
4704
|
-
}
|
|
4705
|
-
function writeFingerprint(directory, fingerprint) {
|
|
4706
|
-
try {
|
|
4707
|
-
const dir = ensureAnalyticsDir(directory);
|
|
4708
|
-
const filePath = join11(dir, FINGERPRINT_FILE);
|
|
4709
|
-
writeFileSync4(filePath, JSON.stringify(fingerprint, null, 2), { encoding: "utf-8", mode: 384 });
|
|
4710
|
-
return true;
|
|
4711
|
-
} catch {
|
|
4712
|
-
return false;
|
|
4713
|
-
}
|
|
4714
|
-
}
|
|
4715
|
-
function readFingerprint(directory) {
|
|
4716
|
-
const filePath = join11(directory, ANALYTICS_DIR, FINGERPRINT_FILE);
|
|
4717
|
-
try {
|
|
4718
|
-
if (!existsSync12(filePath))
|
|
4719
|
-
return null;
|
|
4720
|
-
const content = readFileSync10(filePath, "utf-8");
|
|
4721
|
-
const parsed = JSON.parse(content);
|
|
4722
|
-
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.stack))
|
|
4723
|
-
return null;
|
|
4724
|
-
return parsed;
|
|
4725
|
-
} catch {
|
|
4726
|
-
return null;
|
|
4727
|
-
}
|
|
4728
|
-
}
|
|
4729
|
-
function writeMetricsReport(directory, report) {
|
|
4730
|
-
try {
|
|
4731
|
-
const dir = ensureAnalyticsDir(directory);
|
|
4732
|
-
const filePath = join11(dir, METRICS_REPORTS_FILE);
|
|
4733
|
-
const line = JSON.stringify(report) + `
|
|
4734
|
-
`;
|
|
4735
|
-
appendFileSync2(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
4736
|
-
try {
|
|
4737
|
-
const TYPICAL_ENTRY_BYTES = 200;
|
|
4738
|
-
const rotationSizeThreshold = MAX_METRICS_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
|
|
4739
|
-
const { size } = statSync3(filePath);
|
|
4740
|
-
if (size > rotationSizeThreshold) {
|
|
4741
|
-
const content = readFileSync10(filePath, "utf-8");
|
|
4742
|
-
const lines = content.split(`
|
|
4743
|
-
`).filter((l) => l.trim().length > 0);
|
|
4744
|
-
if (lines.length > MAX_METRICS_ENTRIES) {
|
|
4745
|
-
const trimmed = lines.slice(-MAX_METRICS_ENTRIES).join(`
|
|
4746
|
-
`) + `
|
|
4747
|
-
`;
|
|
4748
|
-
writeFileSync4(filePath, trimmed, { encoding: "utf-8", mode: 384 });
|
|
4749
|
-
}
|
|
4750
|
-
}
|
|
4751
|
-
} catch {}
|
|
4752
|
-
return true;
|
|
4753
|
-
} catch {
|
|
4754
|
-
return false;
|
|
4755
|
-
}
|
|
4756
|
-
}
|
|
4757
|
-
function readMetricsReports(directory) {
|
|
4758
|
-
const filePath = join11(directory, ANALYTICS_DIR, METRICS_REPORTS_FILE);
|
|
4759
|
-
try {
|
|
4760
|
-
if (!existsSync12(filePath))
|
|
4761
|
-
return [];
|
|
4762
|
-
const content = readFileSync10(filePath, "utf-8");
|
|
4763
|
-
const lines = content.split(`
|
|
4764
|
-
`).filter((line) => line.trim().length > 0);
|
|
4765
|
-
const reports = [];
|
|
4766
|
-
for (const line of lines) {
|
|
4767
|
-
try {
|
|
4768
|
-
reports.push(JSON.parse(line));
|
|
4769
|
-
} catch {}
|
|
4770
|
-
}
|
|
4771
|
-
return reports;
|
|
4772
|
-
} catch {
|
|
4773
|
-
return [];
|
|
4774
|
-
}
|
|
4775
|
-
}
|
|
4776
|
-
|
|
4777
|
-
// src/features/analytics/token-report.ts
|
|
4778
|
-
function generateTokenReport(summaries) {
|
|
4779
|
-
if (summaries.length === 0) {
|
|
4780
|
-
return "No session data available.";
|
|
4781
|
-
}
|
|
4782
|
-
const sections = [];
|
|
4783
|
-
const totalSessions = summaries.length;
|
|
4784
|
-
const totalMessages = summaries.reduce((sum, s) => sum + (s.tokenUsage?.totalMessages ?? 0), 0);
|
|
4785
|
-
const totalInput = summaries.reduce((sum, s) => sum + (s.tokenUsage?.inputTokens ?? 0), 0);
|
|
4786
|
-
const totalOutput = summaries.reduce((sum, s) => sum + (s.tokenUsage?.outputTokens ?? 0), 0);
|
|
4787
|
-
const totalReasoning = summaries.reduce((sum, s) => sum + (s.tokenUsage?.reasoningTokens ?? 0), 0);
|
|
4788
|
-
const totalCacheRead = summaries.reduce((sum, s) => sum + (s.tokenUsage?.cacheReadTokens ?? 0), 0);
|
|
4789
|
-
const totalCacheWrite = summaries.reduce((sum, s) => sum + (s.tokenUsage?.cacheWriteTokens ?? 0), 0);
|
|
4790
|
-
const totalCost = summaries.reduce((sum, s) => sum + (s.totalCost ?? 0), 0);
|
|
4791
|
-
sections.push(`## Overall Totals
|
|
4792
|
-
` + `- Sessions: ${fmt(totalSessions)}
|
|
4793
|
-
` + `- Messages: ${fmt(totalMessages)}
|
|
4794
|
-
` + `- Input tokens: ${fmt(totalInput)}
|
|
4795
|
-
` + `- Output tokens: ${fmt(totalOutput)}
|
|
4796
|
-
` + `- Reasoning tokens: ${fmt(totalReasoning)}
|
|
4797
|
-
` + `- Cache read tokens: ${fmt(totalCacheRead)}
|
|
4798
|
-
` + `- Cache write tokens: ${fmt(totalCacheWrite)}
|
|
4799
|
-
` + `- Total cost: ${fmtCost(totalCost)}`);
|
|
4800
|
-
const agentGroups = new Map;
|
|
4801
|
-
for (const s of summaries) {
|
|
4802
|
-
const key = s.agentName ?? "(unknown)";
|
|
4803
|
-
const group = agentGroups.get(key);
|
|
4804
|
-
if (group) {
|
|
4805
|
-
group.push(s);
|
|
4806
|
-
} else {
|
|
4807
|
-
agentGroups.set(key, [s]);
|
|
4808
|
-
}
|
|
4809
|
-
}
|
|
4810
|
-
const agentStats = Array.from(agentGroups.entries()).map(([agent, sessions]) => {
|
|
4811
|
-
const agentCost = sessions.reduce((sum, s) => sum + (s.totalCost ?? 0), 0);
|
|
4812
|
-
const agentTokens = sessions.reduce((sum, s) => sum + (s.tokenUsage?.inputTokens ?? 0) + (s.tokenUsage?.outputTokens ?? 0) + (s.tokenUsage?.reasoningTokens ?? 0), 0);
|
|
4813
|
-
const avgTokens = sessions.length > 0 ? Math.round(agentTokens / sessions.length) : 0;
|
|
4814
|
-
const avgCost = sessions.length > 0 ? agentCost / sessions.length : 0;
|
|
4815
|
-
return { agent, sessions: sessions.length, avgTokens, avgCost, totalCost: agentCost };
|
|
4816
|
-
}).sort((a, b) => b.totalCost - a.totalCost);
|
|
4817
|
-
const agentLines = agentStats.map((a) => `- **${a.agent}**: ${fmt(a.sessions)} session${a.sessions === 1 ? "" : "s"}, ` + `avg ${fmt(a.avgTokens)} tokens/session, ` + `avg ${fmtCost(a.avgCost)}/session, ` + `total ${fmtCost(a.totalCost)}`);
|
|
4818
|
-
sections.push(`## Per-Agent Breakdown
|
|
4819
|
-
${agentLines.join(`
|
|
4820
|
-
`)}`);
|
|
4821
|
-
const top5 = [...summaries].sort((a, b) => (b.totalCost ?? 0) - (a.totalCost ?? 0)).slice(0, 5);
|
|
4822
|
-
const top5Lines = top5.map((s) => {
|
|
4823
|
-
const id = s.sessionId.length > 8 ? s.sessionId.slice(0, 8) : s.sessionId;
|
|
4824
|
-
const agent = s.agentName ?? "(unknown)";
|
|
4825
|
-
const cost = fmtCost(s.totalCost ?? 0);
|
|
4826
|
-
const tokens = (s.tokenUsage?.inputTokens ?? 0) + (s.tokenUsage?.outputTokens ?? 0) + (s.tokenUsage?.reasoningTokens ?? 0);
|
|
4827
|
-
const duration = fmtDuration(s.durationMs);
|
|
4828
|
-
return `- \`${id}\` ${agent} — ${cost}, ${fmt(tokens)} tokens, ${duration}`;
|
|
4829
|
-
});
|
|
4830
|
-
sections.push(`## Top 5 Costliest Sessions
|
|
4831
|
-
${top5Lines.join(`
|
|
4832
|
-
`)}`);
|
|
4833
|
-
return sections.join(`
|
|
4834
|
-
|
|
4835
|
-
`);
|
|
4836
|
-
}
|
|
4837
|
-
function fmt(n) {
|
|
4838
|
-
return n.toLocaleString("en-US");
|
|
4839
|
-
}
|
|
4840
|
-
function fmtCost(n) {
|
|
4841
|
-
return `$${n.toFixed(2)}`;
|
|
4842
|
-
}
|
|
4843
|
-
function fmtDuration(ms) {
|
|
4844
|
-
const totalSeconds = Math.round(ms / 1000);
|
|
4845
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
4846
|
-
const seconds = totalSeconds % 60;
|
|
4847
|
-
if (minutes === 0)
|
|
4848
|
-
return `${seconds}s`;
|
|
4849
|
-
return `${minutes}m ${seconds}s`;
|
|
4850
|
-
}
|
|
4851
|
-
|
|
4852
4635
|
// src/features/analytics/format-metrics.ts
|
|
4853
4636
|
function formatNumber(n) {
|
|
4854
4637
|
return n.toLocaleString("en-US");
|
|
@@ -4985,7 +4768,7 @@ function formatMetricsMarkdown(reports, summaries, args) {
|
|
|
4985
4768
|
}
|
|
4986
4769
|
|
|
4987
4770
|
// src/features/analytics/plan-parser.ts
|
|
4988
|
-
import { readFileSync as
|
|
4771
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
4989
4772
|
function extractSection2(content, heading) {
|
|
4990
4773
|
const lines = content.split(`
|
|
4991
4774
|
`);
|
|
@@ -5020,7 +4803,7 @@ function extractFilePath2(raw) {
|
|
|
5020
4803
|
function extractPlannedFiles(planPath) {
|
|
5021
4804
|
let content;
|
|
5022
4805
|
try {
|
|
5023
|
-
content =
|
|
4806
|
+
content = readFileSync10(planPath, "utf-8");
|
|
5024
4807
|
} catch {
|
|
5025
4808
|
return [];
|
|
5026
4809
|
}
|
|
@@ -5107,6 +4890,144 @@ function calculateAdherence(plannedFiles, actualFiles) {
|
|
|
5107
4890
|
};
|
|
5108
4891
|
}
|
|
5109
4892
|
|
|
4893
|
+
// src/features/analytics/types.ts
|
|
4894
|
+
var ANALYTICS_DIR = ".weave/analytics";
|
|
4895
|
+
var SESSION_SUMMARIES_FILE = "session-summaries.jsonl";
|
|
4896
|
+
var FINGERPRINT_FILE = "fingerprint.json";
|
|
4897
|
+
var METRICS_REPORTS_FILE = "metrics-reports.jsonl";
|
|
4898
|
+
var MAX_METRICS_ENTRIES = 100;
|
|
4899
|
+
function zeroTokenUsage() {
|
|
4900
|
+
return { input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite: 0 };
|
|
4901
|
+
}
|
|
4902
|
+
|
|
4903
|
+
// src/features/analytics/storage.ts
|
|
4904
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, appendFileSync as appendFileSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync4, statSync as statSync3 } from "fs";
|
|
4905
|
+
import { join as join11 } from "path";
|
|
4906
|
+
var MAX_SESSION_ENTRIES = 1000;
|
|
4907
|
+
function ensureAnalyticsDir(directory) {
|
|
4908
|
+
const dir = join11(directory, ANALYTICS_DIR);
|
|
4909
|
+
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
4910
|
+
return dir;
|
|
4911
|
+
}
|
|
4912
|
+
function appendSessionSummary(directory, summary) {
|
|
4913
|
+
try {
|
|
4914
|
+
const dir = ensureAnalyticsDir(directory);
|
|
4915
|
+
const filePath = join11(dir, SESSION_SUMMARIES_FILE);
|
|
4916
|
+
const line = JSON.stringify(summary) + `
|
|
4917
|
+
`;
|
|
4918
|
+
appendFileSync2(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
4919
|
+
try {
|
|
4920
|
+
const TYPICAL_ENTRY_BYTES = 200;
|
|
4921
|
+
const rotationSizeThreshold = MAX_SESSION_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
|
|
4922
|
+
const { size } = statSync3(filePath);
|
|
4923
|
+
if (size > rotationSizeThreshold) {
|
|
4924
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
4925
|
+
const lines = content.split(`
|
|
4926
|
+
`).filter((l) => l.trim().length > 0);
|
|
4927
|
+
if (lines.length > MAX_SESSION_ENTRIES) {
|
|
4928
|
+
const trimmed = lines.slice(-MAX_SESSION_ENTRIES).join(`
|
|
4929
|
+
`) + `
|
|
4930
|
+
`;
|
|
4931
|
+
writeFileSync4(filePath, trimmed, { encoding: "utf-8", mode: 384 });
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4934
|
+
} catch {}
|
|
4935
|
+
return true;
|
|
4936
|
+
} catch {
|
|
4937
|
+
return false;
|
|
4938
|
+
}
|
|
4939
|
+
}
|
|
4940
|
+
function readSessionSummaries(directory) {
|
|
4941
|
+
const filePath = join11(directory, ANALYTICS_DIR, SESSION_SUMMARIES_FILE);
|
|
4942
|
+
try {
|
|
4943
|
+
if (!existsSync12(filePath))
|
|
4944
|
+
return [];
|
|
4945
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
4946
|
+
const lines = content.split(`
|
|
4947
|
+
`).filter((line) => line.trim().length > 0);
|
|
4948
|
+
const summaries = [];
|
|
4949
|
+
for (const line of lines) {
|
|
4950
|
+
try {
|
|
4951
|
+
summaries.push(JSON.parse(line));
|
|
4952
|
+
} catch {}
|
|
4953
|
+
}
|
|
4954
|
+
return summaries;
|
|
4955
|
+
} catch {
|
|
4956
|
+
return [];
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
function writeFingerprint(directory, fingerprint) {
|
|
4960
|
+
try {
|
|
4961
|
+
const dir = ensureAnalyticsDir(directory);
|
|
4962
|
+
const filePath = join11(dir, FINGERPRINT_FILE);
|
|
4963
|
+
writeFileSync4(filePath, JSON.stringify(fingerprint, null, 2), { encoding: "utf-8", mode: 384 });
|
|
4964
|
+
return true;
|
|
4965
|
+
} catch {
|
|
4966
|
+
return false;
|
|
4967
|
+
}
|
|
4968
|
+
}
|
|
4969
|
+
function readFingerprint(directory) {
|
|
4970
|
+
const filePath = join11(directory, ANALYTICS_DIR, FINGERPRINT_FILE);
|
|
4971
|
+
try {
|
|
4972
|
+
if (!existsSync12(filePath))
|
|
4973
|
+
return null;
|
|
4974
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
4975
|
+
const parsed = JSON.parse(content);
|
|
4976
|
+
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.stack))
|
|
4977
|
+
return null;
|
|
4978
|
+
return parsed;
|
|
4979
|
+
} catch {
|
|
4980
|
+
return null;
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
function writeMetricsReport(directory, report) {
|
|
4984
|
+
try {
|
|
4985
|
+
const dir = ensureAnalyticsDir(directory);
|
|
4986
|
+
const filePath = join11(dir, METRICS_REPORTS_FILE);
|
|
4987
|
+
const line = JSON.stringify(report) + `
|
|
4988
|
+
`;
|
|
4989
|
+
appendFileSync2(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
4990
|
+
try {
|
|
4991
|
+
const TYPICAL_ENTRY_BYTES = 200;
|
|
4992
|
+
const rotationSizeThreshold = MAX_METRICS_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
|
|
4993
|
+
const { size } = statSync3(filePath);
|
|
4994
|
+
if (size > rotationSizeThreshold) {
|
|
4995
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
4996
|
+
const lines = content.split(`
|
|
4997
|
+
`).filter((l) => l.trim().length > 0);
|
|
4998
|
+
if (lines.length > MAX_METRICS_ENTRIES) {
|
|
4999
|
+
const trimmed = lines.slice(-MAX_METRICS_ENTRIES).join(`
|
|
5000
|
+
`) + `
|
|
5001
|
+
`;
|
|
5002
|
+
writeFileSync4(filePath, trimmed, { encoding: "utf-8", mode: 384 });
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
} catch {}
|
|
5006
|
+
return true;
|
|
5007
|
+
} catch {
|
|
5008
|
+
return false;
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
function readMetricsReports(directory) {
|
|
5012
|
+
const filePath = join11(directory, ANALYTICS_DIR, METRICS_REPORTS_FILE);
|
|
5013
|
+
try {
|
|
5014
|
+
if (!existsSync12(filePath))
|
|
5015
|
+
return [];
|
|
5016
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
5017
|
+
const lines = content.split(`
|
|
5018
|
+
`).filter((line) => line.trim().length > 0);
|
|
5019
|
+
const reports = [];
|
|
5020
|
+
for (const line of lines) {
|
|
5021
|
+
try {
|
|
5022
|
+
reports.push(JSON.parse(line));
|
|
5023
|
+
} catch {}
|
|
5024
|
+
}
|
|
5025
|
+
return reports;
|
|
5026
|
+
} catch {
|
|
5027
|
+
return [];
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5110
5031
|
// src/features/analytics/plan-token-aggregator.ts
|
|
5111
5032
|
function aggregateTokensForPlan(directory, sessionIds) {
|
|
5112
5033
|
const summaries = readSessionSummaries(directory);
|
|
@@ -5167,10 +5088,95 @@ function generateMetricsReport(directory, state) {
|
|
|
5167
5088
|
}
|
|
5168
5089
|
}
|
|
5169
5090
|
|
|
5091
|
+
// src/features/analytics/token-report.ts
|
|
5092
|
+
function generateTokenReport(summaries) {
|
|
5093
|
+
if (summaries.length === 0) {
|
|
5094
|
+
return "No session data available.";
|
|
5095
|
+
}
|
|
5096
|
+
const sections = [];
|
|
5097
|
+
const totalSessions = summaries.length;
|
|
5098
|
+
const totalMessages = summaries.reduce((sum, s) => sum + (s.tokenUsage?.totalMessages ?? 0), 0);
|
|
5099
|
+
const totalInput = summaries.reduce((sum, s) => sum + (s.tokenUsage?.inputTokens ?? 0), 0);
|
|
5100
|
+
const totalOutput = summaries.reduce((sum, s) => sum + (s.tokenUsage?.outputTokens ?? 0), 0);
|
|
5101
|
+
const totalReasoning = summaries.reduce((sum, s) => sum + (s.tokenUsage?.reasoningTokens ?? 0), 0);
|
|
5102
|
+
const totalCacheRead = summaries.reduce((sum, s) => sum + (s.tokenUsage?.cacheReadTokens ?? 0), 0);
|
|
5103
|
+
const totalCacheWrite = summaries.reduce((sum, s) => sum + (s.tokenUsage?.cacheWriteTokens ?? 0), 0);
|
|
5104
|
+
const totalCost = summaries.reduce((sum, s) => sum + (s.totalCost ?? 0), 0);
|
|
5105
|
+
sections.push(`## Overall Totals
|
|
5106
|
+
` + `- Sessions: ${fmt(totalSessions)}
|
|
5107
|
+
` + `- Messages: ${fmt(totalMessages)}
|
|
5108
|
+
` + `- Input tokens: ${fmt(totalInput)}
|
|
5109
|
+
` + `- Output tokens: ${fmt(totalOutput)}
|
|
5110
|
+
` + `- Reasoning tokens: ${fmt(totalReasoning)}
|
|
5111
|
+
` + `- Cache read tokens: ${fmt(totalCacheRead)}
|
|
5112
|
+
` + `- Cache write tokens: ${fmt(totalCacheWrite)}
|
|
5113
|
+
` + `- Total cost: ${fmtCost(totalCost)}`);
|
|
5114
|
+
const agentGroups = new Map;
|
|
5115
|
+
for (const s of summaries) {
|
|
5116
|
+
const key = s.agentName ?? "(unknown)";
|
|
5117
|
+
const group = agentGroups.get(key);
|
|
5118
|
+
if (group) {
|
|
5119
|
+
group.push(s);
|
|
5120
|
+
} else {
|
|
5121
|
+
agentGroups.set(key, [s]);
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
const agentStats = Array.from(agentGroups.entries()).map(([agent, sessions]) => {
|
|
5125
|
+
const agentCost = sessions.reduce((sum, s) => sum + (s.totalCost ?? 0), 0);
|
|
5126
|
+
const agentTokens = sessions.reduce((sum, s) => sum + (s.tokenUsage?.inputTokens ?? 0) + (s.tokenUsage?.outputTokens ?? 0) + (s.tokenUsage?.reasoningTokens ?? 0), 0);
|
|
5127
|
+
const avgTokens = sessions.length > 0 ? Math.round(agentTokens / sessions.length) : 0;
|
|
5128
|
+
const avgCost = sessions.length > 0 ? agentCost / sessions.length : 0;
|
|
5129
|
+
return { agent, sessions: sessions.length, avgTokens, avgCost, totalCost: agentCost };
|
|
5130
|
+
}).sort((a, b) => b.totalCost - a.totalCost);
|
|
5131
|
+
const agentLines = agentStats.map((a) => `- **${a.agent}**: ${fmt(a.sessions)} session${a.sessions === 1 ? "" : "s"}, ` + `avg ${fmt(a.avgTokens)} tokens/session, ` + `avg ${fmtCost(a.avgCost)}/session, ` + `total ${fmtCost(a.totalCost)}`);
|
|
5132
|
+
sections.push(`## Per-Agent Breakdown
|
|
5133
|
+
${agentLines.join(`
|
|
5134
|
+
`)}`);
|
|
5135
|
+
const top5 = [...summaries].sort((a, b) => (b.totalCost ?? 0) - (a.totalCost ?? 0)).slice(0, 5);
|
|
5136
|
+
const top5Lines = top5.map((s) => {
|
|
5137
|
+
const id = s.sessionId.length > 8 ? s.sessionId.slice(0, 8) : s.sessionId;
|
|
5138
|
+
const agent = s.agentName ?? "(unknown)";
|
|
5139
|
+
const cost = fmtCost(s.totalCost ?? 0);
|
|
5140
|
+
const tokens = (s.tokenUsage?.inputTokens ?? 0) + (s.tokenUsage?.outputTokens ?? 0) + (s.tokenUsage?.reasoningTokens ?? 0);
|
|
5141
|
+
const duration = fmtDuration(s.durationMs);
|
|
5142
|
+
return `- \`${id}\` ${agent} — ${cost}, ${fmt(tokens)} tokens, ${duration}`;
|
|
5143
|
+
});
|
|
5144
|
+
sections.push(`## Top 5 Costliest Sessions
|
|
5145
|
+
${top5Lines.join(`
|
|
5146
|
+
`)}`);
|
|
5147
|
+
return sections.join(`
|
|
5148
|
+
|
|
5149
|
+
`);
|
|
5150
|
+
}
|
|
5151
|
+
function fmt(n) {
|
|
5152
|
+
return n.toLocaleString("en-US");
|
|
5153
|
+
}
|
|
5154
|
+
function fmtCost(n) {
|
|
5155
|
+
return `$${n.toFixed(2)}`;
|
|
5156
|
+
}
|
|
5157
|
+
function fmtDuration(ms) {
|
|
5158
|
+
const totalSeconds = Math.round(ms / 1000);
|
|
5159
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
5160
|
+
const seconds = totalSeconds % 60;
|
|
5161
|
+
if (minutes === 0)
|
|
5162
|
+
return `${seconds}s`;
|
|
5163
|
+
return `${minutes}m ${seconds}s`;
|
|
5164
|
+
}
|
|
5165
|
+
|
|
5170
5166
|
// src/plugin/plugin-interface.ts
|
|
5171
5167
|
var FINALIZE_TODOS_MARKER = "<!-- weave:finalize-todos -->";
|
|
5172
5168
|
function createPluginInterface(args) {
|
|
5173
|
-
const {
|
|
5169
|
+
const {
|
|
5170
|
+
pluginConfig,
|
|
5171
|
+
hooks,
|
|
5172
|
+
tools,
|
|
5173
|
+
configHandler,
|
|
5174
|
+
agents,
|
|
5175
|
+
client,
|
|
5176
|
+
directory = "",
|
|
5177
|
+
tracker,
|
|
5178
|
+
taskSystemEnabled = false
|
|
5179
|
+
} = args;
|
|
5174
5180
|
const lastAssistantMessageText = new Map;
|
|
5175
5181
|
const lastUserMessageText = new Map;
|
|
5176
5182
|
const todoFinalizedSessions = new Set;
|
|
@@ -5184,6 +5190,7 @@ function createPluginInterface(args) {
|
|
|
5184
5190
|
});
|
|
5185
5191
|
config.agent = result.agents;
|
|
5186
5192
|
config.command = result.commands;
|
|
5193
|
+
config.mcps = result.mcps;
|
|
5187
5194
|
if (result.defaultAgent) {
|
|
5188
5195
|
config.default_agent = result.defaultAgent;
|
|
5189
5196
|
}
|
|
@@ -5313,7 +5320,10 @@ ${cmdResult.contextInjection}`;
|
|
|
5313
5320
|
const maxTokens = input.model?.limit?.context ?? 0;
|
|
5314
5321
|
if (sessionId && maxTokens > 0) {
|
|
5315
5322
|
setContextLimit(sessionId, maxTokens);
|
|
5316
|
-
log("[context-window] Captured context limit", {
|
|
5323
|
+
log("[context-window] Captured context limit", {
|
|
5324
|
+
sessionId,
|
|
5325
|
+
maxTokens
|
|
5326
|
+
});
|
|
5317
5327
|
}
|
|
5318
5328
|
if (tracker && hooks.analyticsEnabled && sessionId && input.agent) {
|
|
5319
5329
|
tracker.setAgentName(sessionId, input.agent);
|
|
@@ -5340,7 +5350,9 @@ ${cmdResult.contextInjection}`;
|
|
|
5340
5350
|
try {
|
|
5341
5351
|
tracker.endSession(evt.properties.info.id);
|
|
5342
5352
|
} catch (err) {
|
|
5343
|
-
log("[analytics] Failed to end session (non-fatal)", {
|
|
5353
|
+
log("[analytics] Failed to end session (non-fatal)", {
|
|
5354
|
+
error: String(err)
|
|
5355
|
+
});
|
|
5344
5356
|
}
|
|
5345
5357
|
if (directory) {
|
|
5346
5358
|
try {
|
|
@@ -5447,7 +5459,10 @@ ${cmdResult.contextInjection}`;
|
|
|
5447
5459
|
agent: result.switchAgent
|
|
5448
5460
|
});
|
|
5449
5461
|
} catch (err) {
|
|
5450
|
-
log("[workflow] Failed to inject workflow continuation", {
|
|
5462
|
+
log("[workflow] Failed to inject workflow continuation", {
|
|
5463
|
+
sessionId,
|
|
5464
|
+
error: String(err)
|
|
5465
|
+
});
|
|
5451
5466
|
}
|
|
5452
5467
|
return;
|
|
5453
5468
|
}
|
|
@@ -5467,10 +5482,15 @@ ${cmdResult.contextInjection}`;
|
|
|
5467
5482
|
parts: [{ type: "text", text: result.continuationPrompt }]
|
|
5468
5483
|
}
|
|
5469
5484
|
});
|
|
5470
|
-
log("[work-continuation] Injected continuation prompt", {
|
|
5485
|
+
log("[work-continuation] Injected continuation prompt", {
|
|
5486
|
+
sessionId
|
|
5487
|
+
});
|
|
5471
5488
|
continuationFired = true;
|
|
5472
5489
|
} catch (err) {
|
|
5473
|
-
log("[work-continuation] Failed to inject continuation", {
|
|
5490
|
+
log("[work-continuation] Failed to inject continuation", {
|
|
5491
|
+
sessionId,
|
|
5492
|
+
error: String(err)
|
|
5493
|
+
});
|
|
5474
5494
|
}
|
|
5475
5495
|
} else if (result.continuationPrompt) {
|
|
5476
5496
|
log("[work-continuation] continuationPrompt available but no client", { sessionId });
|
|
@@ -5482,7 +5502,9 @@ ${cmdResult.contextInjection}`;
|
|
|
5482
5502
|
const sessionId = evt.properties?.sessionID ?? "";
|
|
5483
5503
|
if (sessionId && !todoFinalizedSessions.has(sessionId)) {
|
|
5484
5504
|
try {
|
|
5485
|
-
const todosResponse = await client.session.todo({
|
|
5505
|
+
const todosResponse = await client.session.todo({
|
|
5506
|
+
path: { id: sessionId }
|
|
5507
|
+
});
|
|
5486
5508
|
const todos = todosResponse.data ?? [];
|
|
5487
5509
|
const hasInProgress = todos.some((t) => t.status === "in_progress");
|
|
5488
5510
|
if (hasInProgress) {
|
|
@@ -5510,7 +5532,10 @@ Use todowrite NOW to mark all of them as "completed" (or "cancelled" if abandone
|
|
|
5510
5532
|
});
|
|
5511
5533
|
}
|
|
5512
5534
|
} catch (err) {
|
|
5513
|
-
log("[todo-finalize] Failed to check/finalize todos (non-fatal)", {
|
|
5535
|
+
log("[todo-finalize] Failed to check/finalize todos (non-fatal)", {
|
|
5536
|
+
sessionId,
|
|
5537
|
+
error: String(err)
|
|
5538
|
+
});
|
|
5514
5539
|
}
|
|
5515
5540
|
}
|
|
5516
5541
|
}
|