@esankhan3/anvil-agent-core 0.1.2 → 0.2.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.
Files changed (93) hide show
  1. package/dist/__tests__/mcp-merged-executor.test.d.ts +2 -0
  2. package/dist/__tests__/mcp-merged-executor.test.d.ts.map +1 -0
  3. package/dist/__tests__/mcp-merged-executor.test.js +221 -0
  4. package/dist/__tests__/mcp-merged-executor.test.js.map +1 -0
  5. package/dist/__tests__/mcp.test.js +21 -15
  6. package/dist/__tests__/mcp.test.js.map +1 -1
  7. package/dist/__tests__/model-catalog.test.d.ts +11 -0
  8. package/dist/__tests__/model-catalog.test.d.ts.map +1 -0
  9. package/dist/__tests__/model-catalog.test.js +102 -0
  10. package/dist/__tests__/model-catalog.test.js.map +1 -0
  11. package/dist/__tests__/model-tier-resolver.test.d.ts +19 -0
  12. package/dist/__tests__/model-tier-resolver.test.d.ts.map +1 -0
  13. package/dist/__tests__/model-tier-resolver.test.js +97 -0
  14. package/dist/__tests__/model-tier-resolver.test.js.map +1 -0
  15. package/dist/adk-adapter.d.ts.map +1 -1
  16. package/dist/adk-adapter.js +11 -0
  17. package/dist/adk-adapter.js.map +1 -1
  18. package/dist/agent/session/__tests__/spawn-telemetry.test.js +39 -4
  19. package/dist/agent/session/__tests__/spawn-telemetry.test.js.map +1 -1
  20. package/dist/agent/session/adapter.d.ts +34 -0
  21. package/dist/agent/session/adapter.d.ts.map +1 -1
  22. package/dist/agent/session/adapter.js.map +1 -1
  23. package/dist/agent/session/default-adapter-factory.d.ts.map +1 -1
  24. package/dist/agent/session/default-adapter-factory.js +66 -2
  25. package/dist/agent/session/default-adapter-factory.js.map +1 -1
  26. package/dist/agent/session/language-model-bridge.d.ts.map +1 -1
  27. package/dist/agent/session/language-model-bridge.js +52 -1
  28. package/dist/agent/session/language-model-bridge.js.map +1 -1
  29. package/dist/agent/session/session.d.ts +16 -0
  30. package/dist/agent/session/session.d.ts.map +1 -1
  31. package/dist/agent/session/session.js +86 -2
  32. package/dist/agent/session/session.js.map +1 -1
  33. package/dist/gemini-adapter.d.ts.map +1 -1
  34. package/dist/gemini-adapter.js +11 -0
  35. package/dist/gemini-adapter.js.map +1 -1
  36. package/dist/gemini-cli-adapter.d.ts.map +1 -1
  37. package/dist/gemini-cli-adapter.js +10 -0
  38. package/dist/gemini-cli-adapter.js.map +1 -1
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +6 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/mcp/client.d.ts +64 -18
  44. package/dist/mcp/client.d.ts.map +1 -1
  45. package/dist/mcp/client.js +190 -26
  46. package/dist/mcp/client.js.map +1 -1
  47. package/dist/mcp/index.d.ts +5 -2
  48. package/dist/mcp/index.d.ts.map +1 -1
  49. package/dist/mcp/index.js +3 -8
  50. package/dist/mcp/index.js.map +1 -1
  51. package/dist/mcp/merged-executor.d.ts +74 -0
  52. package/dist/mcp/merged-executor.d.ts.map +1 -0
  53. package/dist/mcp/merged-executor.js +198 -0
  54. package/dist/mcp/merged-executor.js.map +1 -0
  55. package/dist/mcp/pool.d.ts +71 -0
  56. package/dist/mcp/pool.d.ts.map +1 -0
  57. package/dist/mcp/pool.js +171 -0
  58. package/dist/mcp/pool.js.map +1 -0
  59. package/dist/mcp/tool-merger.d.ts +8 -5
  60. package/dist/mcp/tool-merger.d.ts.map +1 -1
  61. package/dist/mcp/tool-merger.js +8 -16
  62. package/dist/mcp/tool-merger.js.map +1 -1
  63. package/dist/model-catalog.d.ts +36 -0
  64. package/dist/model-catalog.d.ts.map +1 -0
  65. package/dist/model-catalog.js +102 -0
  66. package/dist/model-catalog.js.map +1 -0
  67. package/dist/model-tier-resolver.d.ts +61 -0
  68. package/dist/model-tier-resolver.d.ts.map +1 -0
  69. package/dist/model-tier-resolver.js +170 -0
  70. package/dist/model-tier-resolver.js.map +1 -0
  71. package/dist/ollama-adapter.d.ts.map +1 -1
  72. package/dist/ollama-adapter.js +6 -1
  73. package/dist/ollama-adapter.js.map +1 -1
  74. package/dist/opencode-adapter.d.ts +11 -0
  75. package/dist/opencode-adapter.d.ts.map +1 -1
  76. package/dist/opencode-adapter.js +13 -0
  77. package/dist/opencode-adapter.js.map +1 -1
  78. package/dist/openrouter-adapter.d.ts +15 -0
  79. package/dist/openrouter-adapter.d.ts.map +1 -1
  80. package/dist/openrouter-adapter.js +69 -19
  81. package/dist/openrouter-adapter.js.map +1 -1
  82. package/dist/provider-liveness.d.ts +5 -0
  83. package/dist/provider-liveness.d.ts.map +1 -1
  84. package/dist/provider-liveness.js +11 -15
  85. package/dist/provider-liveness.js.map +1 -1
  86. package/dist/registry.d.ts +1 -0
  87. package/dist/registry.d.ts.map +1 -1
  88. package/dist/registry.js +1 -1
  89. package/dist/registry.js.map +1 -1
  90. package/dist/upstream-error.d.ts.map +1 -1
  91. package/dist/upstream-error.js +11 -1
  92. package/dist/upstream-error.js.map +1 -1
  93. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `McpClientPool` — workspace-scoped, session-lifetime pool of
3
+ * `McpAgentClient`s.
4
+ *
5
+ * Lifecycle owned by `AgentProcess` so resume turns reuse already-connected
6
+ * servers instead of re-paying the 250–1000 ms connect cost per turn. Each
7
+ * client is lazy-connected on first need; failures are logged + that server
8
+ * is dropped from the pool (not propagated as a spawn failure — one sick
9
+ * MCP shouldn't poison the run).
10
+ *
11
+ * Reconnect-on-EOF: if a stdio server crashes mid-session, the next tool
12
+ * call attempts to reconnect once before surfacing the error to the model.
13
+ */
14
+ import { type McpToolDescriptor, type McpProgressEvent } from './client.js';
15
+ import { loadMcpServers } from './config-loader.js';
16
+ import type { McpServerConfig } from './types.js';
17
+ export interface McpClientPoolOpts {
18
+ /** Workspace root used to resolve `mcp.json` discovery. */
19
+ workspaceRoot?: string;
20
+ /** Pre-resolved server configs — bypasses `loadMcpServers`. Test seam. */
21
+ servers?: McpServerConfig[];
22
+ /** Per-call deadline for `connect()` on each server. Defaults to 5 s. */
23
+ connectTimeoutMs?: number;
24
+ /** Optional run id forwarded to each client for stderr log naming. */
25
+ runId?: string;
26
+ /** Forwarded to every client constructed by the pool. */
27
+ onProgress?: (ev: McpProgressEvent) => void;
28
+ /** Test seam — override the loader. */
29
+ loader?: typeof loadMcpServers;
30
+ }
31
+ export declare class McpClientPool {
32
+ private readonly opts;
33
+ private readonly clients;
34
+ /** Tools discovered + cached. Refreshed on first call after reconnect. */
35
+ private toolsCache;
36
+ private discovering;
37
+ private closed;
38
+ /** Names of servers that failed to connect + the reason — surfaced to
39
+ * diagnostics so the dashboard activity panel can show "github-mcp
40
+ * failed to connect: ECONNREFUSED". */
41
+ readonly failures: Array<{
42
+ server: string;
43
+ reason: string;
44
+ }>;
45
+ constructor(opts: McpClientPoolOpts);
46
+ private loadConfig;
47
+ /** True iff at least one server is configured (regardless of liveness). */
48
+ hasServers(): boolean;
49
+ /**
50
+ * Connect every configured server in parallel, with a per-server
51
+ * timeout. Servers that fail to connect are removed from the pool and
52
+ * recorded in `failures` — they cannot serve tools but the run continues.
53
+ * Returns the union of tool descriptors from connected servers.
54
+ */
55
+ discoverTools(): Promise<McpToolDescriptor[]>;
56
+ private _discoverTools;
57
+ /**
58
+ * Dispatch a namespaced (`mcp__<server>__<tool>`) call. Returns the
59
+ * SDK's raw result for the merged executor to flatten. Throws when the
60
+ * server isn't connected and reconnect fails — caller wraps as a tool
61
+ * error so the model can recover.
62
+ */
63
+ callTool(toolName: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>;
64
+ /** Abort every in-flight tool call across every client. */
65
+ cancelInFlight(reason?: string): void;
66
+ /** Close every client. Idempotent. */
67
+ close(): Promise<void>;
68
+ /** Find the owning server name for a `mcp__<server>__<tool>` id. */
69
+ private findOwner;
70
+ }
71
+ //# sourceMappingURL=pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/mcp/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAkB,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,cAAc,EAA8B,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,cAAc,CAAC;CAChC;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,0EAA0E;IAC1E,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,WAAW,CAA6C;IAChE,OAAO,CAAC,MAAM,CAAS;IACvB;;4CAEwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;gBAEtD,IAAI,EAAE,iBAAiB;IAcnC,OAAO,CAAC,UAAU;IAWlB,2EAA2E;IAC3E,UAAU,IAAI,OAAO;IAIrB;;;;;OAKG;IACG,aAAa,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;YAQrC,cAAc;IA8B5B;;;;;OAKG;IACG,QAAQ,CACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC;IA8BnB,2DAA2D;IAC3D,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAMrC,sCAAsC;IAChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,oEAAoE;IACpE,OAAO,CAAC,SAAS;CAUlB"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * `McpClientPool` — workspace-scoped, session-lifetime pool of
3
+ * `McpAgentClient`s.
4
+ *
5
+ * Lifecycle owned by `AgentProcess` so resume turns reuse already-connected
6
+ * servers instead of re-paying the 250–1000 ms connect cost per turn. Each
7
+ * client is lazy-connected on first need; failures are logged + that server
8
+ * is dropped from the pool (not propagated as a spawn failure — one sick
9
+ * MCP shouldn't poison the run).
10
+ *
11
+ * Reconnect-on-EOF: if a stdio server crashes mid-session, the next tool
12
+ * call attempts to reconnect once before surfacing the error to the model.
13
+ */
14
+ import { McpAgentClient } from './client.js';
15
+ import { loadMcpServers } from './config-loader.js';
16
+ export class McpClientPool {
17
+ opts;
18
+ clients = new Map();
19
+ /** Tools discovered + cached. Refreshed on first call after reconnect. */
20
+ toolsCache = null;
21
+ discovering = null;
22
+ closed = false;
23
+ /** Names of servers that failed to connect + the reason — surfaced to
24
+ * diagnostics so the dashboard activity panel can show "github-mcp
25
+ * failed to connect: ECONNREFUSED". */
26
+ failures = [];
27
+ constructor(opts) {
28
+ this.opts = opts;
29
+ const servers = opts.servers ?? this.loadConfig();
30
+ for (const cfg of servers) {
31
+ this.clients.set(cfg.name, new McpAgentClient(cfg, {
32
+ runId: opts.runId,
33
+ onProgress: opts.onProgress,
34
+ }));
35
+ }
36
+ }
37
+ loadConfig() {
38
+ const loaderOpts = { workspaceRoot: this.opts.workspaceRoot };
39
+ const fn = this.opts.loader ?? loadMcpServers;
40
+ try {
41
+ return fn(loaderOpts);
42
+ }
43
+ catch (err) {
44
+ this.failures.push({ server: '<config>', reason: err.message });
45
+ return [];
46
+ }
47
+ }
48
+ /** True iff at least one server is configured (regardless of liveness). */
49
+ hasServers() {
50
+ return this.clients.size > 0;
51
+ }
52
+ /**
53
+ * Connect every configured server in parallel, with a per-server
54
+ * timeout. Servers that fail to connect are removed from the pool and
55
+ * recorded in `failures` — they cannot serve tools but the run continues.
56
+ * Returns the union of tool descriptors from connected servers.
57
+ */
58
+ async discoverTools() {
59
+ if (this.closed)
60
+ return [];
61
+ if (this.toolsCache)
62
+ return this.toolsCache;
63
+ if (this.discovering)
64
+ return this.discovering;
65
+ this.discovering = this._discoverTools().finally(() => { this.discovering = null; });
66
+ return this.discovering;
67
+ }
68
+ async _discoverTools() {
69
+ const timeout = this.opts.connectTimeoutMs ?? 5000;
70
+ const results = await Promise.allSettled(Array.from(this.clients.entries()).map(async ([name, client]) => {
71
+ try {
72
+ await Promise.race([
73
+ client.connect(),
74
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`connect timed out after ${timeout}ms`)), timeout)),
75
+ ]);
76
+ return await client.listTools();
77
+ }
78
+ catch (err) {
79
+ this.failures.push({ server: name, reason: err.message });
80
+ // Drop the client — it's unusable. listTools() may throw because
81
+ // the connect failed too.
82
+ this.clients.delete(name);
83
+ try {
84
+ await client.close();
85
+ }
86
+ catch { /* ok */ }
87
+ return [];
88
+ }
89
+ }));
90
+ const tools = [];
91
+ for (const r of results) {
92
+ if (r.status === 'fulfilled')
93
+ tools.push(...r.value);
94
+ }
95
+ this.toolsCache = tools;
96
+ return tools;
97
+ }
98
+ /**
99
+ * Dispatch a namespaced (`mcp__<server>__<tool>`) call. Returns the
100
+ * SDK's raw result for the merged executor to flatten. Throws when the
101
+ * server isn't connected and reconnect fails — caller wraps as a tool
102
+ * error so the model can recover.
103
+ */
104
+ async callTool(toolName, args, signal) {
105
+ if (this.closed)
106
+ throw new Error('McpClientPool is closed');
107
+ const owner = this.findOwner(toolName);
108
+ if (!owner)
109
+ throw new Error(`No MCP server owns "${toolName}"`);
110
+ const client = this.clients.get(owner);
111
+ if (!client)
112
+ throw new Error(`MCP server "${owner}" is unavailable`);
113
+ try {
114
+ return await client.callTool(toolName, args, signal);
115
+ }
116
+ catch (err) {
117
+ // EOF on stdio = server died. Try to reconnect once + replay.
118
+ const msg = err.message ?? '';
119
+ if (/closed|EPIPE|ECONNRESET|connection.*lost|EOF/i.test(msg)) {
120
+ try {
121
+ await client.close();
122
+ await client.connect();
123
+ this.toolsCache = null; // tool list may have changed
124
+ return await client.callTool(toolName, args, signal);
125
+ }
126
+ catch (reconnectErr) {
127
+ this.failures.push({
128
+ server: owner,
129
+ reason: `reconnect failed: ${reconnectErr.message}`,
130
+ });
131
+ this.clients.delete(owner);
132
+ throw reconnectErr;
133
+ }
134
+ }
135
+ throw err;
136
+ }
137
+ }
138
+ /** Abort every in-flight tool call across every client. */
139
+ cancelInFlight(reason) {
140
+ for (const c of this.clients.values()) {
141
+ try {
142
+ c.cancelInFlight(reason);
143
+ }
144
+ catch { /* ok */ }
145
+ }
146
+ }
147
+ /** Close every client. Idempotent. */
148
+ async close() {
149
+ if (this.closed)
150
+ return;
151
+ this.closed = true;
152
+ this.cancelInFlight('pool closing');
153
+ await Promise.allSettled(Array.from(this.clients.values()).map((c) => c.close()));
154
+ this.clients.clear();
155
+ this.toolsCache = null;
156
+ }
157
+ /** Find the owning server name for a `mcp__<server>__<tool>` id. */
158
+ findOwner(toolName) {
159
+ // Format: mcp__<server>__<tool>. Server names may themselves contain
160
+ // underscores so we resolve by longest matching prefix.
161
+ if (!toolName.startsWith('mcp__'))
162
+ return undefined;
163
+ const candidates = Array.from(this.clients.keys()).sort((a, b) => b.length - a.length);
164
+ for (const name of candidates) {
165
+ if (toolName.startsWith(`mcp__${name}__`))
166
+ return name;
167
+ }
168
+ return undefined;
169
+ }
170
+ }
171
+ //# sourceMappingURL=pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/mcp/pool.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,13 @@
1
1
  /**
2
- * `buildAgentToolset` — merge built-in tools with MCP-discovered tools into a
3
- * single `ToolSchema[]` registered with the LLM provider, plus a dispatch
4
- * map from namespaced tool name → owning MCP client.
2
+ * `buildAgentToolset` — legacy helper that merges built-in tool schemas with
3
+ * MCP-discovered tools into a single `ToolSchema[]` plus a dispatch map.
5
4
  *
6
- * Per ADR §H7, the provider doesn't know MCP exists; the agent layer routes
7
- * tool calls based on whether the name is in `mcpDispatch`.
5
+ * Superseded by `MergedToolExecutor` for live agent paths — that class is
6
+ * what the bridge wires into `ModelAdapterConfig.toolExecutor`. This
7
+ * helper remains for callers that want the flat tools+dispatch shape (and
8
+ * for backward compat with the existing test surface).
9
+ *
10
+ * Naming convention is `mcp__<server>__<tool>` (Claude Code convention).
8
11
  */
9
12
  import type { ToolSchema } from '../types.js';
10
13
  import type { McpAgentClient } from './client.js';
@@ -1 +1 @@
1
- {"version":3,"file":"tool-merger.d.ts","sourceRoot":"","sources":["../../src/mcp/tool-merger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,gEAAgE;IAChE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC1C;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,UAAU,EAAE,EACrB,UAAU,EAAE,cAAc,EAAE,GAC3B,OAAO,CAAC,YAAY,CAAC,CA4BvB"}
1
+ {"version":3,"file":"tool-merger.d.ts","sourceRoot":"","sources":["../../src/mcp/tool-merger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,gEAAgE;IAChE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC1C;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,UAAU,EAAE,EACrB,UAAU,EAAE,cAAc,EAAE,GAC3B,OAAO,CAAC,YAAY,CAAC,CA4BvB"}
@@ -1,32 +1,24 @@
1
- /**
2
- * `buildAgentToolset` — merge built-in tools with MCP-discovered tools into a
3
- * single `ToolSchema[]` registered with the LLM provider, plus a dispatch
4
- * map from namespaced tool name → owning MCP client.
5
- *
6
- * Per ADR §H7, the provider doesn't know MCP exists; the agent layer routes
7
- * tool calls based on whether the name is in `mcpDispatch`.
8
- */
9
1
  export async function buildAgentToolset(builtIn, mcpClients) {
10
2
  const tools = [...builtIn];
11
3
  const mcpDispatch = new Map();
12
4
  const seen = new Set(builtIn.map((t) => t.name));
13
5
  for (const client of mcpClients) {
14
- let mcpTools;
6
+ let mcpDescriptors;
15
7
  try {
16
- mcpTools = await client.listTools();
8
+ mcpDescriptors = await client.listTools();
17
9
  }
18
10
  catch (err) {
19
11
  process.stderr.write(`[anvil-mcp] WARN: server "${client.config.name}" listTools failed: ${err.message}\n`);
20
12
  continue;
21
13
  }
22
- for (const t of mcpTools) {
23
- if (seen.has(t.name)) {
24
- process.stderr.write(`[anvil-mcp] WARN: tool name collision "${t.name}"; ignoring duplicate from "${client.config.name}"\n`);
14
+ for (const desc of mcpDescriptors) {
15
+ if (seen.has(desc.name)) {
16
+ process.stderr.write(`[anvil-mcp] WARN: tool name collision "${desc.name}"; ignoring duplicate from "${client.config.name}"\n`);
25
17
  continue;
26
18
  }
27
- tools.push(t);
28
- mcpDispatch.set(t.name, client);
29
- seen.add(t.name);
19
+ tools.push(desc.schema);
20
+ mcpDispatch.set(desc.name, client);
21
+ seen.add(desc.name);
30
22
  }
31
23
  }
32
24
  return { tools, mcpDispatch };
@@ -1 +1 @@
1
- {"version":3,"file":"tool-merger.js","sourceRoot":"","sources":["../../src/mcp/tool-merger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAqB,EACrB,UAA4B;IAE5B,MAAM,KAAK,GAAiB,CAAC,GAAG,OAAO,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA0B,CAAC;IACtD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjD,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,IAAI,QAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,MAAM,CAAC,MAAM,CAAC,IAAI,uBAAwB,GAAa,CAAC,OAAO,IAAI,CACjG,CAAC;YACF,SAAS;QACX,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0CAA0C,CAAC,CAAC,IAAI,+BAA+B,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CACvG,CAAC;gBACF,SAAS;YACX,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACd,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AAChC,CAAC"}
1
+ {"version":3,"file":"tool-merger.js","sourceRoot":"","sources":["../../src/mcp/tool-merger.ts"],"names":[],"mappings":"AAqBA,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAqB,EACrB,UAA4B;IAE5B,MAAM,KAAK,GAAiB,CAAC,GAAG,OAAO,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA0B,CAAC;IACtD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjD,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,IAAI,cAAc,CAAC;QACnB,IAAI,CAAC;YACH,cAAc,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,MAAM,CAAC,MAAM,CAAC,IAAI,uBAAwB,GAAa,CAAC,OAAO,IAAI,CACjG,CAAC;YACF,SAAS;QACX,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0CAA0C,IAAI,CAAC,IAAI,+BAA+B,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAC1G,CAAC;gBACF,SAAS;YACX,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACxB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACnC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Model catalog — single source of truth for per-model token limits.
3
+ *
4
+ * Promoted to `@esankhan3/anvil-agent-core` in Phase F1 of the dashboard
5
+ * pipeline-runner deprecation plan (`docs/dashboard-pipeline-deprecation-plan.md`).
6
+ * Lives next to the provider adapters because token limits are
7
+ * provider-shaped knowledge — same layer as which envelope each provider
8
+ * accepts. Cli + dashboard + future tooling all read from here.
9
+ *
10
+ * Resolution order (first match wins):
11
+ * 1. ENV override — `ANVIL_CONTEXT_WINDOW_<sanitized-model-id>` (e.g. `ANVIL_CONTEXT_WINDOW_CLAUDE_OPUS_4_7=1000000`)
12
+ * 2. Explicit override table — for models that diverge from their family defaults (legacy 200K variants)
13
+ * 3. Family rules — regex match on model ID, ordered from most specific to least
14
+ * 4. OpenRouter-style `org/model` — recurse on the model segment
15
+ * 5. Conservative DEFAULT
16
+ *
17
+ * Source: https://docs.claude.com/en/docs/about-claude/models/overview
18
+ * (Opus 4.7, Opus 4.6, and Sonnet 4.6 all ship with native 1M context windows;
19
+ * Haiku 4.5 stays at 200K. Legacy Sonnet/Opus 4.5 and earlier remain 200K.)
20
+ *
21
+ * To refresh limits without code changes, set env vars or edit OVERRIDES below.
22
+ * Programmatic refresh path: Anthropic Models API exposes `max_input_tokens` per model.
23
+ */
24
+ export interface ModelSpec {
25
+ contextWindow: number;
26
+ maxOutput: number;
27
+ }
28
+ /** Conservative default for completely unknown model IDs. */
29
+ export declare const DEFAULT_SPEC: ModelSpec;
30
+ /** Resolve the full spec (context window + max output) for a model ID. */
31
+ export declare function getModelSpec(modelId: string): ModelSpec;
32
+ /** Context window (input tokens) for a model ID. */
33
+ export declare function getContextWindow(modelId: string): number;
34
+ /** Max output tokens for a model ID. */
35
+ export declare function getMaxOutput(modelId: string): number;
36
+ //# sourceMappingURL=model-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../src/model-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6DAA6D;AAC7D,eAAO,MAAM,YAAY,EAAE,SAAyD,CAAC;AAqDrF,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAuBvD;AAED,oDAAoD;AACpD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Model catalog — single source of truth for per-model token limits.
3
+ *
4
+ * Promoted to `@esankhan3/anvil-agent-core` in Phase F1 of the dashboard
5
+ * pipeline-runner deprecation plan (`docs/dashboard-pipeline-deprecation-plan.md`).
6
+ * Lives next to the provider adapters because token limits are
7
+ * provider-shaped knowledge — same layer as which envelope each provider
8
+ * accepts. Cli + dashboard + future tooling all read from here.
9
+ *
10
+ * Resolution order (first match wins):
11
+ * 1. ENV override — `ANVIL_CONTEXT_WINDOW_<sanitized-model-id>` (e.g. `ANVIL_CONTEXT_WINDOW_CLAUDE_OPUS_4_7=1000000`)
12
+ * 2. Explicit override table — for models that diverge from their family defaults (legacy 200K variants)
13
+ * 3. Family rules — regex match on model ID, ordered from most specific to least
14
+ * 4. OpenRouter-style `org/model` — recurse on the model segment
15
+ * 5. Conservative DEFAULT
16
+ *
17
+ * Source: https://docs.claude.com/en/docs/about-claude/models/overview
18
+ * (Opus 4.7, Opus 4.6, and Sonnet 4.6 all ship with native 1M context windows;
19
+ * Haiku 4.5 stays at 200K. Legacy Sonnet/Opus 4.5 and earlier remain 200K.)
20
+ *
21
+ * To refresh limits without code changes, set env vars or edit OVERRIDES below.
22
+ * Programmatic refresh path: Anthropic Models API exposes `max_input_tokens` per model.
23
+ */
24
+ /** Conservative default for completely unknown model IDs. */
25
+ export const DEFAULT_SPEC = { contextWindow: 128_000, maxOutput: 16_000 };
26
+ /**
27
+ * Exceptions to family rules. Only list a model here when it differs from its
28
+ * family default (e.g. a 200K legacy variant whose family now defaults to 1M).
29
+ */
30
+ const OVERRIDES = {
31
+ // Legacy Claude models that kept 200K
32
+ 'claude-sonnet-4-5': { contextWindow: 200_000, maxOutput: 64_000 },
33
+ 'claude-sonnet-4-5-20250929': { contextWindow: 200_000, maxOutput: 64_000 },
34
+ 'claude-opus-4-5': { contextWindow: 200_000, maxOutput: 64_000 },
35
+ 'claude-opus-4-5-20251101': { contextWindow: 200_000, maxOutput: 64_000 },
36
+ 'claude-opus-4-1': { contextWindow: 200_000, maxOutput: 32_000 },
37
+ 'claude-opus-4-1-20250805': { contextWindow: 200_000, maxOutput: 32_000 },
38
+ 'claude-sonnet-4-20250514': { contextWindow: 200_000, maxOutput: 64_000 },
39
+ 'claude-opus-4-20250514': { contextWindow: 200_000, maxOutput: 32_000 },
40
+ 'claude-3-5-sonnet': { contextWindow: 200_000, maxOutput: 8_000 },
41
+ 'claude-3-opus': { contextWindow: 200_000, maxOutput: 4_000 },
42
+ 'claude-3-haiku-20240307': { contextWindow: 200_000, maxOutput: 4_000 },
43
+ };
44
+ /**
45
+ * Family rules — tried in order. First match wins.
46
+ * Add new families here rather than enumerating individual model IDs.
47
+ */
48
+ const FAMILY_RULES = [
49
+ // Claude Haiku (all versions): 200K — stayed at 200K through 4.5
50
+ { pattern: /(^|[^a-z])haiku/i, spec: { contextWindow: 200_000, maxOutput: 64_000 } },
51
+ // Claude Opus / Sonnet — current generation is 1M native (Opus 4.6+, Sonnet 4.6+, aliases)
52
+ // Covers: `opus`, `sonnet`, `claude-opus-4-6`, `claude-opus-4-7`, `claude-sonnet-4-6`, `claude-opus-4-7[1m]`, etc.
53
+ { pattern: /^(claude-)?(opus|sonnet)(-|$|\[)/i, spec: { contextWindow: 1_000_000, maxOutput: 64_000 } },
54
+ { pattern: /^claude-(opus|sonnet)-/i, spec: { contextWindow: 1_000_000, maxOutput: 64_000 } },
55
+ // Catch-all for any other claude-* model — assume modern 1M, override above if legacy
56
+ { pattern: /^claude-/i, spec: { contextWindow: 1_000_000, maxOutput: 64_000 } },
57
+ // Gemini 2.x+ — 1M native
58
+ { pattern: /^gemini-[2-9]/i, spec: { contextWindow: 1_000_000, maxOutput: 64_000 } },
59
+ { pattern: /^gemini/i, spec: { contextWindow: 1_000_000, maxOutput: 32_000 } },
60
+ // OpenAI reasoning (o1, o3, o4...) — 200K
61
+ { pattern: /^o[1-9](-|$)/i, spec: { contextWindow: 200_000, maxOutput: 100_000 } },
62
+ // OpenAI GPT-4.x / 4o — 128K
63
+ { pattern: /^gpt-4/i, spec: { contextWindow: 128_000, maxOutput: 16_000 } },
64
+ ];
65
+ /** Sanitize model ID into an env-var-safe key. */
66
+ function envKeyFor(modelId) {
67
+ return `ANVIL_CONTEXT_WINDOW_${modelId.replace(/[^a-z0-9]/gi, '_').toUpperCase()}`;
68
+ }
69
+ /** Resolve the full spec (context window + max output) for a model ID. */
70
+ export function getModelSpec(modelId) {
71
+ // 1. ENV override — operator tuning without redeploy
72
+ const envVal = process.env[envKeyFor(modelId)];
73
+ if (envVal) {
74
+ const n = parseInt(envVal, 10);
75
+ if (Number.isFinite(n) && n > 0)
76
+ return { ...DEFAULT_SPEC, contextWindow: n };
77
+ }
78
+ // 2. Explicit override for legacy variants
79
+ if (OVERRIDES[modelId])
80
+ return OVERRIDES[modelId];
81
+ // 3. Family rules
82
+ for (const { pattern, spec } of FAMILY_RULES) {
83
+ if (pattern.test(modelId))
84
+ return spec;
85
+ }
86
+ // 4. OpenRouter-style `org/model` — recurse on the model segment
87
+ if (modelId.includes('/')) {
88
+ const segment = modelId.split('/').slice(-1)[0];
89
+ if (segment && segment !== modelId)
90
+ return getModelSpec(segment);
91
+ }
92
+ return DEFAULT_SPEC;
93
+ }
94
+ /** Context window (input tokens) for a model ID. */
95
+ export function getContextWindow(modelId) {
96
+ return getModelSpec(modelId).contextWindow;
97
+ }
98
+ /** Max output tokens for a model ID. */
99
+ export function getMaxOutput(modelId) {
100
+ return getModelSpec(modelId).maxOutput;
101
+ }
102
+ //# sourceMappingURL=model-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-catalog.js","sourceRoot":"","sources":["../src/model-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,YAAY,GAAc,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAErF;;;GAGG;AACH,MAAM,SAAS,GAA8B;IAC3C,sCAAsC;IACtC,mBAAmB,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IAClE,4BAA4B,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IAC3E,iBAAiB,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IAChE,0BAA0B,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IACzE,iBAAiB,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IAChE,0BAA0B,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IACzE,0BAA0B,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IACzE,wBAAwB,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IACvE,mBAAmB,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE;IACjE,eAAe,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE;IAC7D,yBAAyB,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE;CACxE,CAAC;AAEF;;;GAGG;AACH,MAAM,YAAY,GAAgD;IAChE,iEAAiE;IACjE,EAAE,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;IAEpF,2FAA2F;IAC3F,mHAAmH;IACnH,EAAE,OAAO,EAAE,mCAAmC,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;IACvG,EAAE,OAAO,EAAE,yBAAyB,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;IAE7F,sFAAsF;IACtF,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;IAE/E,0BAA0B;IAC1B,EAAE,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;IACpF,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;IAE9E,0CAA0C;IAC1C,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;IAElF,6BAA6B;IAC7B,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;CAC5E,CAAC;AAEF,kDAAkD;AAClD,SAAS,SAAS,CAAC,OAAe;IAChC,OAAO,wBAAwB,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;AACrF,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,qDAAqD;IACrD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC/B,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,GAAG,YAAY,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC;IAED,2CAA2C;IAC3C,IAAI,SAAS,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC;IAElD,kBAAkB;IAClB,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,YAAY,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IACzC,CAAC;IAED,iEAAiE;IACjE,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,IAAI,OAAO,IAAI,OAAO,KAAK,OAAO;YAAE,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC;AAC7C,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC;AACzC,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Dynamic model tier resolver — resolves model IDs from a discovery
3
+ * result instead of hardcoding them.
4
+ *
5
+ * Promoted to `@esankhan3/anvil-agent-core` in Phase F2 of the dashboard
6
+ * pipeline-runner deprecation plan (`docs/dashboard-pipeline-deprecation-plan.md`).
7
+ * Lives next to the provider adapters because tier-based routing is
8
+ * provider-shaped knowledge — same layer as `model-catalog`. The
9
+ * dashboard's discovery layer (`provider-registry.ts`, FS+exec heavy)
10
+ * stays in dashboard and produces values that satisfy this resolver's
11
+ * structural input types.
12
+ *
13
+ * Tier routing strategy defines which "weight class" each stage needs:
14
+ * fast → cheap model for most stages, balanced for build
15
+ * balanced → mix of cheap and mid-range
16
+ * thorough → mid-range for most, powerful for specs
17
+ *
18
+ * The resolver looks up the best available model for each weight class
19
+ * from the cached discovery result.
20
+ */
21
+ /** Capability flags a discovered model may carry. */
22
+ export type ResolverCapability = 'agentic' | 'chat' | 'embedding' | 'reranking';
23
+ /** Per-model weight class. `'local'` is the zero-cost (Ollama) tier. */
24
+ export type ResolverModelWeight = 'fast' | 'balanced' | 'powerful' | 'local';
25
+ /** Minimum shape the resolver reads from each discovered model. */
26
+ export interface ResolverModel {
27
+ id: string;
28
+ capabilities: ResolverCapability[];
29
+ tier?: ResolverModelWeight;
30
+ }
31
+ /** Minimum shape the resolver reads from a discovery result. */
32
+ export interface ResolverDiscoveryResult {
33
+ models: ResolverModel[];
34
+ }
35
+ /**
36
+ * Dashboard-UI cost-tier vocabulary. Distinct from
37
+ * `router/model-registry.ts:ModelTier` (which uses the
38
+ * `'local' | 'cheap' | 'premium'` policy vocabulary). Renamed during
39
+ * Phase F2 to avoid a name collision when both types co-exist on the
40
+ * `@esankhan3/anvil-agent-core` public surface.
41
+ */
42
+ export type ResolverTier = 'fast' | 'balanced' | 'thorough';
43
+ /** @deprecated Use `ResolverTier`. Kept as an alias for back-compat. */
44
+ export type ModelTier = ResolverTier;
45
+ /**
46
+ * Provide the discovery result so the resolver can look up available models.
47
+ * Call this once after `discoverProviders()` completes.
48
+ */
49
+ export declare function setDiscoveryResult(result: ResolverDiscoveryResult): void;
50
+ /**
51
+ * Resolve the model ID for a given tier + stage combination.
52
+ * Uses the provider registry to find the best available model dynamically.
53
+ *
54
+ * @param tier - User-selected cost tier (fast/balanced/thorough)
55
+ * @param stageName - Pipeline stage name (clarify, build, etc.)
56
+ * @param fallbackModel - Model to use if registry lookup fails
57
+ */
58
+ export declare function resolveModelByTier(tier: ResolverTier, stageName: string, fallbackModel: string): string;
59
+ /** Invalidate the resolver cache (e.g., after provider changes) */
60
+ export declare function invalidateResolverCache(): void;
61
+ //# sourceMappingURL=model-tier-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-tier-resolver.d.ts","sourceRoot":"","sources":["../src/model-tier-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAQH,qDAAqD;AACrD,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;AAEhF,wEAAwE;AACxE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;AAE7E,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC5B;AAED,gEAAgE;AAChE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAE5D,wEAAwE;AACxE,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC;AA4DrC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAKxE;AA2CD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,YAAY,EAClB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,MAAM,CA0CR;AAED,mEAAmE;AACnE,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C"}