@open-mercato/ai-assistant 0.4.9-develop-8c36c096d5 → 0.4.9-develop-be6e1cf49c
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/AGENTS.md +78 -37
- package/dist/modules/ai_assistant/api/chat/route.js +104 -13
- package/dist/modules/ai_assistant/api/chat/route.js.map +2 -2
- package/dist/modules/ai_assistant/lib/api-endpoint-index.js +97 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index.js.map +2 -2
- package/dist/modules/ai_assistant/lib/codemode-tools.js +610 -0
- package/dist/modules/ai_assistant/lib/codemode-tools.js.map +7 -0
- package/dist/modules/ai_assistant/lib/http-server.js +65 -7
- package/dist/modules/ai_assistant/lib/http-server.js.map +2 -2
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js +16 -1
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +2 -2
- package/dist/modules/ai_assistant/lib/mcp-server.js +17 -0
- package/dist/modules/ai_assistant/lib/mcp-server.js.map +2 -2
- package/dist/modules/ai_assistant/lib/opencode-handlers.js +32 -0
- package/dist/modules/ai_assistant/lib/opencode-handlers.js.map +2 -2
- package/dist/modules/ai_assistant/lib/sandbox.js +124 -0
- package/dist/modules/ai_assistant/lib/sandbox.js.map +7 -0
- package/dist/modules/ai_assistant/lib/session-memory.js +103 -0
- package/dist/modules/ai_assistant/lib/session-memory.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-loader.js +4 -114
- package/dist/modules/ai_assistant/lib/tool-loader.js.map +3 -3
- package/dist/modules/ai_assistant/lib/truncate.js +26 -0
- package/dist/modules/ai_assistant/lib/truncate.js.map +7 -0
- package/jest.config.cjs +23 -0
- package/package.json +6 -5
- package/src/modules/ai_assistant/api/chat/route.ts +110 -14
- package/src/modules/ai_assistant/lib/__tests__/auth.test.ts +129 -0
- package/src/modules/ai_assistant/lib/__tests__/sandbox.test.ts +642 -0
- package/src/modules/ai_assistant/lib/__tests__/session-memory.test.ts +82 -0
- package/src/modules/ai_assistant/lib/__tests__/truncate.test.ts +76 -0
- package/src/modules/ai_assistant/lib/api-endpoint-index.ts +143 -0
- package/src/modules/ai_assistant/lib/codemode-tools.ts +864 -0
- package/src/modules/ai_assistant/lib/http-server.ts +86 -9
- package/src/modules/ai_assistant/lib/mcp-dev-server.ts +19 -0
- package/src/modules/ai_assistant/lib/mcp-server.ts +21 -0
- package/src/modules/ai_assistant/lib/opencode-handlers.ts +40 -0
- package/src/modules/ai_assistant/lib/sandbox.ts +192 -0
- package/src/modules/ai_assistant/lib/session-memory.ts +174 -0
- package/src/modules/ai_assistant/lib/tool-loader.ts +11 -145
- package/src/modules/ai_assistant/lib/truncate.ts +45 -0
- package/src/modules/ai_assistant/lib/types.ts +2 -0
|
@@ -3,7 +3,6 @@ import type { SearchService } from '@open-mercato/search/service'
|
|
|
3
3
|
import { registerMcpTool, getToolRegistry } from './tool-registry'
|
|
4
4
|
import type { McpToolDefinition, McpToolContext } from './types'
|
|
5
5
|
import { ToolSearchService } from './tool-search'
|
|
6
|
-
import { loadApiDiscoveryTools } from './api-discovery-tools'
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Module tool definition as exported from ai-tools.ts files.
|
|
@@ -68,154 +67,21 @@ export async function loadAllModuleTools(): Promise<void> {
|
|
|
68
67
|
registerMcpTool(contextWhoamiTool, { moduleId: 'context' })
|
|
69
68
|
console.error('[MCP Tools] Registered built-in context_whoami tool')
|
|
70
69
|
|
|
71
|
-
// 2. Register
|
|
70
|
+
// 2. Register Code Mode tools (search + execute)
|
|
71
|
+
// These two tools replace the previous api_discover, call_api, discover_schema,
|
|
72
|
+
// and all module-specific AI tools. The AI writes JavaScript that runs in a
|
|
73
|
+
// node:vm sandbox with access to the OpenAPI spec and api.request().
|
|
72
74
|
try {
|
|
73
|
-
const {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
console.error(`[MCP Tools] Registered ${entityGraphTools.length} entity graph tools`)
|
|
78
|
-
} catch (error) {
|
|
79
|
-
console.error('[MCP Tools] Could not load entity graph tools:', error)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 3. Load auto-discovered ai-tools.ts files from modules
|
|
83
|
-
// Tools are discovered by the generator and registered in ai-tools.generated.ts
|
|
84
|
-
try {
|
|
85
|
-
const pathModule = await import('node:path')
|
|
86
|
-
const path = pathModule.default
|
|
87
|
-
const { pathToFileURL } = await import('node:url')
|
|
88
|
-
const fsModule = await import('node:fs')
|
|
89
|
-
const fs = fsModule.default
|
|
90
|
-
const { findAppRoot, findAllApps } = await import('@open-mercato/shared/lib/bootstrap/appResolver')
|
|
91
|
-
|
|
92
|
-
// Find the app root (contains .mercato/generated/)
|
|
93
|
-
let appRoot = findAppRoot()
|
|
94
|
-
|
|
95
|
-
// Fallback: Try monorepo structure if not found
|
|
96
|
-
if (!appRoot) {
|
|
97
|
-
let current = process.cwd()
|
|
98
|
-
while (current !== path.dirname(current)) {
|
|
99
|
-
const appsDir = path.join(current, 'apps')
|
|
100
|
-
if (fs.existsSync(appsDir)) {
|
|
101
|
-
const apps = findAllApps(current)
|
|
102
|
-
if (apps.length > 0) {
|
|
103
|
-
appRoot = apps[0]
|
|
104
|
-
break
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
current = path.dirname(current)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!appRoot) {
|
|
112
|
-
console.error(`[MCP Tools] Could not find app root with .mercato/generated directory`)
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const tsPath = path.join(appRoot.generatedDir, 'ai-tools.generated.ts')
|
|
117
|
-
|
|
118
|
-
// Check if file exists
|
|
119
|
-
if (!fs.existsSync(tsPath)) {
|
|
120
|
-
console.error(`[MCP Tools] No auto-discovered tools (run npm run modules:prepare to generate)`)
|
|
121
|
-
} else {
|
|
122
|
-
// Compile TypeScript to JavaScript using esbuild (same approach as dynamicLoader)
|
|
123
|
-
const jsPath = tsPath.replace(/\.ts$/, '.mjs')
|
|
124
|
-
const jsExists = fs.existsSync(jsPath)
|
|
125
|
-
|
|
126
|
-
const needsCompile = !jsExists ||
|
|
127
|
-
fs.statSync(tsPath).mtimeMs > fs.statSync(jsPath).mtimeMs
|
|
128
|
-
|
|
129
|
-
if (needsCompile) {
|
|
130
|
-
console.error(`[MCP Tools] Compiling ai-tools.generated.ts...`)
|
|
131
|
-
const esbuild = await import('esbuild')
|
|
132
|
-
const appRoot = path.dirname(path.dirname(path.dirname(tsPath)))
|
|
133
|
-
|
|
134
|
-
// Plugin to resolve @/ alias to app root
|
|
135
|
-
const aliasPlugin: import('esbuild').Plugin = {
|
|
136
|
-
name: 'alias-resolver',
|
|
137
|
-
setup(build) {
|
|
138
|
-
build.onResolve({ filter: /^@\// }, (args) => {
|
|
139
|
-
const resolved = path.join(appRoot, args.path.slice(2))
|
|
140
|
-
if (!fs.existsSync(resolved) && fs.existsSync(resolved + '.ts')) {
|
|
141
|
-
return { path: resolved + '.ts' }
|
|
142
|
-
}
|
|
143
|
-
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory() && fs.existsSync(path.join(resolved, 'index.ts'))) {
|
|
144
|
-
return { path: path.join(resolved, 'index.ts') }
|
|
145
|
-
}
|
|
146
|
-
return { path: resolved }
|
|
147
|
-
})
|
|
148
|
-
},
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Plugin to mark non-JSON package imports as external
|
|
152
|
-
const externalNonJsonPlugin: import('esbuild').Plugin = {
|
|
153
|
-
name: 'external-non-json',
|
|
154
|
-
setup(build) {
|
|
155
|
-
// Filter matches paths that don't start with . or / (package imports)
|
|
156
|
-
build.onResolve({ filter: /^[^./]/ }, (args) => {
|
|
157
|
-
// Skip Windows absolute paths (e.g., C:\...) - they're local files, not packages
|
|
158
|
-
if (/^[a-zA-Z]:/.test(args.path)) {
|
|
159
|
-
return null
|
|
160
|
-
}
|
|
161
|
-
if (args.path.endsWith('.json')) {
|
|
162
|
-
return null
|
|
163
|
-
}
|
|
164
|
-
return { path: args.path, external: true }
|
|
165
|
-
})
|
|
166
|
-
},
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
await esbuild.build({
|
|
170
|
-
entryPoints: [tsPath],
|
|
171
|
-
outfile: jsPath,
|
|
172
|
-
bundle: true,
|
|
173
|
-
format: 'esm',
|
|
174
|
-
platform: 'node',
|
|
175
|
-
target: 'node18',
|
|
176
|
-
plugins: [aliasPlugin, externalNonJsonPlugin],
|
|
177
|
-
loader: { '.json': 'json' },
|
|
178
|
-
})
|
|
179
|
-
console.error(`[MCP Tools] Compiled to ${jsPath}`)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Import the compiled JavaScript
|
|
183
|
-
const fileUrl = pathToFileURL(jsPath).href
|
|
184
|
-
const { aiToolConfigEntries } = (await import(fileUrl)) as {
|
|
185
|
-
aiToolConfigEntries: Array<{ moduleId: string; tools: unknown[] }>
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
let totalTools = 0
|
|
189
|
-
for (const { moduleId, tools } of aiToolConfigEntries) {
|
|
190
|
-
if (Array.isArray(tools) && tools.length > 0) {
|
|
191
|
-
loadModuleTools(moduleId, tools as ModuleAiTool[])
|
|
192
|
-
totalTools += tools.length
|
|
193
|
-
console.error(`[MCP Tools] Loaded ${tools.length} tools from ${moduleId}`)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (totalTools === 0) {
|
|
197
|
-
console.error(`[MCP Tools] No module tools found in generated registry`)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
75
|
+
const { loadCodeModeTools } = await import('./codemode-tools')
|
|
76
|
+
const toolCount = await loadCodeModeTools()
|
|
77
|
+
console.error(`[MCP Tools] Registered ${toolCount} Code Mode tools`)
|
|
200
78
|
} catch (error) {
|
|
201
|
-
|
|
202
|
-
// This is expected when no modules have ai-tools.ts files
|
|
203
|
-
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
204
|
-
console.error(`[MCP Tools] Error loading auto-discovered tools:`, errorMessage)
|
|
205
|
-
if (errorMessage.includes('Cannot find module') || errorMessage.includes('ENOENT')) {
|
|
206
|
-
console.error(`[MCP Tools] No auto-discovered tools (run npm run modules:prepare to generate)`)
|
|
207
|
-
} else {
|
|
208
|
-
console.error(`[MCP Tools] Could not load auto-discovered tools:`, error)
|
|
209
|
-
}
|
|
79
|
+
console.error('[MCP Tools] Could not load Code Mode tools:', error)
|
|
210
80
|
}
|
|
211
81
|
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
console.error(`[MCP Tools] Loaded ${apiToolCount} API discovery tools`)
|
|
216
|
-
} catch (error) {
|
|
217
|
-
console.error('[MCP Tools] Could not load API discovery tools:', error)
|
|
218
|
-
}
|
|
82
|
+
// Note: Auto-discovered module AI tools (from ai-tools.generated.ts) and
|
|
83
|
+
// legacy API discovery tools (find_api, call_api, discover_schema) are no
|
|
84
|
+
// longer loaded. Code Mode's search + execute tools cover all use cases.
|
|
219
85
|
}
|
|
220
86
|
|
|
221
87
|
/**
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response Size Limiter
|
|
3
|
+
*
|
|
4
|
+
* Serializes values with circular reference handling and truncates
|
|
5
|
+
* to a maximum character count suitable for LLM consumption.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const DEFAULT_MAX_CHARS = 40_000 // ~10K tokens
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Serialize and truncate a value for LLM consumption.
|
|
12
|
+
*
|
|
13
|
+
* Handles circular references, non-serializable values, and oversized output.
|
|
14
|
+
*/
|
|
15
|
+
export function truncateResult(value: unknown, maxChars = DEFAULT_MAX_CHARS): string {
|
|
16
|
+
if (value === undefined) return 'undefined'
|
|
17
|
+
|
|
18
|
+
let serialized: string
|
|
19
|
+
try {
|
|
20
|
+
serialized = JSON.stringify(value, circularReplacer(), 2)
|
|
21
|
+
} catch {
|
|
22
|
+
serialized = String(value)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (serialized.length <= maxChars) return serialized
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
serialized.slice(0, maxChars) +
|
|
29
|
+
'\n\n... (truncated — refine your code to return less data)'
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* JSON.stringify replacer that replaces circular references with "[Circular]".
|
|
35
|
+
*/
|
|
36
|
+
function circularReplacer() {
|
|
37
|
+
const seen = new WeakSet()
|
|
38
|
+
return (_key: string, value: unknown) => {
|
|
39
|
+
if (typeof value === 'object' && value !== null) {
|
|
40
|
+
if (seen.has(value)) return '[Circular]'
|
|
41
|
+
seen.add(value)
|
|
42
|
+
}
|
|
43
|
+
return value
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -14,6 +14,8 @@ export interface McpToolContext {
|
|
|
14
14
|
isSuperAdmin: boolean
|
|
15
15
|
/** API key secret for authenticating HTTP requests to internal APIs */
|
|
16
16
|
apiKeySecret?: string
|
|
17
|
+
/** Session token for memory layer (deduplication of search/GET calls) */
|
|
18
|
+
sessionId?: string
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
/**
|