@a4hgehad/weave-mcp 0.8.0 → 0.8.1
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 +252 -225
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4634,221 +4634,6 @@ function getState(sessionId) {
|
|
|
4634
4634
|
function clearSession2(sessionId) {
|
|
4635
4635
|
sessionMap.delete(sessionId);
|
|
4636
4636
|
}
|
|
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
4637
|
// src/features/analytics/format-metrics.ts
|
|
4853
4638
|
function formatNumber(n) {
|
|
4854
4639
|
return n.toLocaleString("en-US");
|
|
@@ -4985,7 +4770,7 @@ function formatMetricsMarkdown(reports, summaries, args) {
|
|
|
4985
4770
|
}
|
|
4986
4771
|
|
|
4987
4772
|
// src/features/analytics/plan-parser.ts
|
|
4988
|
-
import { readFileSync as
|
|
4773
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
4989
4774
|
function extractSection2(content, heading) {
|
|
4990
4775
|
const lines = content.split(`
|
|
4991
4776
|
`);
|
|
@@ -5020,7 +4805,7 @@ function extractFilePath2(raw) {
|
|
|
5020
4805
|
function extractPlannedFiles(planPath) {
|
|
5021
4806
|
let content;
|
|
5022
4807
|
try {
|
|
5023
|
-
content =
|
|
4808
|
+
content = readFileSync10(planPath, "utf-8");
|
|
5024
4809
|
} catch {
|
|
5025
4810
|
return [];
|
|
5026
4811
|
}
|
|
@@ -5107,6 +4892,144 @@ function calculateAdherence(plannedFiles, actualFiles) {
|
|
|
5107
4892
|
};
|
|
5108
4893
|
}
|
|
5109
4894
|
|
|
4895
|
+
// src/features/analytics/types.ts
|
|
4896
|
+
var ANALYTICS_DIR = ".weave/analytics";
|
|
4897
|
+
var SESSION_SUMMARIES_FILE = "session-summaries.jsonl";
|
|
4898
|
+
var FINGERPRINT_FILE = "fingerprint.json";
|
|
4899
|
+
var METRICS_REPORTS_FILE = "metrics-reports.jsonl";
|
|
4900
|
+
var MAX_METRICS_ENTRIES = 100;
|
|
4901
|
+
function zeroTokenUsage() {
|
|
4902
|
+
return { input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite: 0 };
|
|
4903
|
+
}
|
|
4904
|
+
|
|
4905
|
+
// src/features/analytics/storage.ts
|
|
4906
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, appendFileSync as appendFileSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync4, statSync as statSync3 } from "fs";
|
|
4907
|
+
import { join as join11 } from "path";
|
|
4908
|
+
var MAX_SESSION_ENTRIES = 1000;
|
|
4909
|
+
function ensureAnalyticsDir(directory) {
|
|
4910
|
+
const dir = join11(directory, ANALYTICS_DIR);
|
|
4911
|
+
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
4912
|
+
return dir;
|
|
4913
|
+
}
|
|
4914
|
+
function appendSessionSummary(directory, summary) {
|
|
4915
|
+
try {
|
|
4916
|
+
const dir = ensureAnalyticsDir(directory);
|
|
4917
|
+
const filePath = join11(dir, SESSION_SUMMARIES_FILE);
|
|
4918
|
+
const line = JSON.stringify(summary) + `
|
|
4919
|
+
`;
|
|
4920
|
+
appendFileSync2(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
4921
|
+
try {
|
|
4922
|
+
const TYPICAL_ENTRY_BYTES = 200;
|
|
4923
|
+
const rotationSizeThreshold = MAX_SESSION_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
|
|
4924
|
+
const { size } = statSync3(filePath);
|
|
4925
|
+
if (size > rotationSizeThreshold) {
|
|
4926
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
4927
|
+
const lines = content.split(`
|
|
4928
|
+
`).filter((l) => l.trim().length > 0);
|
|
4929
|
+
if (lines.length > MAX_SESSION_ENTRIES) {
|
|
4930
|
+
const trimmed = lines.slice(-MAX_SESSION_ENTRIES).join(`
|
|
4931
|
+
`) + `
|
|
4932
|
+
`;
|
|
4933
|
+
writeFileSync4(filePath, trimmed, { encoding: "utf-8", mode: 384 });
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
} catch {}
|
|
4937
|
+
return true;
|
|
4938
|
+
} catch {
|
|
4939
|
+
return false;
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
function readSessionSummaries(directory) {
|
|
4943
|
+
const filePath = join11(directory, ANALYTICS_DIR, SESSION_SUMMARIES_FILE);
|
|
4944
|
+
try {
|
|
4945
|
+
if (!existsSync12(filePath))
|
|
4946
|
+
return [];
|
|
4947
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
4948
|
+
const lines = content.split(`
|
|
4949
|
+
`).filter((line) => line.trim().length > 0);
|
|
4950
|
+
const summaries = [];
|
|
4951
|
+
for (const line of lines) {
|
|
4952
|
+
try {
|
|
4953
|
+
summaries.push(JSON.parse(line));
|
|
4954
|
+
} catch {}
|
|
4955
|
+
}
|
|
4956
|
+
return summaries;
|
|
4957
|
+
} catch {
|
|
4958
|
+
return [];
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
function writeFingerprint(directory, fingerprint) {
|
|
4962
|
+
try {
|
|
4963
|
+
const dir = ensureAnalyticsDir(directory);
|
|
4964
|
+
const filePath = join11(dir, FINGERPRINT_FILE);
|
|
4965
|
+
writeFileSync4(filePath, JSON.stringify(fingerprint, null, 2), { encoding: "utf-8", mode: 384 });
|
|
4966
|
+
return true;
|
|
4967
|
+
} catch {
|
|
4968
|
+
return false;
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
function readFingerprint(directory) {
|
|
4972
|
+
const filePath = join11(directory, ANALYTICS_DIR, FINGERPRINT_FILE);
|
|
4973
|
+
try {
|
|
4974
|
+
if (!existsSync12(filePath))
|
|
4975
|
+
return null;
|
|
4976
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
4977
|
+
const parsed = JSON.parse(content);
|
|
4978
|
+
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.stack))
|
|
4979
|
+
return null;
|
|
4980
|
+
return parsed;
|
|
4981
|
+
} catch {
|
|
4982
|
+
return null;
|
|
4983
|
+
}
|
|
4984
|
+
}
|
|
4985
|
+
function writeMetricsReport(directory, report) {
|
|
4986
|
+
try {
|
|
4987
|
+
const dir = ensureAnalyticsDir(directory);
|
|
4988
|
+
const filePath = join11(dir, METRICS_REPORTS_FILE);
|
|
4989
|
+
const line = JSON.stringify(report) + `
|
|
4990
|
+
`;
|
|
4991
|
+
appendFileSync2(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
4992
|
+
try {
|
|
4993
|
+
const TYPICAL_ENTRY_BYTES = 200;
|
|
4994
|
+
const rotationSizeThreshold = MAX_METRICS_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
|
|
4995
|
+
const { size } = statSync3(filePath);
|
|
4996
|
+
if (size > rotationSizeThreshold) {
|
|
4997
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
4998
|
+
const lines = content.split(`
|
|
4999
|
+
`).filter((l) => l.trim().length > 0);
|
|
5000
|
+
if (lines.length > MAX_METRICS_ENTRIES) {
|
|
5001
|
+
const trimmed = lines.slice(-MAX_METRICS_ENTRIES).join(`
|
|
5002
|
+
`) + `
|
|
5003
|
+
`;
|
|
5004
|
+
writeFileSync4(filePath, trimmed, { encoding: "utf-8", mode: 384 });
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
} catch {}
|
|
5008
|
+
return true;
|
|
5009
|
+
} catch {
|
|
5010
|
+
return false;
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
5013
|
+
function readMetricsReports(directory) {
|
|
5014
|
+
const filePath = join11(directory, ANALYTICS_DIR, METRICS_REPORTS_FILE);
|
|
5015
|
+
try {
|
|
5016
|
+
if (!existsSync12(filePath))
|
|
5017
|
+
return [];
|
|
5018
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
5019
|
+
const lines = content.split(`
|
|
5020
|
+
`).filter((line) => line.trim().length > 0);
|
|
5021
|
+
const reports = [];
|
|
5022
|
+
for (const line of lines) {
|
|
5023
|
+
try {
|
|
5024
|
+
reports.push(JSON.parse(line));
|
|
5025
|
+
} catch {}
|
|
5026
|
+
}
|
|
5027
|
+
return reports;
|
|
5028
|
+
} catch {
|
|
5029
|
+
return [];
|
|
5030
|
+
}
|
|
5031
|
+
}
|
|
5032
|
+
|
|
5110
5033
|
// src/features/analytics/plan-token-aggregator.ts
|
|
5111
5034
|
function aggregateTokensForPlan(directory, sessionIds) {
|
|
5112
5035
|
const summaries = readSessionSummaries(directory);
|
|
@@ -5167,10 +5090,95 @@ function generateMetricsReport(directory, state) {
|
|
|
5167
5090
|
}
|
|
5168
5091
|
}
|
|
5169
5092
|
|
|
5093
|
+
// src/features/analytics/token-report.ts
|
|
5094
|
+
function generateTokenReport(summaries) {
|
|
5095
|
+
if (summaries.length === 0) {
|
|
5096
|
+
return "No session data available.";
|
|
5097
|
+
}
|
|
5098
|
+
const sections = [];
|
|
5099
|
+
const totalSessions = summaries.length;
|
|
5100
|
+
const totalMessages = summaries.reduce((sum, s) => sum + (s.tokenUsage?.totalMessages ?? 0), 0);
|
|
5101
|
+
const totalInput = summaries.reduce((sum, s) => sum + (s.tokenUsage?.inputTokens ?? 0), 0);
|
|
5102
|
+
const totalOutput = summaries.reduce((sum, s) => sum + (s.tokenUsage?.outputTokens ?? 0), 0);
|
|
5103
|
+
const totalReasoning = summaries.reduce((sum, s) => sum + (s.tokenUsage?.reasoningTokens ?? 0), 0);
|
|
5104
|
+
const totalCacheRead = summaries.reduce((sum, s) => sum + (s.tokenUsage?.cacheReadTokens ?? 0), 0);
|
|
5105
|
+
const totalCacheWrite = summaries.reduce((sum, s) => sum + (s.tokenUsage?.cacheWriteTokens ?? 0), 0);
|
|
5106
|
+
const totalCost = summaries.reduce((sum, s) => sum + (s.totalCost ?? 0), 0);
|
|
5107
|
+
sections.push(`## Overall Totals
|
|
5108
|
+
` + `- Sessions: ${fmt(totalSessions)}
|
|
5109
|
+
` + `- Messages: ${fmt(totalMessages)}
|
|
5110
|
+
` + `- Input tokens: ${fmt(totalInput)}
|
|
5111
|
+
` + `- Output tokens: ${fmt(totalOutput)}
|
|
5112
|
+
` + `- Reasoning tokens: ${fmt(totalReasoning)}
|
|
5113
|
+
` + `- Cache read tokens: ${fmt(totalCacheRead)}
|
|
5114
|
+
` + `- Cache write tokens: ${fmt(totalCacheWrite)}
|
|
5115
|
+
` + `- Total cost: ${fmtCost(totalCost)}`);
|
|
5116
|
+
const agentGroups = new Map;
|
|
5117
|
+
for (const s of summaries) {
|
|
5118
|
+
const key = s.agentName ?? "(unknown)";
|
|
5119
|
+
const group = agentGroups.get(key);
|
|
5120
|
+
if (group) {
|
|
5121
|
+
group.push(s);
|
|
5122
|
+
} else {
|
|
5123
|
+
agentGroups.set(key, [s]);
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
const agentStats = Array.from(agentGroups.entries()).map(([agent, sessions]) => {
|
|
5127
|
+
const agentCost = sessions.reduce((sum, s) => sum + (s.totalCost ?? 0), 0);
|
|
5128
|
+
const agentTokens = sessions.reduce((sum, s) => sum + (s.tokenUsage?.inputTokens ?? 0) + (s.tokenUsage?.outputTokens ?? 0) + (s.tokenUsage?.reasoningTokens ?? 0), 0);
|
|
5129
|
+
const avgTokens = sessions.length > 0 ? Math.round(agentTokens / sessions.length) : 0;
|
|
5130
|
+
const avgCost = sessions.length > 0 ? agentCost / sessions.length : 0;
|
|
5131
|
+
return { agent, sessions: sessions.length, avgTokens, avgCost, totalCost: agentCost };
|
|
5132
|
+
}).sort((a, b) => b.totalCost - a.totalCost);
|
|
5133
|
+
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)}`);
|
|
5134
|
+
sections.push(`## Per-Agent Breakdown
|
|
5135
|
+
${agentLines.join(`
|
|
5136
|
+
`)}`);
|
|
5137
|
+
const top5 = [...summaries].sort((a, b) => (b.totalCost ?? 0) - (a.totalCost ?? 0)).slice(0, 5);
|
|
5138
|
+
const top5Lines = top5.map((s) => {
|
|
5139
|
+
const id = s.sessionId.length > 8 ? s.sessionId.slice(0, 8) : s.sessionId;
|
|
5140
|
+
const agent = s.agentName ?? "(unknown)";
|
|
5141
|
+
const cost = fmtCost(s.totalCost ?? 0);
|
|
5142
|
+
const tokens = (s.tokenUsage?.inputTokens ?? 0) + (s.tokenUsage?.outputTokens ?? 0) + (s.tokenUsage?.reasoningTokens ?? 0);
|
|
5143
|
+
const duration = fmtDuration(s.durationMs);
|
|
5144
|
+
return `- \`${id}\` ${agent} — ${cost}, ${fmt(tokens)} tokens, ${duration}`;
|
|
5145
|
+
});
|
|
5146
|
+
sections.push(`## Top 5 Costliest Sessions
|
|
5147
|
+
${top5Lines.join(`
|
|
5148
|
+
`)}`);
|
|
5149
|
+
return sections.join(`
|
|
5150
|
+
|
|
5151
|
+
`);
|
|
5152
|
+
}
|
|
5153
|
+
function fmt(n) {
|
|
5154
|
+
return n.toLocaleString("en-US");
|
|
5155
|
+
}
|
|
5156
|
+
function fmtCost(n) {
|
|
5157
|
+
return `$${n.toFixed(2)}`;
|
|
5158
|
+
}
|
|
5159
|
+
function fmtDuration(ms) {
|
|
5160
|
+
const totalSeconds = Math.round(ms / 1000);
|
|
5161
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
5162
|
+
const seconds = totalSeconds % 60;
|
|
5163
|
+
if (minutes === 0)
|
|
5164
|
+
return `${seconds}s`;
|
|
5165
|
+
return `${minutes}m ${seconds}s`;
|
|
5166
|
+
}
|
|
5167
|
+
|
|
5170
5168
|
// src/plugin/plugin-interface.ts
|
|
5171
5169
|
var FINALIZE_TODOS_MARKER = "<!-- weave:finalize-todos -->";
|
|
5172
5170
|
function createPluginInterface(args) {
|
|
5173
|
-
const {
|
|
5171
|
+
const {
|
|
5172
|
+
pluginConfig,
|
|
5173
|
+
hooks,
|
|
5174
|
+
tools,
|
|
5175
|
+
configHandler,
|
|
5176
|
+
agents,
|
|
5177
|
+
client,
|
|
5178
|
+
directory = "",
|
|
5179
|
+
tracker,
|
|
5180
|
+
taskSystemEnabled = false
|
|
5181
|
+
} = args;
|
|
5174
5182
|
const lastAssistantMessageText = new Map;
|
|
5175
5183
|
const lastUserMessageText = new Map;
|
|
5176
5184
|
const todoFinalizedSessions = new Set;
|
|
@@ -5184,6 +5192,7 @@ function createPluginInterface(args) {
|
|
|
5184
5192
|
});
|
|
5185
5193
|
config.agent = result.agents;
|
|
5186
5194
|
config.command = result.commands;
|
|
5195
|
+
config.mcps = result.mcps;
|
|
5187
5196
|
if (result.defaultAgent) {
|
|
5188
5197
|
config.default_agent = result.defaultAgent;
|
|
5189
5198
|
}
|
|
@@ -5313,7 +5322,10 @@ ${cmdResult.contextInjection}`;
|
|
|
5313
5322
|
const maxTokens = input.model?.limit?.context ?? 0;
|
|
5314
5323
|
if (sessionId && maxTokens > 0) {
|
|
5315
5324
|
setContextLimit(sessionId, maxTokens);
|
|
5316
|
-
log("[context-window] Captured context limit", {
|
|
5325
|
+
log("[context-window] Captured context limit", {
|
|
5326
|
+
sessionId,
|
|
5327
|
+
maxTokens
|
|
5328
|
+
});
|
|
5317
5329
|
}
|
|
5318
5330
|
if (tracker && hooks.analyticsEnabled && sessionId && input.agent) {
|
|
5319
5331
|
tracker.setAgentName(sessionId, input.agent);
|
|
@@ -5340,7 +5352,9 @@ ${cmdResult.contextInjection}`;
|
|
|
5340
5352
|
try {
|
|
5341
5353
|
tracker.endSession(evt.properties.info.id);
|
|
5342
5354
|
} catch (err) {
|
|
5343
|
-
log("[analytics] Failed to end session (non-fatal)", {
|
|
5355
|
+
log("[analytics] Failed to end session (non-fatal)", {
|
|
5356
|
+
error: String(err)
|
|
5357
|
+
});
|
|
5344
5358
|
}
|
|
5345
5359
|
if (directory) {
|
|
5346
5360
|
try {
|
|
@@ -5447,7 +5461,10 @@ ${cmdResult.contextInjection}`;
|
|
|
5447
5461
|
agent: result.switchAgent
|
|
5448
5462
|
});
|
|
5449
5463
|
} catch (err) {
|
|
5450
|
-
log("[workflow] Failed to inject workflow continuation", {
|
|
5464
|
+
log("[workflow] Failed to inject workflow continuation", {
|
|
5465
|
+
sessionId,
|
|
5466
|
+
error: String(err)
|
|
5467
|
+
});
|
|
5451
5468
|
}
|
|
5452
5469
|
return;
|
|
5453
5470
|
}
|
|
@@ -5467,10 +5484,15 @@ ${cmdResult.contextInjection}`;
|
|
|
5467
5484
|
parts: [{ type: "text", text: result.continuationPrompt }]
|
|
5468
5485
|
}
|
|
5469
5486
|
});
|
|
5470
|
-
log("[work-continuation] Injected continuation prompt", {
|
|
5487
|
+
log("[work-continuation] Injected continuation prompt", {
|
|
5488
|
+
sessionId
|
|
5489
|
+
});
|
|
5471
5490
|
continuationFired = true;
|
|
5472
5491
|
} catch (err) {
|
|
5473
|
-
log("[work-continuation] Failed to inject continuation", {
|
|
5492
|
+
log("[work-continuation] Failed to inject continuation", {
|
|
5493
|
+
sessionId,
|
|
5494
|
+
error: String(err)
|
|
5495
|
+
});
|
|
5474
5496
|
}
|
|
5475
5497
|
} else if (result.continuationPrompt) {
|
|
5476
5498
|
log("[work-continuation] continuationPrompt available but no client", { sessionId });
|
|
@@ -5482,7 +5504,9 @@ ${cmdResult.contextInjection}`;
|
|
|
5482
5504
|
const sessionId = evt.properties?.sessionID ?? "";
|
|
5483
5505
|
if (sessionId && !todoFinalizedSessions.has(sessionId)) {
|
|
5484
5506
|
try {
|
|
5485
|
-
const todosResponse = await client.session.todo({
|
|
5507
|
+
const todosResponse = await client.session.todo({
|
|
5508
|
+
path: { id: sessionId }
|
|
5509
|
+
});
|
|
5486
5510
|
const todos = todosResponse.data ?? [];
|
|
5487
5511
|
const hasInProgress = todos.some((t) => t.status === "in_progress");
|
|
5488
5512
|
if (hasInProgress) {
|
|
@@ -5510,7 +5534,10 @@ Use todowrite NOW to mark all of them as "completed" (or "cancelled" if abandone
|
|
|
5510
5534
|
});
|
|
5511
5535
|
}
|
|
5512
5536
|
} catch (err) {
|
|
5513
|
-
log("[todo-finalize] Failed to check/finalize todos (non-fatal)", {
|
|
5537
|
+
log("[todo-finalize] Failed to check/finalize todos (non-fatal)", {
|
|
5538
|
+
sessionId,
|
|
5539
|
+
error: String(err)
|
|
5540
|
+
});
|
|
5514
5541
|
}
|
|
5515
5542
|
}
|
|
5516
5543
|
}
|