twining-mcp 1.4.3 → 1.5.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.
Files changed (55) hide show
  1. package/README.md +51 -2
  2. package/dist/analytics/analytics-engine.d.ts +18 -0
  3. package/dist/analytics/analytics-engine.d.ts.map +1 -0
  4. package/dist/analytics/analytics-engine.js +114 -0
  5. package/dist/analytics/analytics-engine.js.map +1 -0
  6. package/dist/analytics/instrumented-server.d.ts +12 -0
  7. package/dist/analytics/instrumented-server.d.ts.map +1 -0
  8. package/dist/analytics/instrumented-server.js +76 -0
  9. package/dist/analytics/instrumented-server.js.map +1 -0
  10. package/dist/analytics/metrics-collector.d.ts +15 -0
  11. package/dist/analytics/metrics-collector.d.ts.map +1 -0
  12. package/dist/analytics/metrics-collector.js +36 -0
  13. package/dist/analytics/metrics-collector.js.map +1 -0
  14. package/dist/analytics/metrics-store.d.ts +20 -0
  15. package/dist/analytics/metrics-store.d.ts.map +1 -0
  16. package/dist/analytics/metrics-store.js +109 -0
  17. package/dist/analytics/metrics-store.js.map +1 -0
  18. package/dist/analytics/telemetry-client.d.ts +19 -0
  19. package/dist/analytics/telemetry-client.d.ts.map +1 -0
  20. package/dist/analytics/telemetry-client.js +137 -0
  21. package/dist/analytics/telemetry-client.js.map +1 -0
  22. package/dist/config.d.ts.map +1 -1
  23. package/dist/config.js +10 -0
  24. package/dist/config.js.map +1 -1
  25. package/dist/dashboard/api-routes.d.ts.map +1 -1
  26. package/dist/dashboard/api-routes.js +69 -0
  27. package/dist/dashboard/api-routes.js.map +1 -1
  28. package/dist/dashboard/public/app.js +178 -0
  29. package/dist/dashboard/public/index.html +82 -0
  30. package/dist/dashboard/public/style.css +73 -0
  31. package/dist/engine/blackboard.d.ts +5 -0
  32. package/dist/engine/blackboard.d.ts.map +1 -1
  33. package/dist/engine/blackboard.js +17 -0
  34. package/dist/engine/blackboard.js.map +1 -1
  35. package/dist/index.js +23 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/server.d.ts +8 -1
  38. package/dist/server.d.ts.map +1 -1
  39. package/dist/server.js +8 -5
  40. package/dist/server.js.map +1 -1
  41. package/dist/storage/blackboard-store.d.ts +5 -0
  42. package/dist/storage/blackboard-store.d.ts.map +1 -1
  43. package/dist/storage/blackboard-store.js +44 -0
  44. package/dist/storage/blackboard-store.js.map +1 -1
  45. package/dist/storage/init.js +1 -1
  46. package/dist/storage/init.js.map +1 -1
  47. package/dist/tools/blackboard-tools.d.ts.map +1 -1
  48. package/dist/tools/blackboard-tools.js +25 -0
  49. package/dist/tools/blackboard-tools.js.map +1 -1
  50. package/dist/utils/types.d.ts +79 -0
  51. package/dist/utils/types.d.ts.map +1 -1
  52. package/package.json +4 -1
  53. package/src/dashboard/public/app.js +178 -0
  54. package/src/dashboard/public/index.html +82 -0
  55. package/src/dashboard/public/style.css +73 -0
package/README.md CHANGED
@@ -182,17 +182,66 @@ No. It's a local MCP server — tool calls are local file reads/writes. Semantic
182
182
  Yes. Twining is a standard MCP server. Any MCP host can connect to it.
183
183
 
184
184
  **Where does my data go?**
185
- Nowhere. All state is local in `.twining/`. No telemetry, no cloud, no external calls.
185
+ All coordination state is local in `.twining/`. Tool call metrics are stored locally in `.twining/metrics.jsonl` (gitignored). Optional anonymous telemetry can be enabled see [Analytics](#analytics) below.
186
186
 
187
187
  **Is Twining an agent orchestrator?**
188
188
  No. It's a coordination state layer. It captures what agents decided and why, and makes that knowledge available to future agents. Use it alongside orchestrators, agent teams, or standalone sessions.
189
189
 
190
+ ## Analytics
191
+
192
+ Twining includes a three-layer analytics system to help you understand the value it provides.
193
+
194
+ ### Insights Dashboard Tab
195
+
196
+ The web dashboard includes an **Insights** tab showing:
197
+
198
+ - **Value Metrics** — Blind decision prevention rate, warning acknowledgment, test coverage via `tested_by` graph relations, commit traceability, decision lifecycle, knowledge graph stats, and agent coordination metrics
199
+ - **Tool Usage** — Call counts, error rates, average/P95 latency per tool
200
+ - **Error Breakdown** — Errors grouped by tool and error code
201
+
202
+ All value metrics are computed from existing `.twining/` data — no new data collection needed.
203
+
204
+ ### Tool Call Metrics
205
+
206
+ Every MCP tool call is automatically instrumented with timing and success/error tracking. Metrics are stored locally in `.twining/metrics.jsonl` (gitignored — operational data, not architectural).
207
+
208
+ To disable local metrics collection, set in `.twining/config.yml`:
209
+
210
+ ```yaml
211
+ analytics:
212
+ metrics:
213
+ enabled: false
214
+ ```
215
+
216
+ ### Opt-in Telemetry
217
+
218
+ Anonymous aggregate usage data can optionally be sent to PostHog to help improve Twining. **Disabled by default.** To enable, add to `.twining/config.yml`:
219
+
220
+ ```yaml
221
+ analytics:
222
+ telemetry:
223
+ enabled: true
224
+ ```
225
+
226
+ That's it — the PostHog project key is built into the source code. If you run your own PostHog instance, you can override with `posthog_api_key` and `posthog_host`.
227
+
228
+ **What is sent:** tool names, call durations, success/failure booleans, server version, OS, architecture.
229
+
230
+ **What is never sent:** file paths, decision content, agent names, error messages, tool arguments, environment variables.
231
+
232
+ **Privacy safeguards:**
233
+ - `DO_NOT_TRACK=1` environment variable always overrides config
234
+ - `CI=true` auto-disables telemetry
235
+ - Identity is a SHA-256 hash of hostname + project root (never raw paths)
236
+ - Network failures are silent — no retries
237
+ - `posthog-node` is an optional dependency — graceful no-op if not installed
238
+
190
239
  ## Development
191
240
 
192
241
  ```bash
193
242
  npm install # Install dependencies
194
243
  npm run build # Build
195
- npm test # Run tests (444 tests)
244
+ npm test # Run tests (570+ tests)
196
245
  npm run test:watch
197
246
  ```
198
247
 
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Analytics engine — computes value stats from existing .twining/ data.
3
+ * No new data collection needed — reads existing stores.
4
+ */
5
+ import type { BlackboardStore } from "../storage/blackboard-store.js";
6
+ import type { DecisionStore } from "../storage/decision-store.js";
7
+ import type { GraphStore } from "../storage/graph-store.js";
8
+ import type { HandoffStore } from "../storage/handoff-store.js";
9
+ import type { ValueStats } from "../utils/types.js";
10
+ export declare class AnalyticsEngine {
11
+ private readonly blackboardStore;
12
+ private readonly decisionStore;
13
+ private readonly graphStore;
14
+ private readonly handoffStore;
15
+ constructor(blackboardStore: BlackboardStore, decisionStore: DecisionStore, graphStore: GraphStore, handoffStore: HandoffStore);
16
+ computeValueStats(): Promise<ValueStats>;
17
+ }
18
+ //# sourceMappingURL=analytics-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-engine.d.ts","sourceRoot":"","sources":["../../src/analytics/analytics-engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAHZ,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,YAAY;IAGvC,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC;CA6H/C"}
@@ -0,0 +1,114 @@
1
+ export class AnalyticsEngine {
2
+ blackboardStore;
3
+ decisionStore;
4
+ graphStore;
5
+ handoffStore;
6
+ constructor(blackboardStore, decisionStore, graphStore, handoffStore) {
7
+ this.blackboardStore = blackboardStore;
8
+ this.decisionStore = decisionStore;
9
+ this.graphStore = graphStore;
10
+ this.handoffStore = handoffStore;
11
+ }
12
+ async computeValueStats() {
13
+ const [decisionIndex, bbResult, entities, relations, handoffs,] = await Promise.all([
14
+ this.decisionStore.getIndex(),
15
+ this.blackboardStore.read(),
16
+ this.graphStore.getEntities(),
17
+ this.graphStore.getRelations(),
18
+ this.handoffStore.list({}),
19
+ ]);
20
+ // Blind decisions prevented
21
+ const totalDecisions = decisionIndex.length;
22
+ let assembledBefore = 0;
23
+ for (const dec of decisionIndex) {
24
+ const full = await this.decisionStore.get(dec.id);
25
+ if (full?.assembled_before) {
26
+ assembledBefore++;
27
+ }
28
+ }
29
+ const preventionRate = totalDecisions > 0 ? assembledBefore / totalDecisions : 0;
30
+ // Warnings surfaced
31
+ const warnings = bbResult.entries.filter((e) => e.entry_type === "warning");
32
+ const warningTotal = warnings.length;
33
+ // Check for acknowledgment patterns: relates_to links or answer entries
34
+ const answerRelatesTo = new Set(bbResult.entries
35
+ .filter((e) => e.entry_type === "answer")
36
+ .flatMap((e) => e.relates_to || []));
37
+ const acknowledged = warnings.filter((w) => answerRelatesTo.has(w.id) || w.tags.includes("acknowledged")).length;
38
+ const resolved = warnings.filter((w) => w.tags.includes("resolved")).length;
39
+ const ignored = warningTotal - acknowledged - resolved;
40
+ // Test coverage via graph relations
41
+ const testedByRelations = relations.filter((r) => r.type === "tested_by");
42
+ const decisionsWithTests = new Set(testedByRelations.map((r) => r.source));
43
+ const withTestedBy = decisionIndex.filter((d) => decisionsWithTests.has(d.id)).length;
44
+ const coverageRate = totalDecisions > 0 ? withTestedBy / totalDecisions : 0;
45
+ // Decision lifecycle
46
+ const lifecycle = { active: 0, provisional: 0, superseded: 0, overridden: 0 };
47
+ for (const dec of decisionIndex) {
48
+ if (dec.status in lifecycle) {
49
+ lifecycle[dec.status]++;
50
+ }
51
+ }
52
+ // Commit traceability
53
+ const withCommits = decisionIndex.filter((d) => d.commit_hashes && d.commit_hashes.length > 0).length;
54
+ const traceabilityRate = totalDecisions > 0 ? withCommits / totalDecisions : 0;
55
+ // Knowledge graph
56
+ const entitiesByType = {};
57
+ for (const entity of entities) {
58
+ entitiesByType[entity.type] = (entitiesByType[entity.type] || 0) + 1;
59
+ }
60
+ const relationsByType = {};
61
+ for (const relation of relations) {
62
+ relationsByType[relation.type] = (relationsByType[relation.type] || 0) + 1;
63
+ }
64
+ // Agent coordination (handoffs are HandoffIndexEntry with result_status + acknowledged)
65
+ const byResultStatus = {};
66
+ let acknowledgedHandoffs = 0;
67
+ for (const handoff of handoffs) {
68
+ const status = handoff.result_status || "unknown";
69
+ byResultStatus[status] = (byResultStatus[status] || 0) + 1;
70
+ if (handoff.acknowledged) {
71
+ acknowledgedHandoffs++;
72
+ }
73
+ }
74
+ const acknowledgmentRate = handoffs.length > 0
75
+ ? acknowledgedHandoffs / handoffs.length
76
+ : 0;
77
+ return {
78
+ blind_decisions_prevented: {
79
+ total_decisions: totalDecisions,
80
+ assembled_before: assembledBefore,
81
+ prevention_rate: Math.round(preventionRate * 100) / 100,
82
+ },
83
+ warnings_surfaced: {
84
+ total: warningTotal,
85
+ acknowledged,
86
+ resolved,
87
+ ignored: Math.max(0, ignored),
88
+ },
89
+ test_coverage: {
90
+ total_decisions: totalDecisions,
91
+ with_tested_by: withTestedBy,
92
+ coverage_rate: Math.round(coverageRate * 100) / 100,
93
+ },
94
+ decision_lifecycle: lifecycle,
95
+ commit_traceability: {
96
+ total_decisions: totalDecisions,
97
+ with_commits: withCommits,
98
+ traceability_rate: Math.round(traceabilityRate * 100) / 100,
99
+ },
100
+ knowledge_graph: {
101
+ entities: entities.length,
102
+ relations: relations.length,
103
+ entities_by_type: entitiesByType,
104
+ relations_by_type: relationsByType,
105
+ },
106
+ agent_coordination: {
107
+ total_handoffs: handoffs.length,
108
+ by_result_status: byResultStatus,
109
+ acknowledgment_rate: Math.round(acknowledgmentRate * 100) / 100,
110
+ },
111
+ };
112
+ }
113
+ }
114
+ //# sourceMappingURL=analytics-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-engine.js","sourceRoot":"","sources":["../../src/analytics/analytics-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Instrumented MCP server — patches registerTool to wrap callbacks with timing.
3
+ * Zero changes to any tool file — instrumentation is invisible to them.
4
+ */
5
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import type { MetricsCollector } from "./metrics-collector.js";
7
+ /**
8
+ * Patch the server's registerTool method to wrap all tool callbacks with
9
+ * timing instrumentation. Returns the same server instance (mutated).
10
+ */
11
+ export declare function createInstrumentedServer(server: McpServer, collector: MetricsCollector): McpServer;
12
+ //# sourceMappingURL=instrumented-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumented-server.d.ts","sourceRoot":"","sources":["../../src/analytics/instrumented-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,gBAAgB,GAC1B,SAAS,CAuEX"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Patch the server's registerTool method to wrap all tool callbacks with
3
+ * timing instrumentation. Returns the same server instance (mutated).
4
+ */
5
+ export function createInstrumentedServer(server, collector) {
6
+ const originalRegisterTool = server.registerTool.bind(server);
7
+ // registerTool signature: (name, config, callback)
8
+ server.registerTool = function (name, config, callback) {
9
+ const wrappedCallback = async function (...cbArgs) {
10
+ const start = Date.now();
11
+ let success = true;
12
+ let errorCode;
13
+ try {
14
+ const result = await callback(...cbArgs);
15
+ // Detect soft errors by inspecting toolError() response format
16
+ if (result && typeof result === "object" && "content" in result) {
17
+ const content = result.content;
18
+ if (Array.isArray(content) && content.length > 0) {
19
+ const first = content[0];
20
+ if (first.text) {
21
+ try {
22
+ const parsed = JSON.parse(first.text);
23
+ if (parsed.error === true) {
24
+ success = false;
25
+ errorCode = parsed.code || "SOFT_ERROR";
26
+ }
27
+ }
28
+ catch {
29
+ // Not JSON or not error format — that's fine
30
+ }
31
+ }
32
+ }
33
+ }
34
+ const durationMs = Date.now() - start;
35
+ const agentId = extractAgentId(cbArgs);
36
+ // Fire-and-forget metric recording
37
+ collector.record({
38
+ tool_name: name,
39
+ timestamp: new Date().toISOString(),
40
+ duration_ms: durationMs,
41
+ success,
42
+ error_code: errorCode,
43
+ agent_id: agentId,
44
+ }).catch(() => { });
45
+ return result;
46
+ }
47
+ catch (err) {
48
+ const durationMs = Date.now() - start;
49
+ const agentId = extractAgentId(cbArgs);
50
+ collector.record({
51
+ tool_name: name,
52
+ timestamp: new Date().toISOString(),
53
+ duration_ms: durationMs,
54
+ success: false,
55
+ error_code: err instanceof Error ? err.constructor.name : "UNKNOWN",
56
+ agent_id: agentId,
57
+ }).catch(() => { });
58
+ throw err;
59
+ }
60
+ };
61
+ return originalRegisterTool(name, config, wrappedCallback);
62
+ };
63
+ return server;
64
+ }
65
+ /** Extract agent_id from tool call arguments */
66
+ function extractAgentId(cbArgs) {
67
+ // MCP tool callbacks receive (args, extra) where args is the parsed tool input
68
+ if (cbArgs.length > 0 && cbArgs[0] && typeof cbArgs[0] === "object") {
69
+ const args = cbArgs[0];
70
+ if (typeof args.agent_id === "string") {
71
+ return args.agent_id;
72
+ }
73
+ }
74
+ return "unknown";
75
+ }
76
+ //# sourceMappingURL=instrumented-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumented-server.js","sourceRoot":"","sources":["../../src/analytics/instrumented-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { MetricEntry } from "../utils/types.js";
2
+ export declare class MetricsCollector {
3
+ private readonly metricsPath;
4
+ private telemetryClient;
5
+ constructor(twiningDir: string);
6
+ /** Set an optional telemetry client to forward sanitized events */
7
+ setTelemetryClient(client: TelemetryClientLike): void;
8
+ /** Record a tool call metric. Fire-and-forget — never throws. */
9
+ record(entry: MetricEntry): Promise<void>;
10
+ }
11
+ /** Minimal interface for telemetry client to avoid circular imports */
12
+ export interface TelemetryClientLike {
13
+ trackToolCalled(toolName: string, durationMs: number, success: boolean): void;
14
+ }
15
+ //# sourceMappingURL=metrics-collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-collector.d.ts","sourceRoot":"","sources":["../../src/analytics/metrics-collector.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,eAAe,CAAoC;gBAE/C,UAAU,EAAE,MAAM;IAI9B,mEAAmE;IACnE,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAIrD,iEAAiE;IAC3D,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBhD;AAED,uEAAuE;AACvE,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/E"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Metrics collector — appends tool call metrics to .twining/metrics.jsonl.
3
+ * Fire-and-forget: never fails a tool call, silently logs on error.
4
+ */
5
+ import path from "node:path";
6
+ import { appendJSONL } from "../storage/file-store.js";
7
+ export class MetricsCollector {
8
+ metricsPath;
9
+ telemetryClient = null;
10
+ constructor(twiningDir) {
11
+ this.metricsPath = path.join(twiningDir, "metrics.jsonl");
12
+ }
13
+ /** Set an optional telemetry client to forward sanitized events */
14
+ setTelemetryClient(client) {
15
+ this.telemetryClient = client;
16
+ }
17
+ /** Record a tool call metric. Fire-and-forget — never throws. */
18
+ async record(entry) {
19
+ try {
20
+ await appendJSONL(this.metricsPath, entry);
21
+ }
22
+ catch (err) {
23
+ console.error("[twining] Metrics write failed (non-fatal):", err.message);
24
+ }
25
+ // Forward to telemetry if configured (sanitized — no args/content)
26
+ if (this.telemetryClient) {
27
+ try {
28
+ this.telemetryClient.trackToolCalled(entry.tool_name, entry.duration_ms, entry.success);
29
+ }
30
+ catch {
31
+ // Silently ignore telemetry failures
32
+ }
33
+ }
34
+ }
35
+ }
36
+ //# sourceMappingURL=metrics-collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-collector.js","sourceRoot":"","sources":["../../src/analytics/metrics-collector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,OAAO,gBAAgB;IACV,WAAW,CAAS;IAC7B,eAAe,GAA+B,IAAI,CAAC;IAE3D,YAAY,UAAkB;QAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IAC5D,CAAC;IAED,mEAAmE;IACnE,kBAAkB,CAAC,MAA2B;QAC5C,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;IAChC,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,MAAM,CAAC,KAAkB;QAC7B,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,6CAA6C,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QACvF,CAAC;QAED,mEAAmE;QACnE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAC1F,CAAC;YAAC,MAAM,CAAC;gBACP,qCAAqC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,20 @@
1
+ import type { ToolUsageSummary, UsageBucket } from "../utils/types.js";
2
+ export declare class MetricsStore {
3
+ private readonly metricsPath;
4
+ private cachedEntries;
5
+ private cachedMtime;
6
+ constructor(twiningDir: string);
7
+ /** Read all metrics, using mtime cache when possible */
8
+ private readAll;
9
+ /** Get tool usage summary, optionally filtered by time */
10
+ getToolUsageSummary(since?: string): Promise<ToolUsageSummary[]>;
11
+ /** Get usage over time in buckets */
12
+ getUsageOverTime(bucketMinutes?: number): Promise<UsageBucket[]>;
13
+ /** Get error breakdown by tool and error code */
14
+ getErrorBreakdown(): Promise<Array<{
15
+ tool_name: string;
16
+ error_code: string;
17
+ count: number;
18
+ }>>;
19
+ }
20
+ //# sourceMappingURL=metrics-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-store.d.ts","sourceRoot":"","sources":["../../src/analytics/metrics-store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGpF,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,WAAW,CAAa;gBAEpB,UAAU,EAAE,MAAM;IAI9B,wDAAwD;YAC1C,OAAO;IAgBrB,0DAA0D;IACpD,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAoCtE,qCAAqC;IAC/B,gBAAgB,CAAC,aAAa,GAAE,MAAW,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAmC1E,iDAAiD;IAC3C,iBAAiB,IAAI,OAAO,CAChC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAChE;CAiBF"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Metrics store — reads metrics.jsonl with mtime caching.
3
+ * Same pattern as BlackboardStore for efficient repeated reads.
4
+ */
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import { readJSONL } from "../storage/file-store.js";
8
+ export class MetricsStore {
9
+ metricsPath;
10
+ cachedEntries = null;
11
+ cachedMtime = 0;
12
+ constructor(twiningDir) {
13
+ this.metricsPath = path.join(twiningDir, "metrics.jsonl");
14
+ }
15
+ /** Read all metrics, using mtime cache when possible */
16
+ async readAll() {
17
+ try {
18
+ if (!fs.existsSync(this.metricsPath))
19
+ return [];
20
+ const stat = fs.statSync(this.metricsPath);
21
+ if (this.cachedEntries !== null && stat.mtimeMs === this.cachedMtime) {
22
+ return this.cachedEntries;
23
+ }
24
+ const entries = await readJSONL(this.metricsPath);
25
+ this.cachedEntries = entries;
26
+ this.cachedMtime = stat.mtimeMs;
27
+ return entries;
28
+ }
29
+ catch {
30
+ return [];
31
+ }
32
+ }
33
+ /** Get tool usage summary, optionally filtered by time */
34
+ async getToolUsageSummary(since) {
35
+ const entries = await this.readAll();
36
+ const filtered = since
37
+ ? entries.filter((e) => e.timestamp >= since)
38
+ : entries;
39
+ const byTool = new Map();
40
+ for (const entry of filtered) {
41
+ const existing = byTool.get(entry.tool_name) || [];
42
+ existing.push(entry);
43
+ byTool.set(entry.tool_name, existing);
44
+ }
45
+ const summaries = [];
46
+ for (const [tool_name, toolEntries] of byTool) {
47
+ const durations = toolEntries.map((e) => e.duration_ms).sort((a, b) => a - b);
48
+ const errorCount = toolEntries.filter((e) => !e.success).length;
49
+ const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
50
+ const p95Index = Math.min(Math.ceil(durations.length * 0.95) - 1, durations.length - 1);
51
+ summaries.push({
52
+ tool_name,
53
+ call_count: toolEntries.length,
54
+ error_count: errorCount,
55
+ avg_duration_ms: Math.round(avgDuration),
56
+ p95_duration_ms: durations[p95Index],
57
+ last_called: toolEntries[toolEntries.length - 1].timestamp,
58
+ });
59
+ }
60
+ return summaries.sort((a, b) => b.call_count - a.call_count);
61
+ }
62
+ /** Get usage over time in buckets */
63
+ async getUsageOverTime(bucketMinutes = 60) {
64
+ const entries = await this.readAll();
65
+ if (entries.length === 0)
66
+ return [];
67
+ const bucketMs = bucketMinutes * 60 * 1000;
68
+ const buckets = new Map();
69
+ for (const entry of entries) {
70
+ const ts = new Date(entry.timestamp).getTime();
71
+ const bucketStart = Math.floor(ts / bucketMs) * bucketMs;
72
+ const existing = buckets.get(bucketStart) || [];
73
+ existing.push(entry);
74
+ buckets.set(bucketStart, existing);
75
+ }
76
+ const result = [];
77
+ const sortedKeys = [...buckets.keys()].sort((a, b) => a - b);
78
+ for (const key of sortedKeys) {
79
+ const bucketEntries = buckets.get(key);
80
+ const durations = bucketEntries.map((e) => e.duration_ms);
81
+ const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
82
+ result.push({
83
+ bucket_start: new Date(key).toISOString(),
84
+ bucket_end: new Date(key + bucketMs).toISOString(),
85
+ call_count: bucketEntries.length,
86
+ error_count: bucketEntries.filter((e) => !e.success).length,
87
+ avg_duration_ms: Math.round(avgDuration),
88
+ });
89
+ }
90
+ return result;
91
+ }
92
+ /** Get error breakdown by tool and error code */
93
+ async getErrorBreakdown() {
94
+ const entries = await this.readAll();
95
+ const errors = entries.filter((e) => !e.success);
96
+ const byKey = new Map();
97
+ for (const entry of errors) {
98
+ const key = `${entry.tool_name}::${entry.error_code || "unknown"}`;
99
+ byKey.set(key, (byKey.get(key) || 0) + 1);
100
+ }
101
+ return [...byKey.entries()]
102
+ .map(([key, count]) => {
103
+ const [tool_name, error_code] = key.split("::");
104
+ return { tool_name: tool_name, error_code: error_code, count };
105
+ })
106
+ .sort((a, b) => b.count - a.count);
107
+ }
108
+ }
109
+ //# sourceMappingURL=metrics-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-store.js","sourceRoot":"","sources":["../../src/analytics/metrics-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { AnalyticsConfig } from "../utils/types.js";
2
+ export declare class TelemetryClient {
3
+ private client;
4
+ private distinctId;
5
+ private enabled;
6
+ /** Initialize telemetry. Returns true if enabled and ready. */
7
+ init(config: AnalyticsConfig | undefined, projectRoot: string, version: string): Promise<boolean>;
8
+ /** Track server started event */
9
+ trackServerStarted(version: string): void;
10
+ /** Track a tool call (sanitized — no args/content/errors) */
11
+ trackToolCalled(toolName: string, durationMs: number, success: boolean): void;
12
+ /** Track session summary (periodic aggregate) */
13
+ trackSessionSummary(callCountsByTool: Record<string, number>, decisionCount: number, entityCount: number): void;
14
+ /** Flush and shut down */
15
+ shutdown(): Promise<void>;
16
+ /** Whether telemetry is active */
17
+ isEnabled(): boolean;
18
+ }
19
+ //# sourceMappingURL=telemetry-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-client.d.ts","sourceRoot":"","sources":["../../src/analytics/telemetry-client.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAczD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,OAAO,CAAkB;IAEjC,+DAA+D;IACzD,IAAI,CACR,MAAM,EAAE,eAAe,GAAG,SAAS,EACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC;IA2CnB,iCAAiC;IACjC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAkBzC,6DAA6D;IAC7D,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAiB7E,iDAAiD;IACjD,mBAAmB,CACjB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACxC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,IAAI;IAiBP,0BAA0B;IACpB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAS/B,kCAAkC;IAClC,SAAS,IAAI,OAAO;CAGrB"}