@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.
- package/dist/__tests__/mcp-merged-executor.test.d.ts +2 -0
- package/dist/__tests__/mcp-merged-executor.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-merged-executor.test.js +221 -0
- package/dist/__tests__/mcp-merged-executor.test.js.map +1 -0
- package/dist/__tests__/mcp.test.js +21 -15
- package/dist/__tests__/mcp.test.js.map +1 -1
- package/dist/__tests__/model-catalog.test.d.ts +11 -0
- package/dist/__tests__/model-catalog.test.d.ts.map +1 -0
- package/dist/__tests__/model-catalog.test.js +102 -0
- package/dist/__tests__/model-catalog.test.js.map +1 -0
- package/dist/__tests__/model-tier-resolver.test.d.ts +19 -0
- package/dist/__tests__/model-tier-resolver.test.d.ts.map +1 -0
- package/dist/__tests__/model-tier-resolver.test.js +97 -0
- package/dist/__tests__/model-tier-resolver.test.js.map +1 -0
- package/dist/adk-adapter.d.ts.map +1 -1
- package/dist/adk-adapter.js +11 -0
- package/dist/adk-adapter.js.map +1 -1
- package/dist/agent/session/__tests__/spawn-telemetry.test.js +39 -4
- package/dist/agent/session/__tests__/spawn-telemetry.test.js.map +1 -1
- package/dist/agent/session/adapter.d.ts +34 -0
- package/dist/agent/session/adapter.d.ts.map +1 -1
- package/dist/agent/session/adapter.js.map +1 -1
- package/dist/agent/session/default-adapter-factory.d.ts.map +1 -1
- package/dist/agent/session/default-adapter-factory.js +66 -2
- package/dist/agent/session/default-adapter-factory.js.map +1 -1
- package/dist/agent/session/language-model-bridge.d.ts.map +1 -1
- package/dist/agent/session/language-model-bridge.js +52 -1
- package/dist/agent/session/language-model-bridge.js.map +1 -1
- package/dist/agent/session/session.d.ts +16 -0
- package/dist/agent/session/session.d.ts.map +1 -1
- package/dist/agent/session/session.js +86 -2
- package/dist/agent/session/session.js.map +1 -1
- package/dist/gemini-adapter.d.ts.map +1 -1
- package/dist/gemini-adapter.js +11 -0
- package/dist/gemini-adapter.js.map +1 -1
- package/dist/gemini-cli-adapter.d.ts.map +1 -1
- package/dist/gemini-cli-adapter.js +10 -0
- package/dist/gemini-cli-adapter.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +64 -18
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +190 -26
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/index.d.ts +5 -2
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +3 -8
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/merged-executor.d.ts +74 -0
- package/dist/mcp/merged-executor.d.ts.map +1 -0
- package/dist/mcp/merged-executor.js +198 -0
- package/dist/mcp/merged-executor.js.map +1 -0
- package/dist/mcp/pool.d.ts +71 -0
- package/dist/mcp/pool.d.ts.map +1 -0
- package/dist/mcp/pool.js +171 -0
- package/dist/mcp/pool.js.map +1 -0
- package/dist/mcp/tool-merger.d.ts +8 -5
- package/dist/mcp/tool-merger.d.ts.map +1 -1
- package/dist/mcp/tool-merger.js +8 -16
- package/dist/mcp/tool-merger.js.map +1 -1
- package/dist/model-catalog.d.ts +36 -0
- package/dist/model-catalog.d.ts.map +1 -0
- package/dist/model-catalog.js +102 -0
- package/dist/model-catalog.js.map +1 -0
- package/dist/model-tier-resolver.d.ts +61 -0
- package/dist/model-tier-resolver.d.ts.map +1 -0
- package/dist/model-tier-resolver.js +170 -0
- package/dist/model-tier-resolver.js.map +1 -0
- package/dist/ollama-adapter.d.ts.map +1 -1
- package/dist/ollama-adapter.js +6 -1
- package/dist/ollama-adapter.js.map +1 -1
- package/dist/opencode-adapter.d.ts +11 -0
- package/dist/opencode-adapter.d.ts.map +1 -1
- package/dist/opencode-adapter.js +13 -0
- package/dist/opencode-adapter.js.map +1 -1
- package/dist/openrouter-adapter.d.ts +15 -0
- package/dist/openrouter-adapter.d.ts.map +1 -1
- package/dist/openrouter-adapter.js +69 -19
- package/dist/openrouter-adapter.js.map +1 -1
- package/dist/provider-liveness.d.ts +5 -0
- package/dist/provider-liveness.d.ts.map +1 -1
- package/dist/provider-liveness.js +11 -15
- package/dist/provider-liveness.js.map +1 -1
- package/dist/registry.d.ts +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/upstream-error.d.ts.map +1 -1
- package/dist/upstream-error.js +11 -1
- package/dist/upstream-error.js.map +1 -1
- 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"}
|
package/dist/mcp/pool.js
ADDED
|
@@ -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` —
|
|
3
|
-
* single `ToolSchema[]`
|
|
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
|
-
*
|
|
7
|
-
*
|
|
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
|
|
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"}
|
package/dist/mcp/tool-merger.js
CHANGED
|
@@ -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
|
|
6
|
+
let mcpDescriptors;
|
|
15
7
|
try {
|
|
16
|
-
|
|
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
|
|
23
|
-
if (seen.has(
|
|
24
|
-
process.stderr.write(`[anvil-mcp] WARN: tool name collision "${
|
|
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(
|
|
28
|
-
mcpDispatch.set(
|
|
29
|
-
seen.add(
|
|
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":"
|
|
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":"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"}
|
|
@@ -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"}
|