@amitdeshmukh/ax-crew 8.6.0 → 8.7.0

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.
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: ax-crew-functions
3
+ version: __VERSION__
4
+ description: "Functions and tools: FunctionRegistryType, AxFunction, toFunction, custom functions, AxCrewFunctions, class-based functions with state"
5
+ ---
6
+
7
+ # Functions
8
+
9
+ Agents call tools via a `FunctionRegistryType` -- a map of function names to either plain `AxFunction` objects or class-based constructors that receive shared state.
10
+
11
+ ## Two Patterns
12
+
13
+ ### Object-based (plain AxFunction)
14
+
15
+ ```ts
16
+ import type { AxFunction } from '@ax-llm/ax';
17
+
18
+ const MyTool: AxFunction = {
19
+ name: 'MyTool',
20
+ description: 'Does something useful',
21
+ parameters: {
22
+ type: 'object',
23
+ properties: {
24
+ input: { type: 'string', description: 'The input value' }
25
+ },
26
+ required: ['input']
27
+ },
28
+ func: ({ input }: { input: string }) => {
29
+ return `Processed: ${input}`;
30
+ }
31
+ };
32
+ ```
33
+
34
+ ### Class-based (with state access)
35
+
36
+ Constructor receives shared state. Must implement `toFunction()` returning an `AxFunction`.
37
+
38
+ ```ts
39
+ import type { AxFunction } from '@ax-llm/ax';
40
+
41
+ class WordPressPost {
42
+ private state: Record<string, any>;
43
+
44
+ constructor(state: Record<string, any>) {
45
+ this.state = state;
46
+ }
47
+
48
+ toFunction(): AxFunction {
49
+ return {
50
+ name: 'WordPressPost',
51
+ description: 'Creates a post on WordPress',
52
+ parameters: {
53
+ type: 'object',
54
+ properties: {
55
+ title: { type: 'string', description: 'Post title' },
56
+ content: { type: 'string', description: 'Post content' },
57
+ status: { type: 'string', description: 'Post status (draft, publish, private)' }
58
+ },
59
+ required: ['title', 'content', 'status']
60
+ },
61
+ func: async ({ title, content, status }: { title: string; content: string; status: string }) => {
62
+ const env = this.state.env || {};
63
+ const url = env.WORDPRESS_URL;
64
+ const username = env.WORDPRESS_USERNAME;
65
+ const password = env.WORDPRESS_PASSWORD;
66
+ // ... make API call using credentials from state
67
+ return { id: 123, link: `${url}/?p=123` };
68
+ }
69
+ };
70
+ }
71
+ }
72
+ ```
73
+
74
+ ## FunctionRegistryType
75
+
76
+ ```ts
77
+ type FunctionRegistryType = {
78
+ [key: string]: AxFunction | { new(state: Record<string, any>): { toFunction: () => AxFunction } };
79
+ };
80
+ ```
81
+
82
+ The registry key must match the name used in `AgentConfig.functions[]`.
83
+
84
+ ## Built-in AxCrewFunctions
85
+
86
+ ```ts
87
+ import { AxCrewFunctions } from '@amitdeshmukh/ax-crew';
88
+ // Contains: { CurrentDateTime, DaysBetweenDates }
89
+ ```
90
+
91
+ **CurrentDateTime** -- returns current date/time in `iso`, `datetime`, or `date` format.
92
+
93
+ **DaysBetweenDates** -- calculates days between two ISO date strings. Parameters: `startDate`, `endDate`.
94
+
95
+ ## Merging Registries
96
+
97
+ ```ts
98
+ import { AxCrew, AxCrewFunctions } from '@amitdeshmukh/ax-crew';
99
+ import type { FunctionRegistryType } from '@amitdeshmukh/ax-crew';
100
+
101
+ const myFunctions: FunctionRegistryType = {
102
+ MyTool: MyTool,
103
+ WordPressPost: WordPressPost, // class-based
104
+ };
105
+
106
+ // Merge built-in + custom
107
+ const crew = new AxCrew(config, { ...AxCrewFunctions, ...myFunctions });
108
+ ```
109
+
110
+ Then reference by name in agent config:
111
+
112
+ ```ts
113
+ {
114
+ name: "poster",
115
+ functions: ["CurrentDateTime", "WordPressPost"],
116
+ // ...
117
+ }
118
+ ```
119
+
120
+ ## Canonical Pattern
121
+
122
+ Full runnable example adapted from the WordPress example:
123
+
124
+ ```ts
125
+ import { AxCrew } from '@amitdeshmukh/ax-crew';
126
+ import type { AxCrewConfig, FunctionRegistryType } from '@amitdeshmukh/ax-crew';
127
+ import type { AxFunction } from '@ax-llm/ax';
128
+ import dotenv from 'dotenv';
129
+ dotenv.config();
130
+
131
+ // Plain AxFunction
132
+ const Summarize: AxFunction = {
133
+ name: 'Summarize',
134
+ description: 'Summarize text to a given length',
135
+ parameters: {
136
+ type: 'object',
137
+ properties: {
138
+ text: { type: 'string', description: 'Text to summarize' },
139
+ maxWords: { type: 'number', description: 'Maximum words' }
140
+ },
141
+ required: ['text']
142
+ },
143
+ func: ({ text, maxWords }: { text: string; maxWords?: number }) => {
144
+ const limit = maxWords ?? 50;
145
+ return text.split(' ').slice(0, limit).join(' ') + '...';
146
+ }
147
+ };
148
+
149
+ // Class-based function with state access
150
+ class FetchFromAPI {
151
+ private state: Record<string, any>;
152
+ constructor(state: Record<string, any>) { this.state = state; }
153
+ toFunction(): AxFunction {
154
+ return {
155
+ name: 'FetchFromAPI',
156
+ description: 'Fetch data from a configured API endpoint',
157
+ parameters: {
158
+ type: 'object',
159
+ properties: {
160
+ endpoint: { type: 'string', description: 'API endpoint path' }
161
+ },
162
+ required: ['endpoint']
163
+ },
164
+ func: async ({ endpoint }: { endpoint: string }) => {
165
+ const baseUrl = this.state.env?.API_BASE_URL || 'https://api.example.com';
166
+ const resp = await fetch(`${baseUrl}${endpoint}`);
167
+ return await resp.json();
168
+ }
169
+ };
170
+ }
171
+ }
172
+
173
+ const config: AxCrewConfig = {
174
+ crew: [
175
+ {
176
+ name: "assistant",
177
+ description: "An assistant that can summarize text and fetch data",
178
+ signature: "request:string -> response:string",
179
+ provider: "openai",
180
+ providerKeyName: "OPENAI_API_KEY",
181
+ ai: { model: "gpt-4o-mini", temperature: 0.5 },
182
+ functions: ["Summarize", "FetchFromAPI"],
183
+ }
184
+ ]
185
+ };
186
+
187
+ async function main() {
188
+ const customFunctions: FunctionRegistryType = {
189
+ Summarize,
190
+ FetchFromAPI,
191
+ };
192
+ const crew = new AxCrew(config, customFunctions);
193
+
194
+ // Set state for class-based functions
195
+ crew.state.set("env", { API_BASE_URL: "https://api.example.com" });
196
+
197
+ await crew.addAllAgents();
198
+ const assistant = crew.agents?.get("assistant");
199
+ const result = await assistant?.forward({ request: "Summarize the latest news" });
200
+ console.log(result?.response);
201
+ crew.destroy();
202
+ }
203
+
204
+ main().catch(console.error);
205
+ ```
206
+
207
+ ## Do Not Generate
208
+
209
+ - Do NOT define functions inline in AgentConfig; always use a `FunctionRegistryType` registry passed to the `AxCrew` constructor.
210
+ - Do NOT forget that class-based function constructors receive `state: Record<string, any>`, not `StateInstance`. Access values directly (e.g. `this.state.env`), since the state object is a plain record populated via `crew.state.set()`.
211
+ - Do NOT use a registry key that differs from the function name used in `AgentConfig.functions[]` -- they must match.
212
+ - Do NOT import `AxCrewFunctions` from `@ax-llm/ax`; import from `@amitdeshmukh/ax-crew`.
213
+
214
+ ## References
215
+
216
+ - [write-post-and-publish-to-wordpress.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/write-post-and-publish-to-wordpress.ts)
217
+ - [src/functions/dateTime.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/functions/dateTime.ts)
218
+ - [src/functions/index.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/functions/index.ts)
@@ -0,0 +1,221 @@
1
+ ---
2
+ name: ax-crew-mcp
3
+ version: "__VERSION__"
4
+ description: "ax-crew MCP integration: MCP, Model Context Protocol, STDIO, HTTP SSE, Streamable HTTP, mcpServers, tools, tool filtering, multiple servers"
5
+ argument-hint: [topic]
6
+ allowed-tools: Read, Grep, Glob
7
+ ---
8
+
9
+ # ax-crew MCP (Model Context Protocol)
10
+
11
+ MCP servers expose external tools to agents. Configured per-agent via `mcpServers` in the agent config. Transport type is auto-detected by config shape.
12
+
13
+ ## Three Transport Types
14
+
15
+ ### STDIO (local process)
16
+
17
+ ```typescript
18
+ mcpServers: {
19
+ "context7": {
20
+ command: "npx", // required
21
+ args: ["-y", "@upstash/context7-mcp"], // optional
22
+ env: { API_KEY: "..." }, // optional
23
+ tools: ["resolve-library-id", "query-docs"], // optional allowlist
24
+ }
25
+ }
26
+ ```
27
+
28
+ Type: `MCPStdioTransportConfig = { command: string; args?: string[]; env?: NodeJS.ProcessEnv; tools?: string[] }`
29
+
30
+ ### HTTP SSE (remote, server-sent events)
31
+
32
+ ```typescript
33
+ mcpServers: {
34
+ "api-server": {
35
+ sseUrl: "https://api.example.com/mcp/sse", // required
36
+ tools: ["search", "fetch"], // optional allowlist
37
+ }
38
+ }
39
+ ```
40
+
41
+ Type: `MCPHTTPSSETransportConfig = { sseUrl: string; tools?: string[] }`
42
+
43
+ ### Streamable HTTP (bidirectional)
44
+
45
+ ```typescript
46
+ mcpServers: {
47
+ "graphjin": {
48
+ mcpEndpoint: "http://localhost:8080/api/v1/mcp", // required
49
+ options: { timeout: 30000 }, // optional AxMCPStreamableHTTPTransportOptions
50
+ tools: ["list_workflows", "execute_workflow"], // optional allowlist
51
+ }
52
+ }
53
+ ```
54
+
55
+ Type: `MCPStreamableHTTPTransportConfig = { mcpEndpoint: string; options?: AxMCPStreamableHTTPTransportOptions; tools?: string[] }`
56
+
57
+ ## tools[] Allowlist
58
+
59
+ When `tools` is specified, only those MCP tool names are exposed to the agent. This reduces token usage and prevents the agent from calling unwanted tools.
60
+
61
+ ```typescript
62
+ mcpServers: {
63
+ "big-server": {
64
+ command: "npx",
65
+ args: ["-y", "some-mcp-server"],
66
+ tools: ["only_this_tool", "and_this_one"], // filter from all available
67
+ }
68
+ }
69
+ ```
70
+
71
+ If `tools` is omitted or empty, all tools from the MCP server are exposed.
72
+
73
+ ## Multiple MCP Servers Per Agent
74
+
75
+ An agent can connect to multiple MCP servers simultaneously. All tools are merged:
76
+
77
+ ```typescript
78
+ {
79
+ name: "MultiToolAgent",
80
+ description: "Agent with multiple MCP servers",
81
+ signature: 'query:string -> answer:string',
82
+ provider: "google-gemini",
83
+ providerKeyName: "GEMINI_API_KEY",
84
+ ai: { model: "gemini-2.5-pro", temperature: 0 },
85
+ mcpServers: {
86
+ "filesystem": {
87
+ command: "npx",
88
+ args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
89
+ },
90
+ "database": {
91
+ mcpEndpoint: "http://localhost:8080/api/v1/mcp",
92
+ tools: ["query", "describe_table"],
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## Canonical Pattern
99
+
100
+ ### STDIO transport (from mcp-agent.ts)
101
+
102
+ ```typescript
103
+ import { AxCrew } from '@amitdeshmukh/ax-crew';
104
+ import type { AxCrewConfig } from '@amitdeshmukh/ax-crew';
105
+
106
+ const config: AxCrewConfig = {
107
+ crew: [
108
+ {
109
+ name: "Context7DocsAgent",
110
+ description: "Agent with access to Context7 Docs APIs",
111
+ signature: 'apiDocQuery:string "a question" -> apiDocAnswer:string "the answer"',
112
+ provider: "google-gemini",
113
+ providerKeyName: "GEMINI_API_KEY",
114
+ ai: { model: "gemini-2.5-pro", temperature: 0 },
115
+ options: { debug: true },
116
+ mcpServers: {
117
+ "context7": {
118
+ command: "npx",
119
+ args: ["-y", "@upstash/context7-mcp", "--api-key", process.env.CONTEXT7_API_KEY!],
120
+ },
121
+ },
122
+ },
123
+ {
124
+ name: "ManagerAgent",
125
+ description: "Orchestrates sub-agents",
126
+ signature: 'question:string -> answer:string',
127
+ provider: "google-gemini",
128
+ providerKeyName: "GEMINI_API_KEY",
129
+ ai: { model: "gemini-2.5-pro", maxTokens: 1000, temperature: 0 },
130
+ agents: ["Context7DocsAgent"],
131
+ },
132
+ ],
133
+ };
134
+
135
+ async function main() {
136
+ const crew = new AxCrew(config);
137
+ try {
138
+ await crew.addAllAgents();
139
+
140
+ const manager = crew.agents?.get("ManagerAgent");
141
+ if (!manager) throw new Error("Agent not found");
142
+
143
+ const result = await manager.forward({
144
+ question: "How do I configure MCP servers in ax-crew?",
145
+ });
146
+ console.log("Answer:", result.answer);
147
+ } finally {
148
+ crew.destroy();
149
+ }
150
+ }
151
+
152
+ main().catch(console.error);
153
+ ```
154
+
155
+ ### Streamable HTTP transport (from graphjin-database-agent.ts)
156
+
157
+ ```typescript
158
+ import { AxCrew } from '@amitdeshmukh/ax-crew';
159
+ import type { AxCrewConfig } from '@amitdeshmukh/ax-crew';
160
+
161
+ const config: AxCrewConfig = {
162
+ crew: [
163
+ {
164
+ name: "DatabaseAgent",
165
+ description: "Agent with direct database access via GraphJin",
166
+ signature: 'dbQuery:string "a database question" -> dbResult:string "the result"',
167
+ provider: "google-gemini",
168
+ providerKeyName: "GEMINI_API_KEY",
169
+ ai: { model: "gemini-2.5-pro", temperature: 0 },
170
+ mcpServers: {
171
+ "graphjin": {
172
+ command: "graphjin",
173
+ args: ["mcp", "--server", "http://localhost:8080"],
174
+ },
175
+ },
176
+ },
177
+ {
178
+ name: "ManagerAgent",
179
+ description: "Orchestrates database queries",
180
+ signature: 'question:string -> answer:string',
181
+ provider: "google-gemini",
182
+ providerKeyName: "GEMINI_API_KEY",
183
+ ai: { model: "gemini-2.5-pro", maxTokens: 2000, temperature: 0 },
184
+ agents: ["DatabaseAgent"],
185
+ },
186
+ ],
187
+ };
188
+
189
+ async function main() {
190
+ const crew = new AxCrew(config);
191
+ try {
192
+ await crew.addAllAgents();
193
+ const manager = crew.agents?.get("ManagerAgent");
194
+ if (!manager) throw new Error("Agent not found");
195
+
196
+ const result = await manager.forward({
197
+ question: "What tables are available in the database?",
198
+ });
199
+ console.log("Answer:", result.answer);
200
+ } finally {
201
+ crew.destroy();
202
+ }
203
+ }
204
+
205
+ main().catch(console.error);
206
+ ```
207
+
208
+ ## Do Not Generate
209
+
210
+ - Do NOT mix transport config keys -- use exactly one of `command`, `sseUrl`, or `mcpEndpoint` per server entry.
211
+ - Do NOT forget `crew.destroy()` -- MCP STDIO processes must be cleaned up.
212
+ - Do NOT put `mcpServers` at the crew level -- it is per-agent only.
213
+ - Do NOT assume MCP tools have `parameters` -- some zero-arg tools omit it; ax-crew normalizes this automatically.
214
+ - Do NOT use `AxMCPStdioTransport` directly -- ax-crew handles transport creation internally from config.
215
+
216
+ ## References
217
+
218
+ - [mcp-agent.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/mcp-agent.ts)
219
+ - [graphjin-database-agent.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/graphjin-database-agent.ts)
220
+ - [Source: agentConfig.ts (initializeMCPServers)](https://github.com/amitdeshmukh/ax-crew/blob/main/src/agents/agentConfig.ts)
221
+ - [Types: MCPStdioTransportConfig, MCPHTTPSSETransportConfig, MCPStreamableHTTPTransportConfig](https://github.com/amitdeshmukh/ax-crew/blob/main/src/types.ts)
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: ax-crew-metrics
3
+ version: "__VERSION__"
4
+ description: "ax-crew metrics and cost tracking: metrics, cost, tracking, getMetrics, getCrewMetrics, MetricsSnapshot, usage, tokens, estimatedCostUSD, resetCosts, resetCrewMetrics"
5
+ argument-hint: [topic]
6
+ allowed-tools: Read, Grep, Glob
7
+ ---
8
+
9
+ # ax-crew Metrics & Cost Tracking
10
+
11
+ ## Per-Agent: agent.getMetrics()
12
+
13
+ Returns a `MetricsSnapshot` scoped to this agent within its crew.
14
+
15
+ ```typescript
16
+ const agent = crew.agents?.get("MyAgent");
17
+ const metrics = agent.getMetrics();
18
+ ```
19
+
20
+ ## Crew-Level: crew.getCrewMetrics()
21
+
22
+ Returns a `MetricsSnapshot` aggregated across all agents in the crew.
23
+
24
+ ```typescript
25
+ const crewMetrics = crew.getCrewMetrics();
26
+ ```
27
+
28
+ ## MetricsSnapshot Shape
29
+
30
+ ```typescript
31
+ interface MetricsSnapshot {
32
+ provider?: string;
33
+ model?: string;
34
+ requests: {
35
+ totalRequests: number;
36
+ totalErrors: number;
37
+ errorRate: number; // errors / requests
38
+ totalStreamingRequests: number;
39
+ durationMsSum: number;
40
+ durationCount: number;
41
+ };
42
+ tokens: {
43
+ promptTokens: number;
44
+ completionTokens: number;
45
+ totalTokens?: number;
46
+ };
47
+ estimatedCostUSD: number; // rounded to 5 decimal places
48
+ functions: {
49
+ totalFunctionCalls: number;
50
+ totalFunctionLatencyMs: number;
51
+ details?: Array<{ // per-function breakdown
52
+ name: string;
53
+ calls: number;
54
+ totalLatencyMs: number;
55
+ }>;
56
+ };
57
+ }
58
+ ```
59
+
60
+ ## Reset Methods
61
+
62
+ ```typescript
63
+ // Reset all cost/usage tracking for the entire crew (resets agent usage + metrics)
64
+ crew.resetCosts();
65
+
66
+ // Reset only the metrics registry for the crew
67
+ crew.resetCrewMetrics();
68
+ ```
69
+
70
+ `resetCosts()` calls `resetUsage()` and `resetMetrics()` on each agent, then clears crew-level metrics.
71
+ `resetCrewMetrics()` only clears the MetricsRegistry entries for this crew.
72
+
73
+ ## Canonical Pattern
74
+
75
+ ```typescript
76
+ import { AxCrew, AxCrewFunctions } from '@amitdeshmukh/ax-crew';
77
+ import type { AxCrewConfig } from '@amitdeshmukh/ax-crew';
78
+
79
+ const config: AxCrewConfig = {
80
+ crew: [
81
+ {
82
+ name: "researcher",
83
+ description: "A research agent that finds information",
84
+ signature: "query:string -> research:string",
85
+ provider: "google-gemini",
86
+ providerKeyName: "GEMINI_API_KEY",
87
+ ai: { model: "gemini-2.5-flash-lite", maxTokens: 4000, stream: true },
88
+ functions: ["CurrentDateTime"],
89
+ },
90
+ {
91
+ name: "writer",
92
+ description: "A writing agent that creates content",
93
+ signature: "topic:string -> article:string",
94
+ provider: "google-gemini",
95
+ providerKeyName: "GEMINI_API_KEY",
96
+ ai: { model: "gemini-2.5-flash-lite", maxTokens: 4000, stream: true },
97
+ agents: ["researcher"],
98
+ },
99
+ ],
100
+ };
101
+
102
+ async function main() {
103
+ const crew = new AxCrew(config, AxCrewFunctions);
104
+ await crew.addAgentsToCrew(["researcher"]);
105
+ await crew.addAgentsToCrew(["writer"]);
106
+
107
+ const writer = crew.agents?.get("writer");
108
+ const researcher = crew.agents?.get("researcher");
109
+ if (!writer || !researcher) throw new Error("Failed to initialize agents");
110
+
111
+ try {
112
+ const { article } = await writer.forward({
113
+ topic: "Quantum Computing Benefits",
114
+ });
115
+ console.log("Article:", article);
116
+
117
+ // Per-agent metrics
118
+ console.log("Writer Metrics:", JSON.stringify(writer.getMetrics?.(), null, 2));
119
+ console.log("Researcher Metrics:", JSON.stringify(researcher.getMetrics?.(), null, 2));
120
+
121
+ // Crew-wide aggregate
122
+ console.log("Crew Metrics:", JSON.stringify(crew.getCrewMetrics(), null, 2));
123
+
124
+ // Reset for next measurement period
125
+ crew.resetCosts();
126
+ } finally {
127
+ crew.destroy();
128
+ }
129
+ }
130
+
131
+ main().catch(console.error);
132
+ ```
133
+
134
+ ## Accessing Specific Fields
135
+
136
+ ```typescript
137
+ const m = agent.getMetrics();
138
+
139
+ // Cost
140
+ console.log(`Estimated USD: ${m?.estimatedCostUSD ?? 0}`);
141
+
142
+ // Tokens
143
+ console.log(`Prompt: ${m?.tokens?.promptTokens}, Completion: ${m?.tokens?.completionTokens}, Total: ${m?.tokens?.totalTokens}`);
144
+
145
+ // Request stats
146
+ console.log(`Requests: ${m?.requests?.totalRequests}, Errors: ${m?.requests?.totalErrors}`);
147
+
148
+ // Function calls
149
+ console.log(`Function calls: ${m?.functions?.totalFunctionCalls}`);
150
+ if (m?.functions?.details) {
151
+ for (const fn of m.functions.details) {
152
+ console.log(` ${fn.name}: ${fn.calls} calls, ${fn.totalLatencyMs}ms`);
153
+ }
154
+ }
155
+ ```
156
+
157
+ ## Do Not Generate
158
+
159
+ - Do NOT call `getMetrics()` on the crew object -- use `crew.getCrewMetrics()` for crew-level and `agent.getMetrics()` for per-agent.
160
+ - Do NOT confuse `resetCosts()` with `resetCrewMetrics()` -- `resetCosts()` is broader (resets agent usage too).
161
+ - Do NOT expect `getMetrics()` to return cost data from the legacy `getUsage()` / `getCosts()` API -- those are separate.
162
+ - Do NOT assume `functions.details` is always present -- it is `undefined` when no function calls have been made.
163
+ - Do NOT forget that `estimatedCostUSD` is an estimate based on tracked token counts and model pricing.
164
+
165
+ ## References
166
+
167
+ - [basic-researcher-writer.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/basic-researcher-writer.ts)
168
+ - [rlm-long-task.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/rlm-long-task.ts)
169
+ - [Source: MetricsSnapshot type](https://github.com/amitdeshmukh/ax-crew/blob/main/src/metrics/types.ts)
170
+ - [Source: MetricsRegistry](https://github.com/amitdeshmukh/ax-crew/blob/main/src/metrics/registry.ts)