@mcoda/codali 0.1.108 → 0.1.109

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 (149) hide show
  1. package/dist/agents/AgentInventory.d.ts +56 -0
  2. package/dist/agents/AgentInventory.d.ts.map +1 -0
  3. package/dist/agents/AgentInventory.js +131 -0
  4. package/dist/agents/RoleResolution.d.ts +49 -0
  5. package/dist/agents/RoleResolution.d.ts.map +1 -0
  6. package/dist/agents/RoleResolution.js +166 -0
  7. package/dist/api/ChatCompletionsAdapter.d.ts +76 -0
  8. package/dist/api/ChatCompletionsAdapter.d.ts.map +1 -0
  9. package/dist/api/ChatCompletionsAdapter.js +54 -0
  10. package/dist/api/CodaliApi.d.ts +78 -0
  11. package/dist/api/CodaliApi.d.ts.map +1 -0
  12. package/dist/api/CodaliApi.js +273 -0
  13. package/dist/api/CodaliServer.d.ts +42 -0
  14. package/dist/api/CodaliServer.d.ts.map +1 -0
  15. package/dist/api/CodaliServer.js +118 -0
  16. package/dist/cli/AskCommand.d.ts +66 -0
  17. package/dist/cli/AskCommand.d.ts.map +1 -0
  18. package/dist/cli/AskCommand.js +536 -0
  19. package/dist/cli/AuthCommand.d.ts +19 -0
  20. package/dist/cli/AuthCommand.d.ts.map +1 -0
  21. package/dist/cli/AuthCommand.js +161 -0
  22. package/dist/cli/ServeCommand.d.ts +5 -0
  23. package/dist/cli/ServeCommand.d.ts.map +1 -0
  24. package/dist/cli/ServeCommand.js +87 -0
  25. package/dist/cli/ToolsCommand.d.ts +22 -0
  26. package/dist/cli/ToolsCommand.d.ts.map +1 -0
  27. package/dist/cli/ToolsCommand.js +208 -0
  28. package/dist/cli.d.ts.map +1 -1
  29. package/dist/cli.js +29 -1
  30. package/dist/connectors/http/HttpToolDefinition.d.ts +74 -0
  31. package/dist/connectors/http/HttpToolDefinition.d.ts.map +1 -0
  32. package/dist/connectors/http/HttpToolDefinition.js +190 -0
  33. package/dist/connectors/http/HttpToolSource.d.ts +18 -0
  34. package/dist/connectors/http/HttpToolSource.d.ts.map +1 -0
  35. package/dist/connectors/http/HttpToolSource.js +29 -0
  36. package/dist/connectors/mcp/McpClient.d.ts +132 -0
  37. package/dist/connectors/mcp/McpClient.d.ts.map +1 -0
  38. package/dist/connectors/mcp/McpClient.js +215 -0
  39. package/dist/connectors/mcp/McpServerRegistry.d.ts +75 -0
  40. package/dist/connectors/mcp/McpServerRegistry.d.ts.map +1 -0
  41. package/dist/connectors/mcp/McpServerRegistry.js +180 -0
  42. package/dist/connectors/mcp/McpToolAdapter.d.ts +48 -0
  43. package/dist/connectors/mcp/McpToolAdapter.d.ts.map +1 -0
  44. package/dist/connectors/mcp/McpToolAdapter.js +101 -0
  45. package/dist/connectors/mcp/McpToolSource.d.ts +27 -0
  46. package/dist/connectors/mcp/McpToolSource.d.ts.map +1 -0
  47. package/dist/connectors/mcp/McpToolSource.js +33 -0
  48. package/dist/connectors/media/ImageGenerationTool.d.ts +43 -0
  49. package/dist/connectors/media/ImageGenerationTool.d.ts.map +1 -0
  50. package/dist/connectors/media/ImageGenerationTool.js +122 -0
  51. package/dist/connectors/oauth/DeviceCodeAuth.d.ts +74 -0
  52. package/dist/connectors/oauth/DeviceCodeAuth.d.ts.map +1 -0
  53. package/dist/connectors/oauth/DeviceCodeAuth.js +153 -0
  54. package/dist/docdex/DocdexClient.d.ts +2 -0
  55. package/dist/docdex/DocdexClient.d.ts.map +1 -1
  56. package/dist/docdex/DocdexClient.js +4 -0
  57. package/dist/gateway/CodaliGateway.d.ts +51 -3
  58. package/dist/gateway/CodaliGateway.d.ts.map +1 -1
  59. package/dist/gateway/CodaliGateway.js +226 -2
  60. package/dist/gateway/CodaliGatewayTypes.d.ts +34 -0
  61. package/dist/gateway/CodaliGatewayTypes.d.ts.map +1 -1
  62. package/dist/gateway/EvidenceNormalizer.d.ts.map +1 -1
  63. package/dist/gateway/EvidenceNormalizer.js +90 -1
  64. package/dist/gateway/Finalizer.d.ts +50 -0
  65. package/dist/gateway/Finalizer.d.ts.map +1 -0
  66. package/dist/gateway/Finalizer.js +79 -0
  67. package/dist/gateway/GatewayPlanner.d.ts +13 -0
  68. package/dist/gateway/GatewayPlanner.d.ts.map +1 -1
  69. package/dist/gateway/GatewayPlanner.js +81 -16
  70. package/dist/gateway/GatewayStateMachine.d.ts.map +1 -1
  71. package/dist/gateway/GatewayStateMachine.js +6 -0
  72. package/dist/gateway/GatewayTracer.d.ts +93 -0
  73. package/dist/gateway/GatewayTracer.d.ts.map +1 -0
  74. package/dist/gateway/GatewayTracer.js +214 -0
  75. package/dist/gateway/LocalGatewayProvider.d.ts +46 -0
  76. package/dist/gateway/LocalGatewayProvider.d.ts.map +1 -0
  77. package/dist/gateway/LocalGatewayProvider.js +161 -0
  78. package/dist/gateway/LocalGatewayTaskRunner.d.ts +79 -0
  79. package/dist/gateway/LocalGatewayTaskRunner.d.ts.map +1 -0
  80. package/dist/gateway/LocalGatewayTaskRunner.js +354 -0
  81. package/dist/gateway/RelevanceJudge.d.ts +14 -0
  82. package/dist/gateway/RelevanceJudge.d.ts.map +1 -0
  83. package/dist/gateway/RelevanceJudge.js +87 -0
  84. package/dist/gateway/ResponseSchema.d.ts +32 -0
  85. package/dist/gateway/ResponseSchema.d.ts.map +1 -0
  86. package/dist/gateway/ResponseSchema.js +120 -0
  87. package/dist/gateway/TemporalContext.d.ts +36 -0
  88. package/dist/gateway/TemporalContext.d.ts.map +1 -0
  89. package/dist/gateway/TemporalContext.js +115 -0
  90. package/dist/gateway/ToolCapabilityCompiler.d.ts +5 -0
  91. package/dist/gateway/ToolCapabilityCompiler.d.ts.map +1 -1
  92. package/dist/gateway/ToolCapabilityCompiler.js +27 -2
  93. package/dist/gateway/ToolDescriptorSource.d.ts +6 -0
  94. package/dist/gateway/ToolDescriptorSource.d.ts.map +1 -0
  95. package/dist/gateway/ToolDescriptorSource.js +30 -0
  96. package/dist/gateway/ToolExposure.d.ts +36 -0
  97. package/dist/gateway/ToolExposure.d.ts.map +1 -0
  98. package/dist/gateway/ToolExposure.js +154 -0
  99. package/dist/index.d.ts +57 -0
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +45 -0
  102. package/dist/providers/ClaudeCliProvider.d.ts +2 -0
  103. package/dist/providers/ClaudeCliProvider.d.ts.map +1 -1
  104. package/dist/providers/ClaudeCliProvider.js +2 -0
  105. package/dist/providers/CodexCliProvider.d.ts +2 -0
  106. package/dist/providers/CodexCliProvider.d.ts.map +1 -1
  107. package/dist/providers/CodexCliProvider.js +2 -0
  108. package/dist/providers/MswarmWorkerProvider.d.ts +2 -0
  109. package/dist/providers/MswarmWorkerProvider.d.ts.map +1 -1
  110. package/dist/providers/MswarmWorkerProvider.js +2 -0
  111. package/dist/providers/OllamaRemoteProvider.d.ts +2 -0
  112. package/dist/providers/OllamaRemoteProvider.d.ts.map +1 -1
  113. package/dist/providers/OllamaRemoteProvider.js +2 -0
  114. package/dist/providers/OpenAiCompatibleProvider.d.ts +2 -0
  115. package/dist/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
  116. package/dist/providers/OpenAiCompatibleProvider.js +2 -0
  117. package/dist/providers/ProviderTypes.d.ts +13 -0
  118. package/dist/providers/ProviderTypes.d.ts.map +1 -1
  119. package/dist/providers/ProviderTypes.js +1 -1
  120. package/dist/runcontext/CredentialFile.d.ts +12 -0
  121. package/dist/runcontext/CredentialFile.d.ts.map +1 -0
  122. package/dist/runcontext/CredentialFile.js +81 -0
  123. package/dist/runcontext/RunContextResolver.d.ts +126 -0
  124. package/dist/runcontext/RunContextResolver.d.ts.map +1 -0
  125. package/dist/runcontext/RunContextResolver.js +389 -0
  126. package/dist/storage/CodaliDatasetPrivacyEngine.d.ts +1 -1
  127. package/dist/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -1
  128. package/dist/storage/CodaliDatasetPrivacyEngine.js +12 -0
  129. package/dist/tools/ToolRegistry.d.ts +14 -1
  130. package/dist/tools/ToolRegistry.d.ts.map +1 -1
  131. package/dist/tools/ToolRegistry.js +38 -1
  132. package/dist/tools/ToolTypes.d.ts +35 -0
  133. package/dist/tools/ToolTypes.d.ts.map +1 -1
  134. package/dist/tools/ToolTypes.js +21 -0
  135. package/dist/tools/TruncateResult.d.ts +29 -0
  136. package/dist/tools/TruncateResult.d.ts.map +1 -0
  137. package/dist/tools/TruncateResult.js +91 -0
  138. package/dist/tools/diff/DiffTool.d.ts.map +1 -1
  139. package/dist/tools/diff/DiffTool.js +2 -0
  140. package/dist/tools/docdex/DocdexTools.d.ts +21 -1
  141. package/dist/tools/docdex/DocdexTools.d.ts.map +1 -1
  142. package/dist/tools/docdex/DocdexTools.js +108 -16
  143. package/dist/tools/filesystem/FileTools.d.ts.map +1 -1
  144. package/dist/tools/filesystem/FileTools.js +8 -0
  145. package/dist/tools/search/SearchTool.d.ts.map +1 -1
  146. package/dist/tools/search/SearchTool.js +3 -1
  147. package/dist/tools/shell/ShellTool.d.ts.map +1 -1
  148. package/dist/tools/shell/ShellTool.js +2 -0
  149. package/package.json +4 -3
@@ -0,0 +1,190 @@
1
+ import { ToolExecutionError } from "../../tools/ToolTypes.js";
2
+ import { refreshAccessToken } from "../oauth/DeviceCodeAuth.js";
3
+ import { truncateToolResult } from "../../tools/TruncateResult.js";
4
+ /**
5
+ * Access tokens minted from refresh tokens, cached until shortly before expiry.
6
+ *
7
+ * Graph access tokens live about an hour. Without caching every tool call would
8
+ * spend a network round trip on a token it already holds; without the safety
9
+ * margin a call can start valid and arrive expired.
10
+ */
11
+ const accessTokenCache = new Map();
12
+ const TOKEN_EXPIRY_MARGIN_MS = 60000;
13
+ export const resetAccessTokenCache = () => accessTokenCache.clear();
14
+ const oauthAccessToken = async (auth, fetchImpl) => {
15
+ if (!auth.tokenUrl || !auth.clientId || !auth.refreshToken) {
16
+ throw new ToolExecutionError("missing_credentials", "oauth2_refresh requires tokenUrl, clientId and refreshToken. Run `codali auth microsoft`.", { retryable: false });
17
+ }
18
+ const key = `${auth.tokenUrl}|${auth.clientId}|${auth.refreshToken.slice(-16)}`;
19
+ const cached = accessTokenCache.get(key);
20
+ if (cached && cached.expiresAtMs > Date.now()) {
21
+ return cached.token;
22
+ }
23
+ try {
24
+ const tokens = await refreshAccessToken({ tokenUrl: auth.tokenUrl, clientId: auth.clientId, scope: auth.scope ?? "" }, auth.refreshToken, fetchImpl);
25
+ accessTokenCache.set(key, {
26
+ token: tokens.accessToken,
27
+ expiresAtMs: Date.now() + tokens.expiresInSeconds * 1000 - TOKEN_EXPIRY_MARGIN_MS,
28
+ });
29
+ return tokens.accessToken;
30
+ }
31
+ catch (error) {
32
+ throw new ToolExecutionError("missing_credentials", `Could not refresh the access token: ${error instanceof Error ? error.message : String(error)}. Re-run \`codali auth microsoft\`.`, { retryable: false });
33
+ }
34
+ };
35
+ const DEFAULT_TIMEOUT_MS = 20000;
36
+ const DEFAULT_MAX_RESPONSE_CHARS = 12000;
37
+ /**
38
+ * Only non-mutating verbs. A connector cannot opt into POST here — write access
39
+ * is a separate decision with its own approval story, not a config flag.
40
+ */
41
+ const ALLOWED_METHODS = new Set(["GET", "HEAD"]);
42
+ const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
43
+ /**
44
+ * Substitutes `{name}` placeholders and appends anything left over as query
45
+ * parameters. Placeholder values are URL-encoded, so a model cannot inject a
46
+ * path segment or escape the connector's base URL.
47
+ */
48
+ export const buildHttpUrl = (baseUrl, template, args) => {
49
+ const consumed = new Set();
50
+ const path = template.replace(/\{(\w+)\}/g, (_match, key) => {
51
+ const value = args[key];
52
+ if (value === undefined || value === null) {
53
+ throw new ToolExecutionError("tool_invalid_args", `Missing required path parameter "${key}".`, { retryable: false, details: { parameter: key } });
54
+ }
55
+ consumed.add(key);
56
+ return encodeURIComponent(String(value));
57
+ });
58
+ const url = new URL(path.replace(/^\//, ""), baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
59
+ for (const [key, value] of Object.entries(args)) {
60
+ if (consumed.has(key) || value === undefined || value === null)
61
+ continue;
62
+ url.searchParams.set(key, String(value));
63
+ }
64
+ return url.toString();
65
+ };
66
+ /**
67
+ * Projects a response down to what was asked for. `items[].name` maps over an
68
+ * array; a plain dotted path drills in.
69
+ */
70
+ export const applyResponseSelector = (payload, selector) => {
71
+ if (!selector)
72
+ return payload;
73
+ let current = payload;
74
+ for (const segment of selector.split(".")) {
75
+ if (current === undefined || current === null)
76
+ return undefined;
77
+ const arrayMatch = segment.match(/^(\w+)\[\]$/);
78
+ if (arrayMatch) {
79
+ const key = arrayMatch[1];
80
+ const value = isRecord(current) ? current[key] : undefined;
81
+ if (!Array.isArray(value))
82
+ return undefined;
83
+ current = value;
84
+ continue;
85
+ }
86
+ if (Array.isArray(current)) {
87
+ current = current.map((entry) => (isRecord(entry) ? entry[segment] : undefined));
88
+ continue;
89
+ }
90
+ current = isRecord(current) ? current[segment] : undefined;
91
+ }
92
+ return current;
93
+ };
94
+ const buildAuthHeaders = (auth) => {
95
+ if (!auth || auth.type === "none")
96
+ return {};
97
+ if (auth.type === "bearer" && auth.token) {
98
+ return { Authorization: `Bearer ${auth.token}` };
99
+ }
100
+ if (auth.type === "basic" && auth.username) {
101
+ const encoded = Buffer.from(`${auth.username}:${auth.password ?? ""}`).toString("base64");
102
+ return { Authorization: `Basic ${encoded}` };
103
+ }
104
+ if (auth.type === "header" && auth.headerName && auth.token) {
105
+ return { [auth.headerName]: auth.token };
106
+ }
107
+ return {};
108
+ };
109
+ const truncate = (value, limit) => value.length > limit
110
+ ? `${value.slice(0, limit)}\n…[truncated ${value.length - limit} characters]`
111
+ : value;
112
+ export const httpToolName = (connector, tool) => `http:${connector}:${tool}`;
113
+ export const httpToolToDefinition = (declaration, options) => {
114
+ const { connector } = options;
115
+ if (!ALLOWED_METHODS.has(declaration.method)) {
116
+ throw new Error(`HTTP tool ${declaration.id} declares method ${declaration.method}; only GET and HEAD are permitted.`);
117
+ }
118
+ const qualified = httpToolName(connector.name, declaration.id);
119
+ const fetchImpl = options.fetchImpl ?? fetch;
120
+ const timeoutMs = connector.timeoutMs ?? DEFAULT_TIMEOUT_MS;
121
+ const maxChars = declaration.maxResponseChars ?? DEFAULT_MAX_RESPONSE_CHARS;
122
+ return {
123
+ name: qualified,
124
+ description: declaration.description,
125
+ inputSchema: declaration.inputSchema ?? {
126
+ type: "object",
127
+ properties: {},
128
+ additionalProperties: true,
129
+ },
130
+ // GET/HEAD only, enforced above.
131
+ readOnly: true,
132
+ capability: connector.name,
133
+ handler: async (args) => {
134
+ const record = isRecord(args) ? args : {};
135
+ const url = buildHttpUrl(connector.baseUrl, declaration.urlTemplate, record);
136
+ const controller = new AbortController();
137
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
138
+ // OAuth needs an await, so it is resolved before the request is built.
139
+ const authHeaders = connector.auth?.type === "oauth2_refresh"
140
+ ? { Authorization: `Bearer ${await oauthAccessToken(connector.auth, fetchImpl)}` }
141
+ : buildAuthHeaders(connector.auth);
142
+ let response;
143
+ try {
144
+ response = await fetchImpl(url, {
145
+ method: declaration.method,
146
+ headers: {
147
+ Accept: "application/json",
148
+ ...(connector.headers ?? {}),
149
+ ...authHeaders,
150
+ },
151
+ signal: controller.signal,
152
+ });
153
+ }
154
+ catch (error) {
155
+ throw new ToolExecutionError("tool_execution_failed", `${qualified} request failed: ${error instanceof Error ? error.message : String(error)}`, { retryable: true, details: { tool: qualified } });
156
+ }
157
+ finally {
158
+ clearTimeout(timer);
159
+ }
160
+ if (!response.ok) {
161
+ // The status is what a model needs to decide whether to try something
162
+ // else; the body may carry a credential, so it is not forwarded.
163
+ throw new ToolExecutionError(response.status === 401 || response.status === 403
164
+ ? "tool_permission_denied"
165
+ : "tool_execution_failed", `${qualified} returned HTTP ${response.status}.`, {
166
+ retryable: response.status >= 500 || response.status === 429,
167
+ details: { tool: qualified, status: response.status },
168
+ });
169
+ }
170
+ const text = await response.text();
171
+ let payload = text;
172
+ try {
173
+ payload = JSON.parse(text);
174
+ }
175
+ catch {
176
+ // Not JSON; the raw text is the payload.
177
+ }
178
+ const selected = applyResponseSelector(payload, declaration.responseSelector);
179
+ const serialized = typeof selected === "string" ? selected : JSON.stringify(selected, null, 2);
180
+ const trimmed = truncateToolResult(selected, serialized ?? "", maxChars);
181
+ return {
182
+ output: trimmed.text || "(empty response)",
183
+ data: selected,
184
+ };
185
+ },
186
+ };
187
+ };
188
+ export const httpConnectorToDefinitions = (options) => options.connector.enabled === false
189
+ ? []
190
+ : options.connector.tools.map((declaration) => httpToolToDefinition(declaration, options));
@@ -0,0 +1,18 @@
1
+ import type { RunContext } from "../../runcontext/RunContextResolver.js";
2
+ import type { ToolRegistry } from "../../tools/ToolRegistry.js";
3
+ /**
4
+ * Registers a run's declared HTTP connectors into its existing tool registry,
5
+ * the same way MCP tools are attached. One registry remains the single source
6
+ * of truth regardless of where a tool came from.
7
+ */
8
+ export interface HttpAttachResult {
9
+ registered: string[];
10
+ warnings: string[];
11
+ }
12
+ export interface HttpAttachOptions {
13
+ context: RunContext;
14
+ toolRegistry: ToolRegistry;
15
+ fetchImpl?: typeof fetch;
16
+ }
17
+ export declare const attachHttpTools: (options: HttpAttachOptions) => HttpAttachResult;
18
+ //# sourceMappingURL=HttpToolSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpToolSource.d.ts","sourceRoot":"","sources":["../../../src/connectors/http/HttpToolSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE;;;;GAIG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,UAAU,CAAC;IACpB,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,eAAO,MAAM,eAAe,GAAI,SAAS,iBAAiB,KAAG,gBAoC5D,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { httpConnectorToDefinitions } from "./HttpToolDefinition.js";
2
+ export const attachHttpTools = (options) => {
3
+ const connectors = options.context.httpConnectors ?? [];
4
+ const registered = [];
5
+ const warnings = [];
6
+ for (const connector of connectors) {
7
+ let definitions;
8
+ try {
9
+ definitions = httpConnectorToDefinitions({ connector, fetchImpl: options.fetchImpl });
10
+ }
11
+ catch (error) {
12
+ // A connector declaring a mutating method is a configuration error, not a
13
+ // runtime one. Reject the connector and say why rather than silently
14
+ // dropping it or, worse, allowing the write.
15
+ warnings.push(`http_connector_rejected:${connector.name}:${error instanceof Error ? error.message : String(error)}`);
16
+ continue;
17
+ }
18
+ for (const definition of definitions) {
19
+ try {
20
+ options.toolRegistry.register(definition);
21
+ registered.push(definition.name);
22
+ }
23
+ catch (error) {
24
+ warnings.push(`http_tool_registration_failed:${definition.name}:${error instanceof Error ? error.message : String(error)}`);
25
+ }
26
+ }
27
+ }
28
+ return { registered, warnings };
29
+ };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Minimal MCP client.
3
+ *
4
+ * Deliberately covers only `tools/list` and `tools/call`. Resources and prompts
5
+ * are deferred because no requirement in `codali_workflow.txt` needs them, and
6
+ * an unused surface is still a surface to maintain and secure.
7
+ *
8
+ * The transports come from the official SDK rather than being hand-rolled, so
9
+ * protocol lifecycle, framing, and session handling stay the SDK's problem.
10
+ * Only the two transports the spec currently defines are offered — stdio and
11
+ * Streamable HTTP. There is no separate legacy HTTP+SSE transport: current MCP
12
+ * carries SSE *within* Streamable HTTP where it is needed.
13
+ */
14
+ /**
15
+ * Protocol version this build was written and tested against. Asserted in
16
+ * tests so an SDK bump that changes the wire protocol fails loudly here rather
17
+ * than silently against a live server.
18
+ */
19
+ export declare const CODALI_MCP_PROTOCOL_VERSION = "2025-11-25";
20
+ export declare const CODALI_MCP_CLIENT_INFO: {
21
+ readonly name: "codali";
22
+ readonly version: "1.0.0";
23
+ };
24
+ export type McpTransportKind = "stdio" | "http";
25
+ export interface McpStdioTransportConfig {
26
+ transport: "stdio";
27
+ command: string;
28
+ args?: string[];
29
+ env?: Record<string, string>;
30
+ cwd?: string;
31
+ }
32
+ export interface McpHttpTransportConfig {
33
+ transport: "http";
34
+ url: string;
35
+ headers?: Record<string, string>;
36
+ }
37
+ export type McpTransportConfig = McpStdioTransportConfig | McpHttpTransportConfig;
38
+ export interface McpServerConfig {
39
+ name: string;
40
+ enabled?: boolean;
41
+ timeoutMs?: number;
42
+ /**
43
+ * Operator's declaration that this server's exposed tools are read-only.
44
+ *
45
+ * Defaults to `false`, and that default is deliberate. A server's own
46
+ * `readOnlyHint` cannot be trusted — it is the party being constrained
47
+ * vouching for itself — and Codali has no other way to know whether
48
+ * `edit_file` edits a file. So an unvetted server's tools are treated as
49
+ * capable of mutation, which keeps them out of the default tool set until an
50
+ * operator either declares the server read-only or names the specific tools
51
+ * to expose via `allowTools`.
52
+ */
53
+ readOnly?: boolean;
54
+ /**
55
+ * Tools to expose from this server. Absent means all discovered tools.
56
+ * Deny always wins over allow.
57
+ */
58
+ allowTools?: string[];
59
+ denyTools?: string[];
60
+ }
61
+ export type McpServerDefinition = McpServerConfig & McpTransportConfig;
62
+ export interface McpToolDefinition {
63
+ name: string;
64
+ description?: string;
65
+ inputSchema?: Record<string, unknown>;
66
+ outputSchema?: Record<string, unknown>;
67
+ /**
68
+ * Server-supplied hints (`readOnlyHint`, `destructiveHint`, …).
69
+ *
70
+ * Retained for display and diagnostics only. These are **not** trusted for
71
+ * security decisions: a server declaring itself read-only is an assertion by
72
+ * the very party being constrained. Codali policy decides what may run.
73
+ */
74
+ annotations?: Record<string, unknown>;
75
+ }
76
+ export interface McpCallResult {
77
+ ok: boolean;
78
+ text: string;
79
+ structured?: unknown;
80
+ raw: unknown;
81
+ }
82
+ export declare class McpClientError extends Error {
83
+ readonly code: string;
84
+ readonly server: string;
85
+ readonly retryable: boolean;
86
+ constructor(code: string, server: string, message: string, options?: {
87
+ retryable?: boolean;
88
+ cause?: unknown;
89
+ });
90
+ }
91
+ /**
92
+ * Flattens MCP content blocks into text the model can read. Non-text blocks are
93
+ * described rather than dropped, so a run never silently loses a result it
94
+ * cannot render.
95
+ */
96
+ export declare const flattenMcpContent: (content: unknown) => string;
97
+ export interface McpClientOptions {
98
+ definition: McpServerDefinition;
99
+ /** Injected in tests to avoid spawning processes or opening sockets. */
100
+ createClient?: () => Promise<McpTransportClient>;
101
+ }
102
+ /** The slice of the SDK client this wrapper depends on. */
103
+ export interface McpTransportClient {
104
+ listTools(params?: Record<string, unknown>, options?: {
105
+ timeout?: number;
106
+ }): Promise<{
107
+ tools: unknown[];
108
+ }>;
109
+ callTool(params: {
110
+ name: string;
111
+ arguments?: Record<string, unknown>;
112
+ }, resultSchema?: unknown, options?: {
113
+ timeout?: number;
114
+ }): Promise<unknown>;
115
+ close(): Promise<void>;
116
+ }
117
+ export declare class McpClient {
118
+ private readonly options;
119
+ private client?;
120
+ private connecting?;
121
+ constructor(options: McpClientOptions);
122
+ get name(): string;
123
+ private get timeoutMs();
124
+ /** Connects lazily; concurrent callers share one in-flight connection. */
125
+ private ensureClient;
126
+ private connect;
127
+ listTools(): Promise<McpToolDefinition[]>;
128
+ callTool(name: string, args: unknown): Promise<McpCallResult>;
129
+ close(): Promise<void>;
130
+ }
131
+ export declare const createMcpClient: (options: McpClientOptions) => McpClient;
132
+ //# sourceMappingURL=McpClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"McpClient.d.ts","sourceRoot":"","sources":["../../../src/connectors/mcp/McpClient.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;AAEH;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,eAAe,CAAC;AAkCxD,eAAO,MAAM,sBAAsB;;;CAGzB,CAAC;AAIX,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhD,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,kBAAkB,GAAG,uBAAuB,GAAG,sBAAsB,CAAC;AAElF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC;CACd;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAG1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAWzD;AAKD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAS,OAAO,KAAG,MAkBpD,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,mBAAmB,CAAC;IAChC,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAClD;AAED,2DAA2D;AAC3D,MAAM,WAAW,kBAAkB;IACjC,SAAS,CACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7B,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAC;IACjC,QAAQ,CACN,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAC7D,YAAY,CAAC,EAAE,OAAO,EACtB,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qBAAa,SAAS;IAIR,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,MAAM,CAAC,CAAqB;IACpC,OAAO,CAAC,UAAU,CAAC,CAA8B;gBAEpB,OAAO,EAAE,gBAAgB;IAEtD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,KAAK,SAAS,GAEpB;IAED,0EAA0E;YAC5D,YAAY;YAWZ,OAAO;IA6Cf,SAAS,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAqCzC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IAmC7D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAW7B;AAED,eAAO,MAAM,eAAe,GAAI,SAAS,gBAAgB,KAAG,SACpC,CAAC"}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Minimal MCP client.
3
+ *
4
+ * Deliberately covers only `tools/list` and `tools/call`. Resources and prompts
5
+ * are deferred because no requirement in `codali_workflow.txt` needs them, and
6
+ * an unused surface is still a surface to maintain and secure.
7
+ *
8
+ * The transports come from the official SDK rather than being hand-rolled, so
9
+ * protocol lifecycle, framing, and session handling stay the SDK's problem.
10
+ * Only the two transports the spec currently defines are offered — stdio and
11
+ * Streamable HTTP. There is no separate legacy HTTP+SSE transport: current MCP
12
+ * carries SSE *within* Streamable HTTP where it is needed.
13
+ */
14
+ /**
15
+ * Protocol version this build was written and tested against. Asserted in
16
+ * tests so an SDK bump that changes the wire protocol fails loudly here rather
17
+ * than silently against a live server.
18
+ */
19
+ export const CODALI_MCP_PROTOCOL_VERSION = "2025-11-25";
20
+ /**
21
+ * The SDK is imported lazily, only when a run actually connects to an MCP
22
+ * server.
23
+ *
24
+ * mswarm vendors Codali's built output and does not depend on the SDK itself,
25
+ * so a top-level import broke every mswarm test with a module-resolution error
26
+ * for a package its cloud path never uses. Loading on demand keeps the
27
+ * dependency where it belongs: paid for by whoever configures a server.
28
+ */
29
+ const loadMcpSdk = async () => {
30
+ try {
31
+ const [clientModule, stdioModule, httpModule] = await Promise.all([
32
+ import("@modelcontextprotocol/sdk/client/index.js"),
33
+ import("@modelcontextprotocol/sdk/client/stdio.js"),
34
+ import("@modelcontextprotocol/sdk/client/streamableHttp.js"),
35
+ ]);
36
+ return {
37
+ Client: clientModule.Client,
38
+ StdioClientTransport: stdioModule.StdioClientTransport,
39
+ StreamableHTTPClientTransport: httpModule.StreamableHTTPClientTransport,
40
+ };
41
+ }
42
+ catch (error) {
43
+ throw new McpClientError("mcp_sdk_unavailable", "sdk", "MCP support needs @modelcontextprotocol/sdk, which is not installed in this build. " +
44
+ "Remove the configured mcpServers, or install the package.", { cause: error });
45
+ }
46
+ };
47
+ export const CODALI_MCP_CLIENT_INFO = {
48
+ name: "codali",
49
+ version: "1.0.0",
50
+ };
51
+ const DEFAULT_TIMEOUT_MS = 30000;
52
+ export class McpClientError extends Error {
53
+ constructor(code, server, message, options = {}) {
54
+ super(message);
55
+ this.name = "McpClientError";
56
+ this.code = code;
57
+ this.server = server;
58
+ this.retryable = options.retryable ?? false;
59
+ if (options.cause !== undefined) {
60
+ this.cause = options.cause;
61
+ }
62
+ }
63
+ }
64
+ const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
65
+ /**
66
+ * Flattens MCP content blocks into text the model can read. Non-text blocks are
67
+ * described rather than dropped, so a run never silently loses a result it
68
+ * cannot render.
69
+ */
70
+ export const flattenMcpContent = (content) => {
71
+ if (!Array.isArray(content))
72
+ return "";
73
+ const parts = [];
74
+ for (const block of content) {
75
+ if (!isRecord(block))
76
+ continue;
77
+ const type = typeof block.type === "string" ? block.type : "";
78
+ if (type === "text" && typeof block.text === "string") {
79
+ parts.push(block.text);
80
+ }
81
+ else if (type === "resource" && isRecord(block.resource)) {
82
+ const resource = block.resource;
83
+ const text = typeof resource.text === "string" ? resource.text : undefined;
84
+ const uri = typeof resource.uri === "string" ? resource.uri : "unknown";
85
+ parts.push(text ?? `[resource ${uri}]`);
86
+ }
87
+ else if (type) {
88
+ parts.push(`[${type} content omitted]`);
89
+ }
90
+ }
91
+ return parts.join("\n").trim();
92
+ };
93
+ export class McpClient {
94
+ constructor(options) {
95
+ this.options = options;
96
+ }
97
+ get name() {
98
+ return this.options.definition.name;
99
+ }
100
+ get timeoutMs() {
101
+ return this.options.definition.timeoutMs ?? DEFAULT_TIMEOUT_MS;
102
+ }
103
+ /** Connects lazily; concurrent callers share one in-flight connection. */
104
+ async ensureClient() {
105
+ if (this.client)
106
+ return this.client;
107
+ this.connecting ?? (this.connecting = this.connect());
108
+ try {
109
+ this.client = await this.connecting;
110
+ return this.client;
111
+ }
112
+ finally {
113
+ this.connecting = undefined;
114
+ }
115
+ }
116
+ async connect() {
117
+ if (this.options.createClient) {
118
+ return this.options.createClient();
119
+ }
120
+ const definition = this.options.definition;
121
+ const sdk = await loadMcpSdk();
122
+ const client = new sdk.Client(CODALI_MCP_CLIENT_INFO, {
123
+ capabilities: {},
124
+ });
125
+ try {
126
+ if (definition.transport === "stdio") {
127
+ await client.connect(new sdk.StdioClientTransport({
128
+ command: definition.command,
129
+ args: definition.args,
130
+ env: definition.env,
131
+ cwd: definition.cwd,
132
+ // Server diagnostics must not be interleaved into Codali's own
133
+ // stdout, which carries the answer.
134
+ stderr: "pipe",
135
+ }));
136
+ }
137
+ else {
138
+ await client.connect(new sdk.StreamableHTTPClientTransport(new URL(definition.url), {
139
+ requestInit: definition.headers ? { headers: definition.headers } : undefined,
140
+ }));
141
+ }
142
+ }
143
+ catch (error) {
144
+ throw new McpClientError("mcp_connect_failed", definition.name, `Could not connect to MCP server "${definition.name}": ${error instanceof Error ? error.message : String(error)}`, { retryable: true, cause: error });
145
+ }
146
+ return client;
147
+ }
148
+ async listTools() {
149
+ const client = await this.ensureClient();
150
+ let response;
151
+ try {
152
+ response = await client.listTools(undefined, { timeout: this.timeoutMs });
153
+ }
154
+ catch (error) {
155
+ throw new McpClientError("mcp_list_tools_failed", this.name, `tools/list failed for "${this.name}": ${error instanceof Error ? error.message : String(error)}`, { retryable: true, cause: error });
156
+ }
157
+ const definition = this.options.definition;
158
+ const allow = definition.allowTools ? new Set(definition.allowTools) : undefined;
159
+ const deny = new Set(definition.denyTools ?? []);
160
+ const tools = [];
161
+ for (const entry of response.tools ?? []) {
162
+ if (!isRecord(entry) || typeof entry.name !== "string")
163
+ continue;
164
+ // Deny wins over allow, always.
165
+ if (deny.has(entry.name))
166
+ continue;
167
+ if (allow && !allow.has(entry.name))
168
+ continue;
169
+ tools.push({
170
+ name: entry.name,
171
+ description: typeof entry.description === "string" ? entry.description : undefined,
172
+ inputSchema: isRecord(entry.inputSchema) ? entry.inputSchema : undefined,
173
+ outputSchema: isRecord(entry.outputSchema) ? entry.outputSchema : undefined,
174
+ annotations: isRecord(entry.annotations) ? entry.annotations : undefined,
175
+ });
176
+ }
177
+ return tools;
178
+ }
179
+ async callTool(name, args) {
180
+ const client = await this.ensureClient();
181
+ let raw;
182
+ try {
183
+ raw = await client.callTool({ name, arguments: isRecord(args) ? args : {} }, undefined, { timeout: this.timeoutMs });
184
+ }
185
+ catch (error) {
186
+ throw new McpClientError("mcp_call_tool_failed", this.name, `tools/call ${name} failed on "${this.name}": ${error instanceof Error ? error.message : String(error)}`, { retryable: true, cause: error });
187
+ }
188
+ const record = isRecord(raw) ? raw : {};
189
+ // MCP reports tool-level failures in-band via isError rather than as a
190
+ // protocol error, so a successful transport round trip can still be a
191
+ // failed call.
192
+ const isError = record.isError === true;
193
+ const text = flattenMcpContent(record.content);
194
+ return {
195
+ ok: !isError,
196
+ text: text || (isError ? "Tool reported an error with no message." : ""),
197
+ structured: record.structuredContent,
198
+ raw,
199
+ };
200
+ }
201
+ async close() {
202
+ const client = this.client;
203
+ this.client = undefined;
204
+ if (!client)
205
+ return;
206
+ try {
207
+ await client.close();
208
+ }
209
+ catch {
210
+ // A server that fails to close cleanly must not fail the run; the process
211
+ // is exiting or the connection is already gone.
212
+ }
213
+ }
214
+ }
215
+ export const createMcpClient = (options) => new McpClient(options);
@@ -0,0 +1,75 @@
1
+ import type { ToolDefinition } from "../../tools/ToolTypes.js";
2
+ import { McpClient, type McpServerDefinition } from "./McpClient.js";
3
+ /**
4
+ * Owns the lifetime of every configured MCP server.
5
+ *
6
+ * Three properties matter more than features here:
7
+ *
8
+ * - **Isolation.** One broken server degrades its own tools to unavailable
9
+ * and never fails the run or affects another server.
10
+ * - **Boundedness.** Every call has a timeout, at most one retry, and
11
+ * competes for a global concurrency budget, so a slow server cannot
12
+ * monopolise a run.
13
+ * - **Cleanup.** stdio servers are child processes. They must be shut down
14
+ * even when a run fails, or the CLI leaves orphans behind.
15
+ */
16
+ /** Concurrent in-flight MCP calls across all servers. */
17
+ export declare const DEFAULT_MAX_CONCURRENT_CALLS = 4;
18
+ export interface McpServerHealth {
19
+ name: string;
20
+ status: "connected" | "failed" | "disabled";
21
+ toolCount: number;
22
+ error?: string;
23
+ discoveryMs?: number;
24
+ }
25
+ export interface McpServerRegistryOptions {
26
+ servers: readonly McpServerDefinition[];
27
+ maxConcurrentCalls?: number;
28
+ /** Injected in tests. */
29
+ createClient?: (definition: McpServerDefinition) => McpClient;
30
+ onEvent?: (event: McpRegistryEvent) => void;
31
+ }
32
+ export type McpRegistryEvent = {
33
+ type: "server_connected";
34
+ server: string;
35
+ toolCount: number;
36
+ durationMs: number;
37
+ } | {
38
+ type: "server_failed";
39
+ server: string;
40
+ error: string;
41
+ } | {
42
+ type: "server_skipped";
43
+ server: string;
44
+ reason: string;
45
+ };
46
+ export declare class McpServerRegistry {
47
+ private readonly options;
48
+ private readonly clients;
49
+ private readonly health;
50
+ private readonly semaphore;
51
+ private discovered;
52
+ constructor(options: McpServerRegistryOptions);
53
+ /**
54
+ * Connects to every enabled server and returns their tools as Codali tool
55
+ * definitions. Servers are contacted in parallel; a failure is recorded as
56
+ * unhealthy rather than thrown, so one bad entry in a tenant's config cannot
57
+ * take down the run.
58
+ */
59
+ discoverTools(): Promise<ToolDefinition[]>;
60
+ /**
61
+ * Wraps a client so every tool call is bounded by the concurrency budget and
62
+ * retried once on a transport failure. Retrying is safe here because Phase 2
63
+ * connectors are read-only.
64
+ */
65
+ private wrapClient;
66
+ healthReport(): McpServerHealth[];
67
+ get isDiscovered(): boolean;
68
+ /**
69
+ * Shuts down every connection. Must run even when a run fails: stdio servers
70
+ * are child processes and would otherwise be orphaned.
71
+ */
72
+ close(): Promise<void>;
73
+ }
74
+ export declare const createMcpServerRegistry: (options: McpServerRegistryOptions) => McpServerRegistry;
75
+ //# sourceMappingURL=McpServerRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"McpServerRegistry.d.ts","sourceRoot":"","sources":["../../../src/connectors/mcp/McpServerRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EACL,SAAS,EAET,KAAK,mBAAmB,EAEzB,MAAM,gBAAgB,CAAC;AAGxB;;;;;;;;;;;;GAYG;AAEH,yDAAyD;AACzD,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAK9C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,SAAS,CAAC;IAC9D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACnF;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA0B/D,qBAAa,iBAAiB;IAMhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,UAAU,CAAS;gBAEE,OAAO,EAAE,wBAAwB;IAM9D;;;;;OAKG;IACG,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAmFhD;;;;OAIG;IACH,OAAO,CAAC,UAAU;IA4BlB,YAAY,IAAI,eAAe,EAAE;IAIjC,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B;AAED,eAAO,MAAM,uBAAuB,GAClC,SAAS,wBAAwB,KAChC,iBAAmD,CAAC"}