@inkeep/agents-run-api 0.0.0-dev-20250910232631
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/LICENSE.md +49 -0
- package/README.md +117 -0
- package/dist/__tests__/setup.d.ts +4 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +80 -0
- package/dist/__tests__/utils/testProject.d.ts +18 -0
- package/dist/__tests__/utils/testProject.d.ts.map +1 -0
- package/dist/__tests__/utils/testProject.js +26 -0
- package/dist/__tests__/utils/testRequest.d.ts +8 -0
- package/dist/__tests__/utils/testRequest.d.ts.map +1 -0
- package/dist/__tests__/utils/testRequest.js +32 -0
- package/dist/__tests__/utils/testTenant.d.ts +64 -0
- package/dist/__tests__/utils/testTenant.d.ts.map +1 -0
- package/dist/__tests__/utils/testTenant.js +71 -0
- package/dist/a2a/client.d.ts +182 -0
- package/dist/a2a/client.d.ts.map +1 -0
- package/dist/a2a/client.js +645 -0
- package/dist/a2a/handlers.d.ts +4 -0
- package/dist/a2a/handlers.d.ts.map +1 -0
- package/dist/a2a/handlers.js +656 -0
- package/dist/a2a/transfer.d.ts +18 -0
- package/dist/a2a/transfer.d.ts.map +1 -0
- package/dist/a2a/transfer.js +22 -0
- package/dist/a2a/types.d.ts +63 -0
- package/dist/a2a/types.d.ts.map +1 -0
- package/dist/a2a/types.js +1 -0
- package/dist/agents/Agent.d.ts +151 -0
- package/dist/agents/Agent.d.ts.map +1 -0
- package/dist/agents/Agent.js +1164 -0
- package/dist/agents/ModelFactory.d.ts +62 -0
- package/dist/agents/ModelFactory.d.ts.map +1 -0
- package/dist/agents/ModelFactory.js +208 -0
- package/dist/agents/SystemPromptBuilder.d.ts +14 -0
- package/dist/agents/SystemPromptBuilder.d.ts.map +1 -0
- package/dist/agents/SystemPromptBuilder.js +62 -0
- package/dist/agents/ToolSessionManager.d.ts +53 -0
- package/dist/agents/ToolSessionManager.d.ts.map +1 -0
- package/dist/agents/ToolSessionManager.js +106 -0
- package/dist/agents/artifactTools.d.ts +30 -0
- package/dist/agents/artifactTools.d.ts.map +1 -0
- package/dist/agents/artifactTools.js +463 -0
- package/dist/agents/generateTaskHandler.d.ts +41 -0
- package/dist/agents/generateTaskHandler.d.ts.map +1 -0
- package/dist/agents/generateTaskHandler.js +350 -0
- package/dist/agents/relationTools.d.ts +35 -0
- package/dist/agents/relationTools.d.ts.map +1 -0
- package/dist/agents/relationTools.js +246 -0
- package/dist/agents/types.d.ts +23 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +1 -0
- package/dist/agents/versions/V1Config.d.ts +21 -0
- package/dist/agents/versions/V1Config.d.ts.map +1 -0
- package/dist/agents/versions/V1Config.js +285 -0
- package/dist/app.d.ts +5 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +219 -0
- package/dist/data/agentGraph.d.ts +4 -0
- package/dist/data/agentGraph.d.ts.map +1 -0
- package/dist/data/agentGraph.js +73 -0
- package/dist/data/agents.d.ts +4 -0
- package/dist/data/agents.d.ts.map +1 -0
- package/dist/data/agents.js +78 -0
- package/dist/data/conversations.d.ts +59 -0
- package/dist/data/conversations.d.ts.map +1 -0
- package/dist/data/conversations.js +216 -0
- package/dist/data/db/clean.d.ts +6 -0
- package/dist/data/db/clean.d.ts.map +1 -0
- package/dist/data/db/clean.js +77 -0
- package/dist/data/db/dbClient.d.ts +3 -0
- package/dist/data/db/dbClient.d.ts.map +1 -0
- package/dist/data/db/dbClient.js +13 -0
- package/dist/env.d.ts +45 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +64 -0
- package/dist/handlers/executionHandler.d.ts +36 -0
- package/dist/handlers/executionHandler.d.ts.map +1 -0
- package/dist/handlers/executionHandler.js +415 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/instrumentation.d.ts +13 -0
- package/dist/instrumentation.d.ts.map +1 -0
- package/dist/instrumentation.js +66 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +32 -0
- package/dist/middleware/api-key-auth.d.ts +22 -0
- package/dist/middleware/api-key-auth.d.ts.map +1 -0
- package/dist/middleware/api-key-auth.js +139 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +1 -0
- package/dist/openapi.d.ts +2 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +36 -0
- package/dist/routes/agents.d.ts +10 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +158 -0
- package/dist/routes/chat.d.ts +10 -0
- package/dist/routes/chat.d.ts.map +1 -0
- package/dist/routes/chat.js +307 -0
- package/dist/routes/chatDataStream.d.ts +10 -0
- package/dist/routes/chatDataStream.d.ts.map +1 -0
- package/dist/routes/chatDataStream.js +185 -0
- package/dist/routes/mcp.d.ts +10 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +500 -0
- package/dist/tracer.d.ts +24 -0
- package/dist/tracer.d.ts.map +1 -0
- package/dist/tracer.js +107 -0
- package/dist/types/chat.d.ts +25 -0
- package/dist/types/chat.d.ts.map +1 -0
- package/dist/types/chat.js +1 -0
- package/dist/types/execution-context.d.ts +14 -0
- package/dist/types/execution-context.d.ts.map +1 -0
- package/dist/types/execution-context.js +14 -0
- package/dist/utils/agent-operations.d.ts +93 -0
- package/dist/utils/agent-operations.d.ts.map +1 -0
- package/dist/utils/agent-operations.js +78 -0
- package/dist/utils/artifact-component-schema.d.ts +29 -0
- package/dist/utils/artifact-component-schema.d.ts.map +1 -0
- package/dist/utils/artifact-component-schema.js +119 -0
- package/dist/utils/artifact-parser.d.ts +71 -0
- package/dist/utils/artifact-parser.d.ts.map +1 -0
- package/dist/utils/artifact-parser.js +253 -0
- package/dist/utils/cleanup.d.ts +19 -0
- package/dist/utils/cleanup.d.ts.map +1 -0
- package/dist/utils/cleanup.js +66 -0
- package/dist/utils/data-component-schema.d.ts +6 -0
- package/dist/utils/data-component-schema.d.ts.map +1 -0
- package/dist/utils/data-component-schema.js +43 -0
- package/dist/utils/graph-session.d.ts +230 -0
- package/dist/utils/graph-session.d.ts.map +1 -0
- package/dist/utils/graph-session.js +1199 -0
- package/dist/utils/incremental-stream-parser.d.ts +62 -0
- package/dist/utils/incremental-stream-parser.d.ts.map +1 -0
- package/dist/utils/incremental-stream-parser.js +330 -0
- package/dist/utils/response-formatter.d.ts +26 -0
- package/dist/utils/response-formatter.d.ts.map +1 -0
- package/dist/utils/response-formatter.js +158 -0
- package/dist/utils/stream-helpers.d.ts +186 -0
- package/dist/utils/stream-helpers.d.ts.map +1 -0
- package/dist/utils/stream-helpers.js +603 -0
- package/dist/utils/stream-registry.d.ts +18 -0
- package/dist/utils/stream-registry.d.ts.map +1 -0
- package/dist/utils/stream-registry.js +33 -0
- package/package.json +95 -0
- package/templates/v1/artifact.xml +7 -0
- package/templates/v1/data-component.xml +9 -0
- package/templates/v1/system-prompt.xml +52 -0
- package/templates/v1/thinking-preparation.xml +34 -0
- package/templates/v1/tool.xml +12 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
|
|
2
|
+
import { contextValidationMiddleware, HeadersScopeSchema } from '@inkeep/agents-core';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
5
|
+
import { z } from 'zod/v3';
|
|
6
|
+
// Type bridge for MCP SDK compatibility with Zod v4
|
|
7
|
+
function createMCPSchema(schema) {
|
|
8
|
+
return schema;
|
|
9
|
+
}
|
|
10
|
+
import { createMessage, createOrGetConversation, getAgentById, getAgentGraphWithDefaultAgent, getConversation, getRequestExecutionContext, handleContextResolution, updateConversation, } from '@inkeep/agents-core';
|
|
11
|
+
import { trace } from '@opentelemetry/api';
|
|
12
|
+
import { toFetchResponse, toReqRes } from 'fetch-to-node';
|
|
13
|
+
import { nanoid } from 'nanoid';
|
|
14
|
+
import dbClient from '../data/db/dbClient';
|
|
15
|
+
import { ExecutionHandler } from '../handlers/executionHandler';
|
|
16
|
+
import { getLogger } from '../logger';
|
|
17
|
+
import { createMCPStreamHelper } from '../utils/stream-helpers';
|
|
18
|
+
const logger = getLogger('mcp');
|
|
19
|
+
/**
|
|
20
|
+
* Singleton mock response object for spoof initialization
|
|
21
|
+
*/
|
|
22
|
+
class MockResponseSingleton {
|
|
23
|
+
static instance;
|
|
24
|
+
mockRes;
|
|
25
|
+
constructor() {
|
|
26
|
+
// Create the mock response object once
|
|
27
|
+
this.mockRes = {
|
|
28
|
+
statusCode: 200,
|
|
29
|
+
headers: {},
|
|
30
|
+
setHeader: function (name, value) {
|
|
31
|
+
this.headers[name] = value;
|
|
32
|
+
},
|
|
33
|
+
getHeaders: function () {
|
|
34
|
+
return this.headers;
|
|
35
|
+
},
|
|
36
|
+
end: () => { },
|
|
37
|
+
write: () => { },
|
|
38
|
+
writeHead: () => { },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
static getInstance() {
|
|
42
|
+
if (!MockResponseSingleton.instance) {
|
|
43
|
+
MockResponseSingleton.instance = new MockResponseSingleton();
|
|
44
|
+
}
|
|
45
|
+
return MockResponseSingleton.instance;
|
|
46
|
+
}
|
|
47
|
+
getMockResponse() {
|
|
48
|
+
// Reset headers for each use to avoid state pollution
|
|
49
|
+
this.mockRes.headers = {};
|
|
50
|
+
this.mockRes.statusCode = 200;
|
|
51
|
+
return this.mockRes;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a spoof initialization message with the given protocol version
|
|
56
|
+
* Extracted as a pure function for better testability and reuse
|
|
57
|
+
*/
|
|
58
|
+
const createSpoofInitMessage = (mcpProtocolVersion) => ({
|
|
59
|
+
method: 'initialize',
|
|
60
|
+
params: {
|
|
61
|
+
protocolVersion: mcpProtocolVersion || '2025-06-18',
|
|
62
|
+
capabilities: {
|
|
63
|
+
tools: true,
|
|
64
|
+
prompts: true,
|
|
65
|
+
resources: false,
|
|
66
|
+
logging: false,
|
|
67
|
+
roots: { listChanged: false },
|
|
68
|
+
},
|
|
69
|
+
clientInfo: {
|
|
70
|
+
name: 'inkeep-mcp-server',
|
|
71
|
+
version: '1.0.0',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
jsonrpc: '2.0',
|
|
75
|
+
id: 0,
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* Spoofs an initialization message to set the transport's initialized flag
|
|
79
|
+
* This is necessary when recreating transports for existing sessions because the transport expects to have received an initialization message from the client.
|
|
80
|
+
*/
|
|
81
|
+
const spoofTransportInitialization = async (transport, req, sessionId, mcpProtocolVersion) => {
|
|
82
|
+
logger.info({ sessionId }, 'Spoofing initialization message to set transport state');
|
|
83
|
+
const spoofInitMessage = createSpoofInitMessage(mcpProtocolVersion);
|
|
84
|
+
const mockRes = MockResponseSingleton.getInstance().getMockResponse();
|
|
85
|
+
try {
|
|
86
|
+
// Send the spoof initialization to set internal state. The transport errors but it still sets the initialized flag.
|
|
87
|
+
await transport.handleRequest(req, mockRes, spoofInitMessage);
|
|
88
|
+
logger.info({ sessionId }, 'Successfully spoofed initialization');
|
|
89
|
+
}
|
|
90
|
+
catch (spoofError) {
|
|
91
|
+
logger.warn({ sessionId, error: spoofError }, 'Spoof initialization failed, continuing anyway');
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const validateSession = async (req, res, body, tenantId, projectId, graphId) => {
|
|
95
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
96
|
+
logger.info({ sessionId }, 'Received MCP session ID');
|
|
97
|
+
if (!sessionId) {
|
|
98
|
+
logger.info({ body }, 'Missing session ID');
|
|
99
|
+
res.writeHead(400).end(JSON.stringify({
|
|
100
|
+
jsonrpc: '2.0',
|
|
101
|
+
error: { code: -32602, message: 'Bad Request: Mcp-Session-Id header is required' },
|
|
102
|
+
id: null,
|
|
103
|
+
}));
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
else if (Array.isArray(sessionId)) {
|
|
107
|
+
res.writeHead(400).end(JSON.stringify({
|
|
108
|
+
jsonrpc: '2.0',
|
|
109
|
+
error: {
|
|
110
|
+
code: -32000,
|
|
111
|
+
message: 'Bad Request: Mcp-Session-Id header must be a single value',
|
|
112
|
+
},
|
|
113
|
+
id: null,
|
|
114
|
+
}));
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
// Get conversation (which stores our session data)
|
|
118
|
+
const conversation = await getConversation(dbClient)({
|
|
119
|
+
scopes: { tenantId, projectId },
|
|
120
|
+
conversationId: sessionId,
|
|
121
|
+
});
|
|
122
|
+
// After line 342 - Add logging to debug conversation lookup
|
|
123
|
+
logger.info({
|
|
124
|
+
sessionId,
|
|
125
|
+
conversationFound: !!conversation,
|
|
126
|
+
sessionType: conversation?.metadata?.sessionData?.sessionType,
|
|
127
|
+
storedGraphId: conversation?.metadata?.sessionData?.graphId,
|
|
128
|
+
requestGraphId: graphId,
|
|
129
|
+
}, 'Conversation lookup result');
|
|
130
|
+
if (!conversation ||
|
|
131
|
+
conversation.metadata?.sessionData?.sessionType !== 'mcp' ||
|
|
132
|
+
conversation.metadata?.sessionData?.graphId !== graphId) {
|
|
133
|
+
logger.info({ sessionId, conversationId: conversation?.id }, 'MCP session not found or invalid');
|
|
134
|
+
res.writeHead(404).end(JSON.stringify({
|
|
135
|
+
jsonrpc: '2.0',
|
|
136
|
+
error: {
|
|
137
|
+
code: -32001,
|
|
138
|
+
message: 'Session not found',
|
|
139
|
+
},
|
|
140
|
+
id: null,
|
|
141
|
+
}));
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return conversation;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Sets up tracing attributes for the active span
|
|
148
|
+
*/
|
|
149
|
+
const setupTracing = (conversationId, tenantId, graphId) => {
|
|
150
|
+
const activeSpan = trace.getActiveSpan();
|
|
151
|
+
if (activeSpan) {
|
|
152
|
+
activeSpan.setAttributes({
|
|
153
|
+
'conversation.id': conversationId,
|
|
154
|
+
'tenant.id': tenantId,
|
|
155
|
+
'graph.id': graphId,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Processes and stores the user message
|
|
161
|
+
*/
|
|
162
|
+
const processUserMessage = async (tenantId, projectId, conversationId, query) => {
|
|
163
|
+
const messageSpan = trace.getActiveSpan();
|
|
164
|
+
if (messageSpan) {
|
|
165
|
+
messageSpan.setAttributes({
|
|
166
|
+
'message.content': query,
|
|
167
|
+
'message.timestamp': Date.now(),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
await createMessage(dbClient)({
|
|
171
|
+
id: nanoid(),
|
|
172
|
+
tenantId,
|
|
173
|
+
projectId,
|
|
174
|
+
conversationId,
|
|
175
|
+
role: 'user',
|
|
176
|
+
content: {
|
|
177
|
+
text: query,
|
|
178
|
+
},
|
|
179
|
+
visibility: 'user-facing',
|
|
180
|
+
messageType: 'chat',
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Executes the agent query and returns the result
|
|
185
|
+
*/
|
|
186
|
+
const executeAgentQuery = async (executionContext, conversationId, query, defaultAgentId) => {
|
|
187
|
+
const requestId = `mcp-${Date.now()}`;
|
|
188
|
+
const mcpStreamHelper = createMCPStreamHelper();
|
|
189
|
+
const executionHandler = new ExecutionHandler();
|
|
190
|
+
const result = await executionHandler.execute({
|
|
191
|
+
executionContext,
|
|
192
|
+
conversationId,
|
|
193
|
+
userMessage: query,
|
|
194
|
+
initialAgentId: defaultAgentId,
|
|
195
|
+
requestId,
|
|
196
|
+
sseHelper: mcpStreamHelper,
|
|
197
|
+
});
|
|
198
|
+
logger.info({ result }, `Execution completed: ${result.success ? 'success' : 'failed'} after ${result.iterations} iterations`);
|
|
199
|
+
if (!result.success) {
|
|
200
|
+
return {
|
|
201
|
+
content: [
|
|
202
|
+
{
|
|
203
|
+
type: 'text',
|
|
204
|
+
text: result.error ||
|
|
205
|
+
`Sorry, I was unable to process your request at this time. Please try again.`,
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
isError: true,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: 'text',
|
|
215
|
+
text: result.response || 'No response generated',
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Creates and configures an MCP server for the given context
|
|
222
|
+
*/
|
|
223
|
+
const getServer = async (requestContext, executionContext, conversationId, credentialStores) => {
|
|
224
|
+
const { tenantId, projectId, graphId } = executionContext;
|
|
225
|
+
setupTracing(conversationId, tenantId, graphId);
|
|
226
|
+
const agentGraph = await getAgentGraphWithDefaultAgent(dbClient)({
|
|
227
|
+
scopes: { tenantId, projectId },
|
|
228
|
+
graphId: graphId,
|
|
229
|
+
});
|
|
230
|
+
if (!agentGraph) {
|
|
231
|
+
throw new Error('Agent graph not found');
|
|
232
|
+
}
|
|
233
|
+
const server = new McpServer({
|
|
234
|
+
name: 'inkeep-chat-api-server',
|
|
235
|
+
version: '1.0.0',
|
|
236
|
+
}, { capabilities: { logging: {} } });
|
|
237
|
+
// Register tools and prompts
|
|
238
|
+
server.tool('send-query-to-agent', `Send a query to the ${agentGraph.name} agent. The agent has the following description: ${agentGraph.description}`, {
|
|
239
|
+
query: createMCPSchema(z.string().describe('The query to send to the agent')),
|
|
240
|
+
}, async ({ query }) => {
|
|
241
|
+
try {
|
|
242
|
+
const defaultAgentId = agentGraph.defaultAgentId;
|
|
243
|
+
const agentInfo = await getAgentById(dbClient)({
|
|
244
|
+
scopes: { tenantId, projectId },
|
|
245
|
+
agentId: defaultAgentId,
|
|
246
|
+
});
|
|
247
|
+
if (!agentInfo) {
|
|
248
|
+
return {
|
|
249
|
+
content: [
|
|
250
|
+
{
|
|
251
|
+
type: 'text',
|
|
252
|
+
text: `Agent not found`,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
isError: true,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const resolvedContext = await handleContextResolution(tenantId, projectId, conversationId, graphId, requestContext, dbClient, credentialStores);
|
|
259
|
+
logger.info({
|
|
260
|
+
tenantId,
|
|
261
|
+
graphId,
|
|
262
|
+
conversationId,
|
|
263
|
+
hasContextConfig: !!agentGraph.contextConfigId,
|
|
264
|
+
hasRequestContext: !!requestContext,
|
|
265
|
+
hasValidatedContext: !!resolvedContext,
|
|
266
|
+
}, 'parameters');
|
|
267
|
+
await processUserMessage(tenantId, projectId, conversationId, query);
|
|
268
|
+
return executeAgentQuery(executionContext, conversationId, query, defaultAgentId);
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
return {
|
|
272
|
+
content: [
|
|
273
|
+
{
|
|
274
|
+
type: 'text',
|
|
275
|
+
text: `Error sending query: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
isError: true,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return server;
|
|
283
|
+
};
|
|
284
|
+
const app = new OpenAPIHono();
|
|
285
|
+
// Only apply context validation to POST requests (GET requests are for SSE streams)
|
|
286
|
+
app.use('/', async (c, next) => {
|
|
287
|
+
if (c.req.method === 'POST') {
|
|
288
|
+
return contextValidationMiddleware(dbClient)(c, next);
|
|
289
|
+
}
|
|
290
|
+
return next();
|
|
291
|
+
});
|
|
292
|
+
/**
|
|
293
|
+
* Validates request parameters and returns execution context if valid
|
|
294
|
+
*/
|
|
295
|
+
const validateRequestParameters = (c) => {
|
|
296
|
+
try {
|
|
297
|
+
const executionContext = getRequestExecutionContext(c);
|
|
298
|
+
const { tenantId, projectId, graphId } = executionContext;
|
|
299
|
+
getLogger('mcp').debug({ tenantId, projectId, graphId }, 'Extracted MCP entity parameters');
|
|
300
|
+
return { valid: true, executionContext };
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
getLogger('chat').warn({ error: error instanceof Error ? error.message : 'Unknown error' }, 'Failed to get execution context');
|
|
304
|
+
return {
|
|
305
|
+
valid: false,
|
|
306
|
+
response: c.json({
|
|
307
|
+
jsonrpc: '2.0',
|
|
308
|
+
error: { code: -32602, message: 'API key authentication required' },
|
|
309
|
+
id: null,
|
|
310
|
+
}, { status: 401 }),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Creates a new MCP session and handles initialization
|
|
316
|
+
*/
|
|
317
|
+
const handleInitializationRequest = async (body, executionContext, validatedContext, req, res, c, credentialStores) => {
|
|
318
|
+
const { tenantId, projectId, graphId } = executionContext;
|
|
319
|
+
logger.info({ body }, 'Received initialization request');
|
|
320
|
+
const sessionId = nanoid();
|
|
321
|
+
// Get the default agent for the graph
|
|
322
|
+
const agentGraph = await getAgentGraphWithDefaultAgent(dbClient)({
|
|
323
|
+
scopes: { tenantId, projectId },
|
|
324
|
+
graphId,
|
|
325
|
+
});
|
|
326
|
+
if (!agentGraph) {
|
|
327
|
+
return c.json({
|
|
328
|
+
jsonrpc: '2.0',
|
|
329
|
+
error: { code: -32001, message: 'Agent graph not found' },
|
|
330
|
+
id: body.id || null,
|
|
331
|
+
}, { status: 404 });
|
|
332
|
+
}
|
|
333
|
+
// Create/get conversation with MCP session metadata
|
|
334
|
+
const conversation = await createOrGetConversation(dbClient)({
|
|
335
|
+
id: sessionId,
|
|
336
|
+
tenantId,
|
|
337
|
+
projectId,
|
|
338
|
+
activeAgentId: agentGraph.defaultAgentId,
|
|
339
|
+
metadata: {
|
|
340
|
+
sessionData: {
|
|
341
|
+
graphId,
|
|
342
|
+
sessionType: 'mcp',
|
|
343
|
+
mcpProtocolVersion: c.req.header('mcp-protocol-version'),
|
|
344
|
+
initialized: false, // Track initialization state
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
logger.info({ sessionId, conversationId: conversation.id }, 'Created MCP session as conversation');
|
|
349
|
+
// Create fresh transport and server for this request
|
|
350
|
+
const transport = new StreamableHTTPServerTransport({
|
|
351
|
+
sessionIdGenerator: () => sessionId,
|
|
352
|
+
});
|
|
353
|
+
const server = await getServer(validatedContext, executionContext, sessionId, credentialStores);
|
|
354
|
+
await server.connect(transport);
|
|
355
|
+
logger.info({ sessionId }, 'Server connected for initialization');
|
|
356
|
+
// Tell client the session ID
|
|
357
|
+
res.setHeader('Mcp-Session-Id', sessionId);
|
|
358
|
+
logger.info({
|
|
359
|
+
sessionId,
|
|
360
|
+
bodyMethod: body?.method,
|
|
361
|
+
bodyId: body?.id,
|
|
362
|
+
}, 'About to handle initialization request');
|
|
363
|
+
await transport.handleRequest(req, res, body);
|
|
364
|
+
logger.info({ sessionId }, 'Successfully handled initialization request');
|
|
365
|
+
return toFetchResponse(res);
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Handles requests for existing MCP sessions
|
|
369
|
+
*/
|
|
370
|
+
const handleExistingSessionRequest = async (body, executionContext, validatedContext, req, res, credentialStores) => {
|
|
371
|
+
const { tenantId, projectId, graphId } = executionContext;
|
|
372
|
+
// Validate the session id
|
|
373
|
+
const conversation = await validateSession(req, res, body, tenantId, projectId, graphId);
|
|
374
|
+
if (!conversation) {
|
|
375
|
+
return toFetchResponse(res);
|
|
376
|
+
}
|
|
377
|
+
const sessionId = conversation.id;
|
|
378
|
+
// Update last activity
|
|
379
|
+
await updateConversation(dbClient)({
|
|
380
|
+
scopes: { tenantId, projectId },
|
|
381
|
+
conversationId: sessionId,
|
|
382
|
+
data: {
|
|
383
|
+
// Just updating the timestamp by calling update
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
// Recreate transport and server from stored session data
|
|
387
|
+
const transport = new StreamableHTTPServerTransport({
|
|
388
|
+
sessionIdGenerator: () => sessionId,
|
|
389
|
+
});
|
|
390
|
+
const server = await getServer(validatedContext, executionContext, sessionId, credentialStores);
|
|
391
|
+
await server.connect(transport);
|
|
392
|
+
// Spoof initialization to set the transport's _initialized flag
|
|
393
|
+
await spoofTransportInitialization(transport, req, sessionId, conversation.metadata?.session_data?.mcpProtocolVersion);
|
|
394
|
+
logger.info({ sessionId }, 'Server connected and transport initialized');
|
|
395
|
+
// Add debugging before transport.handleRequest()
|
|
396
|
+
logger.info({
|
|
397
|
+
sessionId,
|
|
398
|
+
bodyKeys: Object.keys(body || {}),
|
|
399
|
+
bodyMethod: body?.method,
|
|
400
|
+
bodyId: body?.id,
|
|
401
|
+
requestHeaders: Object.fromEntries(Object.entries(req.headers || {}).filter(([k]) => k.startsWith('mcp-'))),
|
|
402
|
+
}, 'About to handle MCP request with existing session');
|
|
403
|
+
try {
|
|
404
|
+
await transport.handleRequest(req, res, body);
|
|
405
|
+
logger.info({ sessionId }, 'Successfully handled MCP request');
|
|
406
|
+
}
|
|
407
|
+
catch (transportError) {
|
|
408
|
+
logger.error({
|
|
409
|
+
sessionId,
|
|
410
|
+
error: transportError,
|
|
411
|
+
errorMessage: transportError instanceof Error ? transportError.message : 'Unknown error',
|
|
412
|
+
}, 'Transport handleRequest failed');
|
|
413
|
+
throw transportError; // Re-throw to be caught by outer catch
|
|
414
|
+
}
|
|
415
|
+
return toFetchResponse(res);
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Creates a JSON-RPC error response
|
|
419
|
+
*/
|
|
420
|
+
const createErrorResponse = (code, message, id = null) => ({
|
|
421
|
+
jsonrpc: '2.0',
|
|
422
|
+
error: { code, message },
|
|
423
|
+
id,
|
|
424
|
+
});
|
|
425
|
+
app.openapi(createRoute({
|
|
426
|
+
method: 'post',
|
|
427
|
+
path: '/',
|
|
428
|
+
tags: ['MCP'],
|
|
429
|
+
summary: 'MCP Protocol',
|
|
430
|
+
description: 'Handles Model Context Protocol (MCP) JSON-RPC requests',
|
|
431
|
+
security: [{ bearerAuth: [] }],
|
|
432
|
+
request: {
|
|
433
|
+
headers: HeadersScopeSchema,
|
|
434
|
+
},
|
|
435
|
+
responses: {
|
|
436
|
+
200: {
|
|
437
|
+
description: 'MCP response',
|
|
438
|
+
},
|
|
439
|
+
401: {
|
|
440
|
+
description: 'Unauthorized - API key authentication required',
|
|
441
|
+
},
|
|
442
|
+
404: {
|
|
443
|
+
description: 'Not Found - Agent graph not found',
|
|
444
|
+
},
|
|
445
|
+
500: {
|
|
446
|
+
description: 'Internal Server Error',
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
}), async (c) => {
|
|
450
|
+
try {
|
|
451
|
+
// Validate parameters
|
|
452
|
+
const paramValidation = validateRequestParameters(c);
|
|
453
|
+
if (!paramValidation.valid) {
|
|
454
|
+
return paramValidation.response;
|
|
455
|
+
}
|
|
456
|
+
const { executionContext } = paramValidation;
|
|
457
|
+
const body = await c.req.json();
|
|
458
|
+
logger.info({ body, bodyKeys: Object.keys(body || {}) }, 'Parsed request body');
|
|
459
|
+
const isInitRequest = body.method === 'initialize';
|
|
460
|
+
const { req, res } = toReqRes(c.req.raw);
|
|
461
|
+
const validatedContext = c.get('validatedContext') || {};
|
|
462
|
+
const credentialStores = c.get('credentialStores');
|
|
463
|
+
logger.info({ validatedContext }, 'Validated context');
|
|
464
|
+
logger.info({ req }, 'request');
|
|
465
|
+
if (isInitRequest) {
|
|
466
|
+
return await handleInitializationRequest(body, executionContext, validatedContext, req, res, c, credentialStores);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
return await handleExistingSessionRequest(body, executionContext, validatedContext, req, res, credentialStores);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
catch (e) {
|
|
473
|
+
logger.error({
|
|
474
|
+
error: e instanceof Error ? e.message : e,
|
|
475
|
+
stack: e instanceof Error ? e.stack : undefined,
|
|
476
|
+
}, 'MCP request error');
|
|
477
|
+
return c.json(createErrorResponse(-32603, 'Internal server error'), { status: 500 });
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
app.get('/', async (c) => {
|
|
481
|
+
logger.info('Received GET MCP request');
|
|
482
|
+
return c.json({
|
|
483
|
+
jsonrpc: '2.0',
|
|
484
|
+
error: {
|
|
485
|
+
code: -32000,
|
|
486
|
+
message: 'Method not allowed.',
|
|
487
|
+
},
|
|
488
|
+
id: null,
|
|
489
|
+
}, { status: 405 });
|
|
490
|
+
});
|
|
491
|
+
// We want to maintain conversations in the database. (https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management)
|
|
492
|
+
app.delete('/', async (c) => {
|
|
493
|
+
logger.info('Received DELETE MCP request');
|
|
494
|
+
return c.json({
|
|
495
|
+
jsonrpc: '2.0',
|
|
496
|
+
error: { code: -32001, message: 'Method Not Allowed' },
|
|
497
|
+
id: null,
|
|
498
|
+
}, { status: 405 });
|
|
499
|
+
});
|
|
500
|
+
export default app;
|
package/dist/tracer.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Span, type Tracer } from '@opentelemetry/api';
|
|
2
|
+
export declare const BASE = "inkeep-chat";
|
|
3
|
+
export declare const SERVICE_NAME = "inkeep-chat";
|
|
4
|
+
export declare const SERVICE_VERSION = "1.0.0";
|
|
5
|
+
export declare const createSpanName: (suffix: string) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Helper function to handle span errors consistently
|
|
8
|
+
* Records the exception, sets error status, and optionally logs
|
|
9
|
+
*/
|
|
10
|
+
export declare function handleSpanError(span: Span, error: unknown, logger?: {
|
|
11
|
+
error: (obj: any, msg?: string) => void;
|
|
12
|
+
}, logMessage?: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Get the global tracer instance
|
|
15
|
+
* This creates a single tracer for the entire application
|
|
16
|
+
*/
|
|
17
|
+
export declare function getGlobalTracer(): Tracer;
|
|
18
|
+
/**
|
|
19
|
+
* Force flush the tracer provider to ensure critical spans are sent immediately
|
|
20
|
+
* This is useful for critical operations where we want to ensure telemetry data
|
|
21
|
+
* is sent before the operation completes or fails
|
|
22
|
+
*/
|
|
23
|
+
export declare function forceFlushTracer(): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=tracer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracer.d.ts","sourceRoot":"","sources":["../src/tracer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,IAAI,EAGT,KAAK,MAAM,EAEZ,MAAM,oBAAoB,CAAC;AAU5B,eAAO,MAAM,IAAI,gBAAgB,CAAC;AAGlC,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAC1C,eAAO,MAAM,eAAe,UAAU,CAAC;AAGvC,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,WAAwB,CAAC;AAyCtE;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE;IAAE,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,EACpD,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI,CAgBN;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAUxC;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAwBtD"}
|
package/dist/tracer.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { SpanStatusCode, trace, } from '@opentelemetry/api';
|
|
2
|
+
import { env } from './env';
|
|
3
|
+
import { getLogger } from './logger';
|
|
4
|
+
// Environments where trace force flush should be enabled
|
|
5
|
+
const FORCE_FLUSH_ENVIRONMENTS = ['development'];
|
|
6
|
+
const logger = getLogger('tracer');
|
|
7
|
+
// Base prefix for all span names - export this to use in other files
|
|
8
|
+
export const BASE = 'inkeep-chat';
|
|
9
|
+
// Service name and version constants for consistent tracer identification
|
|
10
|
+
export const SERVICE_NAME = 'inkeep-chat';
|
|
11
|
+
export const SERVICE_VERSION = '1.0.0';
|
|
12
|
+
// Helper function to create prefixed span names
|
|
13
|
+
export const createSpanName = (suffix) => `${BASE}.${suffix}`;
|
|
14
|
+
// No-op span implementation for when OpenTelemetry is not available
|
|
15
|
+
const createNoOpSpan = () => ({
|
|
16
|
+
setAttributes: () => ({}),
|
|
17
|
+
recordException: () => ({}),
|
|
18
|
+
setStatus: () => ({}),
|
|
19
|
+
addEvent: () => ({}),
|
|
20
|
+
end: () => { },
|
|
21
|
+
isRecording: () => false,
|
|
22
|
+
setAttribute: () => ({}),
|
|
23
|
+
updateName: () => ({}),
|
|
24
|
+
spanContext: () => ({
|
|
25
|
+
traceId: '00000000000000000000000000000000',
|
|
26
|
+
spanId: '0000000000000000',
|
|
27
|
+
traceFlags: 0,
|
|
28
|
+
}),
|
|
29
|
+
addLink: () => ({}),
|
|
30
|
+
addLinks: () => ({}),
|
|
31
|
+
});
|
|
32
|
+
// No-op tracer implementation for when OpenTelemetry is not available
|
|
33
|
+
const noopTracer = {
|
|
34
|
+
startActiveSpan(_name, arg1, arg2, arg3) {
|
|
35
|
+
const fn = typeof arg1 === 'function' ? arg1 : typeof arg2 === 'function' ? arg2 : arg3;
|
|
36
|
+
if (!fn)
|
|
37
|
+
throw new Error('No callback function provided');
|
|
38
|
+
return fn(createNoOpSpan());
|
|
39
|
+
},
|
|
40
|
+
startSpan(_name, _options) {
|
|
41
|
+
return createNoOpSpan();
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
// Global tracer instance - singleton pattern
|
|
45
|
+
let globalTracerInstance = null;
|
|
46
|
+
/**
|
|
47
|
+
* Helper function to handle span errors consistently
|
|
48
|
+
* Records the exception, sets error status, and optionally logs
|
|
49
|
+
*/
|
|
50
|
+
export function handleSpanError(span, error, logger, logMessage) {
|
|
51
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
52
|
+
// Record the exception in the span
|
|
53
|
+
span.recordException(error);
|
|
54
|
+
// Set error status
|
|
55
|
+
span.setStatus({
|
|
56
|
+
code: SpanStatusCode.ERROR,
|
|
57
|
+
message: errorMessage,
|
|
58
|
+
});
|
|
59
|
+
// Optionally log the error
|
|
60
|
+
if (logger && logMessage) {
|
|
61
|
+
logger.error({ error: errorMessage }, logMessage);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the global tracer instance
|
|
66
|
+
* This creates a single tracer for the entire application
|
|
67
|
+
*/
|
|
68
|
+
export function getGlobalTracer() {
|
|
69
|
+
if (!globalTracerInstance) {
|
|
70
|
+
try {
|
|
71
|
+
globalTracerInstance = trace.getTracer(SERVICE_NAME, SERVICE_VERSION);
|
|
72
|
+
}
|
|
73
|
+
catch (_error) {
|
|
74
|
+
logger.debug('OpenTelemetry tracer not available, using no-op tracer');
|
|
75
|
+
globalTracerInstance = noopTracer;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return globalTracerInstance;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Force flush the tracer provider to ensure critical spans are sent immediately
|
|
82
|
+
* This is useful for critical operations where we want to ensure telemetry data
|
|
83
|
+
* is sent before the operation completes or fails
|
|
84
|
+
*/
|
|
85
|
+
export async function forceFlushTracer() {
|
|
86
|
+
const isOtelTracesForceFlushEnabled = env.OTEL_TRACES_FORCE_FLUSH_ENABLED;
|
|
87
|
+
const isForceFlushEnvironment = env.ENVIRONMENT && FORCE_FLUSH_ENVIRONMENTS.includes(env.ENVIRONMENT);
|
|
88
|
+
const shouldForceFlush = isOtelTracesForceFlushEnabled === true ||
|
|
89
|
+
(isOtelTracesForceFlushEnabled == null && isForceFlushEnvironment);
|
|
90
|
+
if (!shouldForceFlush) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
// Import the span processor from instrumentation
|
|
95
|
+
const { spanProcessor } = await import('./instrumentation.js');
|
|
96
|
+
if (spanProcessor && typeof spanProcessor.forceFlush === 'function') {
|
|
97
|
+
await spanProcessor.forceFlush();
|
|
98
|
+
logger.debug('Span processor force flush completed');
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
logger.debug('Span processor does not support force flush or is not available');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
logger.warn({ error }, 'Failed to force flush tracer');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type ContentItem = {
|
|
2
|
+
type: string;
|
|
3
|
+
text?: string;
|
|
4
|
+
};
|
|
5
|
+
export type Message = {
|
|
6
|
+
role: 'system' | 'user' | 'assistant' | 'function' | 'tool';
|
|
7
|
+
content: string | ContentItem[];
|
|
8
|
+
name?: string;
|
|
9
|
+
};
|
|
10
|
+
export type ChatCompletionRequest = {
|
|
11
|
+
model: string;
|
|
12
|
+
messages: Message[];
|
|
13
|
+
temperature?: number;
|
|
14
|
+
top_p?: number;
|
|
15
|
+
n?: number;
|
|
16
|
+
stream?: boolean;
|
|
17
|
+
max_tokens?: number;
|
|
18
|
+
presence_penalty?: number;
|
|
19
|
+
frequency_penalty?: number;
|
|
20
|
+
logit_bias?: Record<string, number>;
|
|
21
|
+
user?: string;
|
|
22
|
+
tools?: Record<string, unknown>;
|
|
23
|
+
runConfig?: Record<string, unknown>;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=chat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/types/chat.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IAC5D,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ExecutionContext } from '@inkeep/agents-core';
|
|
2
|
+
/**
|
|
3
|
+
* Create execution context from middleware values
|
|
4
|
+
*/
|
|
5
|
+
export declare function createExecutionContext(params: {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
tenantId: string;
|
|
8
|
+
projectId: string;
|
|
9
|
+
graphId: string;
|
|
10
|
+
apiKeyId: string;
|
|
11
|
+
agentId?: string;
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
}): ExecutionContext;
|
|
14
|
+
//# sourceMappingURL=execution-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-context.d.ts","sourceRoot":"","sources":["../../src/types/execution-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,gBAAgB,CAUnB"}
|