@open-mercato/ai-assistant 0.4.11-develop.1917.b6098e78cb → 0.4.11-develop.1922.46b23ba3b5
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/frontend/components/DockableChat/DockableChat.js +5 -0
- package/dist/frontend/components/DockableChat/DockableChat.js.map +2 -2
- package/dist/frontend/hooks/useCommandPalette.js +16 -12
- package/dist/frontend/hooks/useCommandPalette.js.map +2 -2
- package/dist/frontend/hooks/useMcpTools.js +22 -21
- package/dist/frontend/hooks/useMcpTools.js.map +2 -2
- package/dist/modules/ai_assistant/api/chat/route.js +9 -1
- package/dist/modules/ai_assistant/api/chat/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/health/route.js +9 -1
- package/dist/modules/ai_assistant/api/health/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/route/route.js +9 -1
- package/dist/modules/ai_assistant/api/route/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/settings/route.js +9 -1
- package/dist/modules/ai_assistant/api/settings/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/tools/execute/route.js +9 -1
- package/dist/modules/ai_assistant/api/tools/execute/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/tools/route.js +9 -1
- package/dist/modules/ai_assistant/api/tools/route.js.map +2 -2
- package/dist/modules/ai_assistant/lib/opencode-client.js +24 -8
- package/dist/modules/ai_assistant/lib/opencode-client.js.map +2 -2
- package/package.json +4 -4
- package/src/frontend/components/DockableChat/DockableChat.tsx +5 -0
- package/src/frontend/hooks/useCommandPalette.ts +17 -12
- package/src/frontend/hooks/useMcpTools.ts +22 -22
- package/src/modules/ai_assistant/api/chat/route.ts +9 -0
- package/src/modules/ai_assistant/api/health/route.ts +9 -0
- package/src/modules/ai_assistant/api/route/route.ts +9 -0
- package/src/modules/ai_assistant/api/settings/route.ts +9 -0
- package/src/modules/ai_assistant/api/tools/execute/route.ts +9 -0
- package/src/modules/ai_assistant/api/tools/route.ts +9 -0
- package/src/modules/ai_assistant/lib/opencode-client.ts +24 -8
|
@@ -19,6 +19,8 @@ import type {
|
|
|
19
19
|
DebugEventType,
|
|
20
20
|
OpenCodeQuestion,
|
|
21
21
|
} from '../types'
|
|
22
|
+
import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
23
|
+
import { apiFetch } from '@open-mercato/ui/backend/utils/api'
|
|
22
24
|
import { COMMAND_PALETTE_SHORTCUT, AI_CHAT_SHORTCUT } from '../constants'
|
|
23
25
|
import { filterTools } from '../utils/toolMatcher'
|
|
24
26
|
import { useMcpTools } from './useMcpTools'
|
|
@@ -497,7 +499,7 @@ export function useCommandPalette(options: UseCommandPaletteOptions) {
|
|
|
497
499
|
// Route query using fast model
|
|
498
500
|
const routeQuery = useCallback(
|
|
499
501
|
async (query: string): Promise<RouteResult> => {
|
|
500
|
-
const
|
|
502
|
+
const { ok, result, status } = await apiCall<RouteResult>('/api/ai_assistant/route', {
|
|
501
503
|
method: 'POST',
|
|
502
504
|
headers: { 'Content-Type': 'application/json' },
|
|
503
505
|
body: JSON.stringify({
|
|
@@ -509,11 +511,15 @@ export function useCommandPalette(options: UseCommandPaletteOptions) {
|
|
|
509
511
|
}),
|
|
510
512
|
})
|
|
511
513
|
|
|
512
|
-
if (!
|
|
513
|
-
throw new Error(`Routing failed: ${
|
|
514
|
+
if (!ok) {
|
|
515
|
+
throw new Error(`Routing failed: ${status}`)
|
|
514
516
|
}
|
|
515
517
|
|
|
516
|
-
|
|
518
|
+
if (!result) {
|
|
519
|
+
throw new Error('Routing returned empty response')
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return result
|
|
517
523
|
},
|
|
518
524
|
[tools]
|
|
519
525
|
)
|
|
@@ -547,7 +553,7 @@ export function useCommandPalette(options: UseCommandPaletteOptions) {
|
|
|
547
553
|
const controller = new AbortController()
|
|
548
554
|
currentStreamController.current = controller
|
|
549
555
|
|
|
550
|
-
const response = await
|
|
556
|
+
const response = await apiFetch('/api/ai_assistant/chat', {
|
|
551
557
|
method: 'POST',
|
|
552
558
|
headers: { 'Content-Type': 'application/json' },
|
|
553
559
|
body: JSON.stringify({
|
|
@@ -765,7 +771,7 @@ export function useCommandPalette(options: UseCommandPaletteOptions) {
|
|
|
765
771
|
}
|
|
766
772
|
setMessages([userMessage])
|
|
767
773
|
|
|
768
|
-
const response = await
|
|
774
|
+
const response = await apiFetch('/api/ai_assistant/chat', {
|
|
769
775
|
method: 'POST',
|
|
770
776
|
headers: { 'Content-Type': 'application/json' },
|
|
771
777
|
body: JSON.stringify({
|
|
@@ -978,7 +984,7 @@ export function useCommandPalette(options: UseCommandPaletteOptions) {
|
|
|
978
984
|
currentStreamController.current = controller
|
|
979
985
|
|
|
980
986
|
// Send to chat API with OpenCode session for context persistence
|
|
981
|
-
const response = await
|
|
987
|
+
const response = await apiFetch('/api/ai_assistant/chat', {
|
|
982
988
|
method: 'POST',
|
|
983
989
|
headers: { 'Content-Type': 'application/json' },
|
|
984
990
|
body: JSON.stringify({
|
|
@@ -1271,7 +1277,7 @@ export function useCommandPalette(options: UseCommandPaletteOptions) {
|
|
|
1271
1277
|
try {
|
|
1272
1278
|
// Send answer as simple POST - the original SSE stream will receive the follow-up
|
|
1273
1279
|
const sessionId = pendingQuestion.sessionID
|
|
1274
|
-
const
|
|
1280
|
+
const { ok, result, status } = await apiCall<{ error?: string }>('/api/ai_assistant/chat', {
|
|
1275
1281
|
method: 'POST',
|
|
1276
1282
|
headers: { 'Content-Type': 'application/json' },
|
|
1277
1283
|
body: JSON.stringify({
|
|
@@ -1283,9 +1289,8 @@ export function useCommandPalette(options: UseCommandPaletteOptions) {
|
|
|
1283
1289
|
}),
|
|
1284
1290
|
})
|
|
1285
1291
|
|
|
1286
|
-
if (!
|
|
1287
|
-
|
|
1288
|
-
throw new Error(errorData.error || `Answer request failed: ${response.status}`)
|
|
1292
|
+
if (!ok) {
|
|
1293
|
+
throw new Error(result?.error || `Answer request failed: ${status}`)
|
|
1289
1294
|
}
|
|
1290
1295
|
|
|
1291
1296
|
// Answer sent successfully - the original stream will handle the response
|
|
@@ -1325,7 +1330,7 @@ export function useCommandPalette(options: UseCommandPaletteOptions) {
|
|
|
1325
1330
|
|
|
1326
1331
|
try {
|
|
1327
1332
|
// Send to chat API
|
|
1328
|
-
const response = await
|
|
1333
|
+
const response = await apiFetch('/api/ai_assistant/chat', {
|
|
1329
1334
|
method: 'POST',
|
|
1330
1335
|
headers: { 'Content-Type': 'application/json' },
|
|
1331
1336
|
body: JSON.stringify({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useState, useCallback, useEffect } from 'react'
|
|
4
|
+
import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
4
5
|
import type { ToolInfo, ToolExecutionResult } from '../types'
|
|
5
6
|
|
|
6
7
|
export function useMcpTools() {
|
|
@@ -12,16 +13,14 @@ export function useMcpTools() {
|
|
|
12
13
|
setIsLoading(true)
|
|
13
14
|
setError(null)
|
|
14
15
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
throw new Error(`Failed to fetch tools: ${response.status}`)
|
|
16
|
+
const { ok, result, status } = await apiCall<{ tools: ToolInfo[] }>(
|
|
17
|
+
'/api/ai_assistant/tools',
|
|
18
|
+
{ headers: { 'x-om-forbidden-redirect': '0' } }
|
|
19
|
+
)
|
|
20
|
+
if (!ok) {
|
|
21
|
+
throw new Error(`Failed to fetch tools: ${status}`)
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
setTools(data.tools || [])
|
|
23
|
+
setTools(result?.tools || [])
|
|
25
24
|
} catch (err) {
|
|
26
25
|
setError(err instanceof Error ? err.message : 'Failed to load tools')
|
|
27
26
|
setTools([])
|
|
@@ -33,27 +32,28 @@ export function useMcpTools() {
|
|
|
33
32
|
const executeTool = useCallback(
|
|
34
33
|
async (toolName: string, args: Record<string, unknown> = {}): Promise<ToolExecutionResult> => {
|
|
35
34
|
try {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
const { ok, result, status } = await apiCall<{ result: unknown; error?: string }>(
|
|
36
|
+
'/api/ai_assistant/tools/execute',
|
|
37
|
+
{
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
'x-om-forbidden-redirect': '0',
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({ toolName, args }),
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
46
|
|
|
47
|
-
if (!
|
|
47
|
+
if (!ok) {
|
|
48
48
|
return {
|
|
49
49
|
success: false,
|
|
50
|
-
error:
|
|
50
|
+
error: result?.error || `Tool execution failed: ${status}`,
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
return {
|
|
55
55
|
success: true,
|
|
56
|
-
result:
|
|
56
|
+
result: result?.result,
|
|
57
57
|
}
|
|
58
58
|
} catch (err) {
|
|
59
59
|
return {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
2
3
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
4
|
import {
|
|
4
5
|
handleOpenCodeMessageStreaming,
|
|
@@ -119,6 +120,14 @@ RESPONSE RULES:
|
|
|
119
120
|
- Only ask for confirmation before create/update/delete operations.
|
|
120
121
|
`.trim()
|
|
121
122
|
|
|
123
|
+
export const openApi: OpenApiRouteDoc = {
|
|
124
|
+
tag: 'AI Assistant',
|
|
125
|
+
summary: 'AI chat',
|
|
126
|
+
methods: {
|
|
127
|
+
POST: { summary: 'Send message to AI agent via SSE stream' },
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
122
131
|
export const metadata = {
|
|
123
132
|
POST: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },
|
|
124
133
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
2
3
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
4
|
import { handleOpenCodeHealth } from '../../lib/opencode-handlers'
|
|
4
5
|
|
|
6
|
+
export const openApi: OpenApiRouteDoc = {
|
|
7
|
+
tag: 'AI Assistant',
|
|
8
|
+
summary: 'AI assistant health check',
|
|
9
|
+
methods: {
|
|
10
|
+
GET: { summary: 'Check OpenCode and MCP connection status' },
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
|
|
5
14
|
export const metadata = {
|
|
6
15
|
GET: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },
|
|
7
16
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
2
3
|
import { generateObject } from '../../lib/ai-sdk'
|
|
3
4
|
import {
|
|
4
5
|
createOpenAI,
|
|
@@ -19,6 +20,14 @@ import {
|
|
|
19
20
|
type ChatProviderId,
|
|
20
21
|
} from '../../lib/chat-config'
|
|
21
22
|
|
|
23
|
+
export const openApi: OpenApiRouteDoc = {
|
|
24
|
+
tag: 'AI Assistant',
|
|
25
|
+
summary: 'AI query routing',
|
|
26
|
+
methods: {
|
|
27
|
+
POST: { summary: 'Route user query to appropriate AI handler' },
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
export const metadata = {
|
|
23
32
|
POST: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },
|
|
24
33
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
2
3
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
4
|
import {
|
|
4
5
|
OPEN_CODE_PROVIDER_IDS,
|
|
@@ -9,6 +10,14 @@ import {
|
|
|
9
10
|
resolveOpenCodeProviderId,
|
|
10
11
|
} from '@open-mercato/shared/lib/ai/opencode-provider'
|
|
11
12
|
|
|
13
|
+
export const openApi: OpenApiRouteDoc = {
|
|
14
|
+
tag: 'AI Assistant',
|
|
15
|
+
summary: 'AI assistant settings',
|
|
16
|
+
methods: {
|
|
17
|
+
GET: { summary: 'Get AI provider configuration' },
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
export const metadata = {
|
|
13
22
|
GET: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },
|
|
14
23
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
2
3
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
4
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
5
|
import { executeTool } from '../../../lib/tool-executor'
|
|
@@ -6,6 +7,14 @@ import { loadAllModuleTools } from '../../../lib/tool-loader'
|
|
|
6
7
|
import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
7
8
|
import type { McpToolContext } from '../../../lib/types'
|
|
8
9
|
|
|
10
|
+
export const openApi: OpenApiRouteDoc = {
|
|
11
|
+
tag: 'AI Assistant',
|
|
12
|
+
summary: 'Execute AI tool',
|
|
13
|
+
methods: {
|
|
14
|
+
POST: { summary: 'Execute a specific MCP tool by name' },
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
export const metadata = {
|
|
10
19
|
POST: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },
|
|
11
20
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server'
|
|
2
|
+
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
2
3
|
import { zodToJsonSchema } from 'zod-to-json-schema'
|
|
3
4
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
4
5
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
@@ -7,6 +8,14 @@ import { loadAllModuleTools } from '../../lib/tool-loader'
|
|
|
7
8
|
import { hasRequiredFeatures } from '../../lib/auth'
|
|
8
9
|
import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
9
10
|
|
|
11
|
+
export const openApi: OpenApiRouteDoc = {
|
|
12
|
+
tag: 'AI Assistant',
|
|
13
|
+
summary: 'List AI tools',
|
|
14
|
+
methods: {
|
|
15
|
+
GET: { summary: 'List available MCP tools filtered by user permissions' },
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
export const metadata = {
|
|
11
20
|
GET: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },
|
|
12
21
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Client for communicating with OpenCode server running in headless mode.
|
|
5
5
|
* OpenCode is used as an AI agent that can execute MCP tools.
|
|
6
6
|
*/
|
|
7
|
+
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
7
8
|
|
|
8
9
|
export type OpenCodeClientConfig = {
|
|
9
10
|
baseUrl: string
|
|
@@ -195,7 +196,9 @@ export class OpenCodeClient {
|
|
|
195
196
|
throw new Error(`Health check failed: ${res.status}`)
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
|
|
199
|
+
const data = await readJsonSafe<OpenCodeHealth>(res, null)
|
|
200
|
+
if (!data) throw new Error('Health check returned invalid JSON response')
|
|
201
|
+
return data
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
/**
|
|
@@ -210,7 +213,9 @@ export class OpenCodeClient {
|
|
|
210
213
|
throw new Error(`MCP status check failed: ${res.status}`)
|
|
211
214
|
}
|
|
212
215
|
|
|
213
|
-
|
|
216
|
+
const data = await readJsonSafe<OpenCodeMcpStatus>(res, null)
|
|
217
|
+
if (!data) throw new Error('MCP status check returned invalid JSON response')
|
|
218
|
+
return data
|
|
214
219
|
}
|
|
215
220
|
|
|
216
221
|
/**
|
|
@@ -228,7 +233,9 @@ export class OpenCodeClient {
|
|
|
228
233
|
throw new Error(`Failed to create session: ${error}`)
|
|
229
234
|
}
|
|
230
235
|
|
|
231
|
-
|
|
236
|
+
const data = await readJsonSafe<OpenCodeSession>(res, null)
|
|
237
|
+
if (!data) throw new Error('Create session returned invalid JSON response')
|
|
238
|
+
return data
|
|
232
239
|
}
|
|
233
240
|
|
|
234
241
|
/**
|
|
@@ -243,7 +250,9 @@ export class OpenCodeClient {
|
|
|
243
250
|
throw new Error(`Failed to get session: ${res.status}`)
|
|
244
251
|
}
|
|
245
252
|
|
|
246
|
-
|
|
253
|
+
const data = await readJsonSafe<OpenCodeSession>(res, null)
|
|
254
|
+
if (!data) throw new Error('Get session returned invalid JSON response')
|
|
255
|
+
return data
|
|
247
256
|
}
|
|
248
257
|
|
|
249
258
|
/**
|
|
@@ -275,7 +284,9 @@ export class OpenCodeClient {
|
|
|
275
284
|
throw new Error(`Failed to send message: ${error}`)
|
|
276
285
|
}
|
|
277
286
|
|
|
278
|
-
|
|
287
|
+
const data = await readJsonSafe<OpenCodeMessage>(res, null)
|
|
288
|
+
if (!data) throw new Error('Send message returned invalid JSON response')
|
|
289
|
+
return data
|
|
279
290
|
}
|
|
280
291
|
|
|
281
292
|
/**
|
|
@@ -305,7 +316,9 @@ export class OpenCodeClient {
|
|
|
305
316
|
throw new Error(`Failed to get config: ${res.status}`)
|
|
306
317
|
}
|
|
307
318
|
|
|
308
|
-
|
|
319
|
+
const data = await readJsonSafe<Record<string, unknown>>(res, null)
|
|
320
|
+
if (!data) throw new Error('Get config returned invalid JSON response')
|
|
321
|
+
return data
|
|
309
322
|
}
|
|
310
323
|
|
|
311
324
|
/**
|
|
@@ -320,7 +333,9 @@ export class OpenCodeClient {
|
|
|
320
333
|
throw new Error(`Failed to get questions: ${res.status}`)
|
|
321
334
|
}
|
|
322
335
|
|
|
323
|
-
|
|
336
|
+
const data = await readJsonSafe<OpenCodeQuestion[]>(res, null)
|
|
337
|
+
if (!data) throw new Error('Get questions returned invalid JSON response')
|
|
338
|
+
return data
|
|
324
339
|
}
|
|
325
340
|
|
|
326
341
|
/**
|
|
@@ -395,7 +410,8 @@ export class OpenCodeClient {
|
|
|
395
410
|
if (res.ok) {
|
|
396
411
|
const contentType = res.headers.get('content-type')
|
|
397
412
|
if (contentType && contentType.includes('application/json')) {
|
|
398
|
-
|
|
413
|
+
const data = await readJsonSafe<{ status: string; questionId?: string }>(res, null)
|
|
414
|
+
if (data) return data
|
|
399
415
|
}
|
|
400
416
|
}
|
|
401
417
|
} catch {
|