@open-mercato/ai-assistant 0.4.2-canary-c02407ff85
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 +1090 -0
- package/README.md +607 -0
- package/build.mjs +92 -0
- package/dist/di.js +8 -0
- package/dist/di.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandFooter.js +80 -0
- package/dist/frontend/components/CommandPalette/CommandFooter.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandHeader.js +53 -0
- package/dist/frontend/components/CommandPalette/CommandHeader.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandInput.js +29 -0
- package/dist/frontend/components/CommandPalette/CommandInput.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandItem.js +92 -0
- package/dist/frontend/components/CommandPalette/CommandItem.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPalette.js +244 -0
- package/dist/frontend/components/CommandPalette/CommandPalette.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js +42 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js +18 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js.map +7 -0
- package/dist/frontend/components/CommandPalette/DebugPanel.js +215 -0
- package/dist/frontend/components/CommandPalette/DebugPanel.js.map +7 -0
- package/dist/frontend/components/CommandPalette/MessageBubble.js +64 -0
- package/dist/frontend/components/CommandPalette/MessageBubble.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js +91 -0
- package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolCallDisplay.js +47 -0
- package/dist/frontend/components/CommandPalette/ToolCallDisplay.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolChatPage.js +74 -0
- package/dist/frontend/components/CommandPalette/ToolChatPage.js.map +7 -0
- package/dist/frontend/components/CommandPalette/index.js +28 -0
- package/dist/frontend/components/CommandPalette/index.js.map +7 -0
- package/dist/frontend/constants.js +41 -0
- package/dist/frontend/constants.js.map +7 -0
- package/dist/frontend/hooks/index.js +13 -0
- package/dist/frontend/hooks/index.js.map +7 -0
- package/dist/frontend/hooks/useCommandPalette.js +1094 -0
- package/dist/frontend/hooks/useCommandPalette.js.map +7 -0
- package/dist/frontend/hooks/useMcpTools.js +66 -0
- package/dist/frontend/hooks/useMcpTools.js.map +7 -0
- package/dist/frontend/hooks/usePageContext.js +48 -0
- package/dist/frontend/hooks/usePageContext.js.map +7 -0
- package/dist/frontend/hooks/useRecentActions.js +56 -0
- package/dist/frontend/hooks/useRecentActions.js.map +7 -0
- package/dist/frontend/hooks/useRecentTools.js +55 -0
- package/dist/frontend/hooks/useRecentTools.js.map +7 -0
- package/dist/frontend/index.js +35 -0
- package/dist/frontend/index.js.map +7 -0
- package/dist/frontend/types.js +1 -0
- package/dist/frontend/types.js.map +7 -0
- package/dist/frontend/utils/index.js +7 -0
- package/dist/frontend/utils/index.js.map +7 -0
- package/dist/frontend/utils/toolMatcher.js +95 -0
- package/dist/frontend/utils/toolMatcher.js.map +7 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +7 -0
- package/dist/modules/ai_assistant/acl.js +14 -0
- package/dist/modules/ai_assistant/acl.js.map +7 -0
- package/dist/modules/ai_assistant/api/chat/route.js +152 -0
- package/dist/modules/ai_assistant/api/chat/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/health/route.js +27 -0
- package/dist/modules/ai_assistant/api/health/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/route/route.js +123 -0
- package/dist/modules/ai_assistant/api/route/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/settings/route.js +60 -0
- package/dist/modules/ai_assistant/api/settings/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/tools/execute/route.js +58 -0
- package/dist/modules/ai_assistant/api/tools/execute/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/tools/route.js +48 -0
- package/dist/modules/ai_assistant/api/tools/route.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js +28 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/cli.js +192 -0
- package/dist/modules/ai_assistant/cli.js.map +7 -0
- package/dist/modules/ai_assistant/di.js +11 -0
- package/dist/modules/ai_assistant/di.js.map +7 -0
- package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js +257 -0
- package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/index.js +13 -0
- package/dist/modules/ai_assistant/index.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-sdk.js +13 -0
- package/dist/modules/ai_assistant/lib/ai-sdk.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-discovery-tools.js +249 -0
- package/dist/modules/ai_assistant/lib/api-discovery-tools.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js +177 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index.js +210 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index.js.map +7 -0
- package/dist/modules/ai_assistant/lib/auth.js +87 -0
- package/dist/modules/ai_assistant/lib/auth.js.map +7 -0
- package/dist/modules/ai_assistant/lib/chat-config.js +117 -0
- package/dist/modules/ai_assistant/lib/chat-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/client-factory.js +60 -0
- package/dist/modules/ai_assistant/lib/client-factory.js.map +7 -0
- package/dist/modules/ai_assistant/lib/http-server.js +367 -0
- package/dist/modules/ai_assistant/lib/http-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/in-process-client.js +126 -0
- package/dist/modules/ai_assistant/lib/in-process-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-client.js +146 -0
- package/dist/modules/ai_assistant/lib/mcp-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js +283 -0
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-server-config.js +160 -0
- package/dist/modules/ai_assistant/lib/mcp-server-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-server.js +156 -0
- package/dist/modules/ai_assistant/lib/mcp-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js +44 -0
- package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js.map +7 -0
- package/dist/modules/ai_assistant/lib/opencode-client.js +247 -0
- package/dist/modules/ai_assistant/lib/opencode-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/opencode-handlers.js +398 -0
- package/dist/modules/ai_assistant/lib/opencode-handlers.js.map +7 -0
- package/dist/modules/ai_assistant/lib/schema-utils.js +94 -0
- package/dist/modules/ai_assistant/lib/schema-utils.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-executor.js +55 -0
- package/dist/modules/ai_assistant/lib/tool-executor.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-index-config.js +125 -0
- package/dist/modules/ai_assistant/lib/tool-index-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-loader.js +88 -0
- package/dist/modules/ai_assistant/lib/tool-loader.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-registry.js +65 -0
- package/dist/modules/ai_assistant/lib/tool-registry.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-search.js +192 -0
- package/dist/modules/ai_assistant/lib/tool-search.js.map +7 -0
- package/dist/modules/ai_assistant/lib/types.js +1 -0
- package/dist/modules/ai_assistant/lib/types.js.map +7 -0
- package/package.json +108 -0
- package/src/di.ts +11 -0
- package/src/frontend/components/CommandPalette/CommandFooter.tsx +113 -0
- package/src/frontend/components/CommandPalette/CommandHeader.tsx +76 -0
- package/src/frontend/components/CommandPalette/CommandInput.tsx +50 -0
- package/src/frontend/components/CommandPalette/CommandItem.tsx +111 -0
- package/src/frontend/components/CommandPalette/CommandPalette.tsx +276 -0
- package/src/frontend/components/CommandPalette/CommandPaletteProvider.tsx +60 -0
- package/src/frontend/components/CommandPalette/CommandPaletteWrapper.tsx +21 -0
- package/src/frontend/components/CommandPalette/DebugPanel.tsx +257 -0
- package/src/frontend/components/CommandPalette/MessageBubble.tsx +73 -0
- package/src/frontend/components/CommandPalette/ToolCallConfirmation.tsx +130 -0
- package/src/frontend/components/CommandPalette/ToolCallDisplay.tsx +57 -0
- package/src/frontend/components/CommandPalette/ToolChatPage.tsx +125 -0
- package/src/frontend/components/CommandPalette/index.ts +14 -0
- package/src/frontend/constants.ts +35 -0
- package/src/frontend/hooks/index.ts +5 -0
- package/src/frontend/hooks/useCommandPalette.ts +1389 -0
- package/src/frontend/hooks/useMcpTools.ts +73 -0
- package/src/frontend/hooks/usePageContext.ts +61 -0
- package/src/frontend/hooks/useRecentActions.ts +64 -0
- package/src/frontend/hooks/useRecentTools.ts +69 -0
- package/src/frontend/index.ts +39 -0
- package/src/frontend/types.ts +260 -0
- package/src/frontend/utils/index.ts +1 -0
- package/src/frontend/utils/toolMatcher.ts +127 -0
- package/src/index.ts +92 -0
- package/src/modules/ai_assistant/acl.ts +10 -0
- package/src/modules/ai_assistant/api/chat/route.ts +213 -0
- package/src/modules/ai_assistant/api/health/route.ts +30 -0
- package/src/modules/ai_assistant/api/route/route.ts +149 -0
- package/src/modules/ai_assistant/api/settings/route.ts +73 -0
- package/src/modules/ai_assistant/api/tools/execute/route.ts +71 -0
- package/src/modules/ai_assistant/api/tools/route.ts +57 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/page.meta.ts +26 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/page.tsx +12 -0
- package/src/modules/ai_assistant/cli.ts +233 -0
- package/src/modules/ai_assistant/di.ts +9 -0
- package/src/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.tsx +418 -0
- package/src/modules/ai_assistant/index.ts +11 -0
- package/src/modules/ai_assistant/lib/ai-sdk.ts +5 -0
- package/src/modules/ai_assistant/lib/api-discovery-tools.ts +334 -0
- package/src/modules/ai_assistant/lib/api-endpoint-index-config.ts +243 -0
- package/src/modules/ai_assistant/lib/api-endpoint-index.ts +381 -0
- package/src/modules/ai_assistant/lib/auth.ts +185 -0
- package/src/modules/ai_assistant/lib/chat-config.ts +152 -0
- package/src/modules/ai_assistant/lib/client-factory.ts +130 -0
- package/src/modules/ai_assistant/lib/http-server.ts +498 -0
- package/src/modules/ai_assistant/lib/in-process-client.ts +205 -0
- package/src/modules/ai_assistant/lib/mcp-client.ts +221 -0
- package/src/modules/ai_assistant/lib/mcp-dev-server.ts +373 -0
- package/src/modules/ai_assistant/lib/mcp-server-config.ts +287 -0
- package/src/modules/ai_assistant/lib/mcp-server.ts +214 -0
- package/src/modules/ai_assistant/lib/mcp-tool-adapter.ts +76 -0
- package/src/modules/ai_assistant/lib/opencode-client.ts +426 -0
- package/src/modules/ai_assistant/lib/opencode-handlers.ts +676 -0
- package/src/modules/ai_assistant/lib/schema-utils.ts +142 -0
- package/src/modules/ai_assistant/lib/tool-executor.ts +71 -0
- package/src/modules/ai_assistant/lib/tool-index-config.ts +178 -0
- package/src/modules/ai_assistant/lib/tool-loader.ts +149 -0
- package/src/modules/ai_assistant/lib/tool-registry.ts +114 -0
- package/src/modules/ai_assistant/lib/tool-search.ts +308 -0
- package/src/modules/ai_assistant/lib/types.ts +147 -0
- package/test-schema.ts +37 -0
- package/tsconfig.json +10 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useEffect } from 'react'
|
|
4
|
+
import type { ToolInfo, ToolExecutionResult } from '../types'
|
|
5
|
+
|
|
6
|
+
export function useMcpTools() {
|
|
7
|
+
const [tools, setTools] = useState<ToolInfo[]>([])
|
|
8
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
9
|
+
const [error, setError] = useState<string | null>(null)
|
|
10
|
+
|
|
11
|
+
const fetchTools = useCallback(async () => {
|
|
12
|
+
setIsLoading(true)
|
|
13
|
+
setError(null)
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch('/api/ai_assistant/tools')
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
throw new Error(`Failed to fetch tools: ${response.status}`)
|
|
18
|
+
}
|
|
19
|
+
const data = await response.json()
|
|
20
|
+
setTools(data.tools || [])
|
|
21
|
+
} catch (err) {
|
|
22
|
+
setError(err instanceof Error ? err.message : 'Failed to load tools')
|
|
23
|
+
setTools([])
|
|
24
|
+
} finally {
|
|
25
|
+
setIsLoading(false)
|
|
26
|
+
}
|
|
27
|
+
}, [])
|
|
28
|
+
|
|
29
|
+
const executeTool = useCallback(
|
|
30
|
+
async (toolName: string, args: Record<string, unknown> = {}): Promise<ToolExecutionResult> => {
|
|
31
|
+
try {
|
|
32
|
+
const response = await fetch('/api/ai_assistant/tools/execute', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
body: JSON.stringify({ toolName, args }),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const data = await response.json()
|
|
39
|
+
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
return {
|
|
42
|
+
success: false,
|
|
43
|
+
error: data.error || `Tool execution failed: ${response.status}`,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
result: data.result,
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: err instanceof Error ? err.message : 'Tool execution failed',
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
[]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
// Fetch tools on mount
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
fetchTools()
|
|
64
|
+
}, [fetchTools])
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
tools,
|
|
68
|
+
isLoading,
|
|
69
|
+
error,
|
|
70
|
+
fetchTools,
|
|
71
|
+
executeTool,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
import { usePathname } from 'next/navigation'
|
|
5
|
+
import type { PageContext } from '../types'
|
|
6
|
+
|
|
7
|
+
interface UsePageContextOptions {
|
|
8
|
+
tenantId: string
|
|
9
|
+
organizationId: string | null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function extractModule(pathParts: string[]): string | null {
|
|
13
|
+
// Path structure: /backend/[module]/[entity]/[id]
|
|
14
|
+
// or /backend/[module]/[entity]
|
|
15
|
+
const backendIndex = pathParts.indexOf('backend')
|
|
16
|
+
if (backendIndex === -1 || backendIndex >= pathParts.length - 1) {
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
19
|
+
return pathParts[backendIndex + 1] || null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function extractEntityType(pathParts: string[]): string | null {
|
|
23
|
+
const backendIndex = pathParts.indexOf('backend')
|
|
24
|
+
if (backendIndex === -1 || backendIndex >= pathParts.length - 2) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
return pathParts[backendIndex + 2] || null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function extractRecordId(pathParts: string[]): string | null {
|
|
31
|
+
const backendIndex = pathParts.indexOf('backend')
|
|
32
|
+
if (backendIndex === -1 || backendIndex >= pathParts.length - 3) {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
const potentialId = pathParts[backendIndex + 3]
|
|
36
|
+
// Check if it looks like a UUID or ID
|
|
37
|
+
if (potentialId && /^[a-f0-9-]{36}$/i.test(potentialId)) {
|
|
38
|
+
return potentialId
|
|
39
|
+
}
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function usePageContext(options: UsePageContextOptions): PageContext {
|
|
44
|
+
const pathname = usePathname()
|
|
45
|
+
const { tenantId, organizationId } = options
|
|
46
|
+
|
|
47
|
+
const pageContext = useMemo<PageContext>(() => {
|
|
48
|
+
const parts = pathname.split('/').filter(Boolean)
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
path: pathname,
|
|
52
|
+
module: extractModule(parts),
|
|
53
|
+
entityType: extractEntityType(parts),
|
|
54
|
+
recordId: extractRecordId(parts),
|
|
55
|
+
tenantId,
|
|
56
|
+
organizationId,
|
|
57
|
+
}
|
|
58
|
+
}, [pathname, tenantId, organizationId])
|
|
59
|
+
|
|
60
|
+
return pageContext
|
|
61
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useEffect } from 'react'
|
|
4
|
+
import type { RecentAction } from '../types'
|
|
5
|
+
import { RECENT_ACTIONS_KEY, MAX_RECENT_ACTIONS } from '../constants'
|
|
6
|
+
|
|
7
|
+
function loadRecentActions(): RecentAction[] {
|
|
8
|
+
if (typeof window === 'undefined') return []
|
|
9
|
+
try {
|
|
10
|
+
const stored = localStorage.getItem(RECENT_ACTIONS_KEY)
|
|
11
|
+
if (!stored) return []
|
|
12
|
+
const parsed = JSON.parse(stored)
|
|
13
|
+
if (!Array.isArray(parsed)) return []
|
|
14
|
+
return parsed.slice(0, MAX_RECENT_ACTIONS)
|
|
15
|
+
} catch {
|
|
16
|
+
return []
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function saveRecentActions(actions: RecentAction[]): void {
|
|
21
|
+
if (typeof window === 'undefined') return
|
|
22
|
+
try {
|
|
23
|
+
localStorage.setItem(RECENT_ACTIONS_KEY, JSON.stringify(actions.slice(0, MAX_RECENT_ACTIONS)))
|
|
24
|
+
} catch {
|
|
25
|
+
// Ignore localStorage errors
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function useRecentActions() {
|
|
30
|
+
const [recentActions, setRecentActions] = useState<RecentAction[]>([])
|
|
31
|
+
|
|
32
|
+
// Load from localStorage on mount
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setRecentActions(loadRecentActions())
|
|
35
|
+
}, [])
|
|
36
|
+
|
|
37
|
+
const addRecentAction = useCallback((action: Omit<RecentAction, 'id' | 'timestamp'>) => {
|
|
38
|
+
setRecentActions((prev) => {
|
|
39
|
+
const newAction: RecentAction = {
|
|
40
|
+
...action,
|
|
41
|
+
id: crypto.randomUUID(),
|
|
42
|
+
timestamp: Date.now(),
|
|
43
|
+
}
|
|
44
|
+
// Remove duplicates by toolName, add new at start
|
|
45
|
+
const filtered = prev.filter((a) => a.toolName !== action.toolName)
|
|
46
|
+
const updated = [newAction, ...filtered].slice(0, MAX_RECENT_ACTIONS)
|
|
47
|
+
saveRecentActions(updated)
|
|
48
|
+
return updated
|
|
49
|
+
})
|
|
50
|
+
}, [])
|
|
51
|
+
|
|
52
|
+
const clearRecentActions = useCallback(() => {
|
|
53
|
+
setRecentActions([])
|
|
54
|
+
if (typeof window !== 'undefined') {
|
|
55
|
+
localStorage.removeItem(RECENT_ACTIONS_KEY)
|
|
56
|
+
}
|
|
57
|
+
}, [])
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
recentActions,
|
|
61
|
+
addRecentAction,
|
|
62
|
+
clearRecentActions,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useEffect, useMemo } from 'react'
|
|
4
|
+
import type { ToolInfo } from '../types'
|
|
5
|
+
|
|
6
|
+
const RECENT_TOOLS_KEY = 'om:command-palette:recent-tools'
|
|
7
|
+
const MAX_RECENT_TOOLS = 5
|
|
8
|
+
|
|
9
|
+
function loadRecentToolIds(): string[] {
|
|
10
|
+
if (typeof window === 'undefined') return []
|
|
11
|
+
try {
|
|
12
|
+
const stored = localStorage.getItem(RECENT_TOOLS_KEY)
|
|
13
|
+
if (!stored) return []
|
|
14
|
+
const parsed = JSON.parse(stored)
|
|
15
|
+
if (!Array.isArray(parsed)) return []
|
|
16
|
+
return parsed.slice(0, MAX_RECENT_TOOLS)
|
|
17
|
+
} catch {
|
|
18
|
+
return []
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function saveRecentToolIds(toolIds: string[]): void {
|
|
23
|
+
if (typeof window === 'undefined') return
|
|
24
|
+
try {
|
|
25
|
+
localStorage.setItem(RECENT_TOOLS_KEY, JSON.stringify(toolIds.slice(0, MAX_RECENT_TOOLS)))
|
|
26
|
+
} catch {
|
|
27
|
+
// Ignore localStorage errors
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useRecentTools(allTools: ToolInfo[]) {
|
|
32
|
+
const [recentIds, setRecentIds] = useState<string[]>([])
|
|
33
|
+
|
|
34
|
+
// Load from localStorage on mount
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
setRecentIds(loadRecentToolIds())
|
|
37
|
+
}, [])
|
|
38
|
+
|
|
39
|
+
const saveRecentTool = useCallback((toolName: string) => {
|
|
40
|
+
setRecentIds((prev) => {
|
|
41
|
+
// Add new tool at start, remove duplicates
|
|
42
|
+
const updated = [toolName, ...prev.filter((t) => t !== toolName)].slice(0, MAX_RECENT_TOOLS)
|
|
43
|
+
saveRecentToolIds(updated)
|
|
44
|
+
return updated
|
|
45
|
+
})
|
|
46
|
+
}, [])
|
|
47
|
+
|
|
48
|
+
const clearRecentTools = useCallback(() => {
|
|
49
|
+
setRecentIds([])
|
|
50
|
+
if (typeof window !== 'undefined') {
|
|
51
|
+
localStorage.removeItem(RECENT_TOOLS_KEY)
|
|
52
|
+
}
|
|
53
|
+
}, [])
|
|
54
|
+
|
|
55
|
+
// Map recent tool IDs to actual tool objects
|
|
56
|
+
const recentTools = useMemo(
|
|
57
|
+
() =>
|
|
58
|
+
recentIds
|
|
59
|
+
.map((id) => allTools.find((t) => t.name === id))
|
|
60
|
+
.filter((t): t is ToolInfo => t !== undefined),
|
|
61
|
+
[recentIds, allTools]
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
recentTools,
|
|
66
|
+
saveRecentTool,
|
|
67
|
+
clearRecentTools,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Components
|
|
2
|
+
export {
|
|
3
|
+
CommandPalette,
|
|
4
|
+
CommandPaletteProvider,
|
|
5
|
+
CommandPaletteWrapper,
|
|
6
|
+
useCommandPaletteContext,
|
|
7
|
+
} from './components/CommandPalette'
|
|
8
|
+
|
|
9
|
+
// Hooks
|
|
10
|
+
export {
|
|
11
|
+
useCommandPalette,
|
|
12
|
+
useMcpTools,
|
|
13
|
+
usePageContext,
|
|
14
|
+
useRecentActions,
|
|
15
|
+
} from './hooks'
|
|
16
|
+
|
|
17
|
+
// Types
|
|
18
|
+
export type {
|
|
19
|
+
CommandPaletteMode,
|
|
20
|
+
PageContext,
|
|
21
|
+
SelectedEntity,
|
|
22
|
+
ToolInfo,
|
|
23
|
+
ToolExecutionResult,
|
|
24
|
+
RecentAction,
|
|
25
|
+
ToolCall,
|
|
26
|
+
ChatMessage,
|
|
27
|
+
CommandPaletteState,
|
|
28
|
+
CommandPaletteContextValue,
|
|
29
|
+
} from './types'
|
|
30
|
+
|
|
31
|
+
// Utils
|
|
32
|
+
export { filterTools, groupToolsByModule, humanizeToolName } from './utils'
|
|
33
|
+
|
|
34
|
+
// Constants
|
|
35
|
+
export {
|
|
36
|
+
COMMAND_PALETTE_SHORTCUT,
|
|
37
|
+
RECENT_ACTIONS_KEY,
|
|
38
|
+
MAX_RECENT_ACTIONS,
|
|
39
|
+
} from './constants'
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// Phase-based state for intelligent routing
|
|
2
|
+
export type PalettePhase =
|
|
3
|
+
| 'idle' // Empty, waiting for input
|
|
4
|
+
| 'routing' // Fast model analyzing intent
|
|
5
|
+
| 'chatting' // Smart model conversation for tool params
|
|
6
|
+
| 'confirming' // Waiting for user to approve tool call
|
|
7
|
+
| 'executing' // Tool running
|
|
8
|
+
|
|
9
|
+
// Page-based navigation for Raycast-style interface (deprecated, use PalettePhase)
|
|
10
|
+
export type CommandPalettePage = 'home' | 'tool-chat'
|
|
11
|
+
|
|
12
|
+
// Connection status for MCP servers
|
|
13
|
+
export type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error'
|
|
14
|
+
|
|
15
|
+
// Legacy mode type (deprecated, kept for compatibility)
|
|
16
|
+
export type CommandPaletteMode = 'commands' | 'chat'
|
|
17
|
+
|
|
18
|
+
export interface PageContext {
|
|
19
|
+
path: string
|
|
20
|
+
module: string | null
|
|
21
|
+
entityType: string | null
|
|
22
|
+
recordId: string | null
|
|
23
|
+
tenantId: string
|
|
24
|
+
organizationId: string | null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SelectedEntity {
|
|
28
|
+
entityType: string
|
|
29
|
+
recordId: string
|
|
30
|
+
displayName: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ToolInfo {
|
|
34
|
+
name: string
|
|
35
|
+
description: string
|
|
36
|
+
inputSchema: Record<string, unknown>
|
|
37
|
+
module?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ToolExecutionResult {
|
|
41
|
+
success: boolean
|
|
42
|
+
result?: unknown
|
|
43
|
+
error?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface RecentAction {
|
|
47
|
+
id: string
|
|
48
|
+
toolName: string
|
|
49
|
+
displayName: string
|
|
50
|
+
timestamp: number
|
|
51
|
+
args?: Record<string, unknown>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Tool call with confirmation support
|
|
55
|
+
export interface ToolCall {
|
|
56
|
+
id: string
|
|
57
|
+
toolName: string
|
|
58
|
+
args: Record<string, unknown>
|
|
59
|
+
status: 'pending' | 'running' | 'completed' | 'error'
|
|
60
|
+
result?: unknown
|
|
61
|
+
error?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Pending tool call awaiting user confirmation
|
|
65
|
+
export interface PendingToolCall {
|
|
66
|
+
id: string
|
|
67
|
+
toolName: string
|
|
68
|
+
args: Record<string, unknown>
|
|
69
|
+
status: 'pending' | 'approved' | 'rejected' | 'executing' | 'completed' | 'error'
|
|
70
|
+
result?: unknown
|
|
71
|
+
error?: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ChatMessage {
|
|
75
|
+
id: string
|
|
76
|
+
role: 'user' | 'assistant' | 'system'
|
|
77
|
+
content: string
|
|
78
|
+
createdAt?: Date
|
|
79
|
+
// Tool call info if this message contains a tool call
|
|
80
|
+
toolCalls?: PendingToolCall[]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// New phase-based state for intelligent routing
|
|
84
|
+
export interface CommandPaletteState {
|
|
85
|
+
isOpen: boolean
|
|
86
|
+
phase: PalettePhase
|
|
87
|
+
inputValue: string
|
|
88
|
+
selectedIndex: number
|
|
89
|
+
isLoading: boolean
|
|
90
|
+
isStreaming: boolean
|
|
91
|
+
connectionStatus: ConnectionStatus
|
|
92
|
+
// Legacy fields for backwards compatibility
|
|
93
|
+
page: CommandPalettePage
|
|
94
|
+
mode: CommandPaletteMode
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface CommandPaletteContextValue {
|
|
98
|
+
state: CommandPaletteState
|
|
99
|
+
isThinking: boolean
|
|
100
|
+
isSessionAuthorized: boolean
|
|
101
|
+
pageContext: PageContext | null
|
|
102
|
+
selectedEntities: SelectedEntity[]
|
|
103
|
+
tools: ToolInfo[]
|
|
104
|
+
filteredTools: ToolInfo[]
|
|
105
|
+
recentActions: RecentAction[]
|
|
106
|
+
recentTools: ToolInfo[]
|
|
107
|
+
messages: ChatMessage[]
|
|
108
|
+
pendingToolCalls: PendingToolCall[]
|
|
109
|
+
selectedTool: ToolInfo | null
|
|
110
|
+
initialContext: {
|
|
111
|
+
tenantId: string | null
|
|
112
|
+
organizationId: string | null
|
|
113
|
+
userId: string
|
|
114
|
+
isSuperAdmin: boolean
|
|
115
|
+
features: string[]
|
|
116
|
+
} | null
|
|
117
|
+
availableEntities: Array<{ entityId: string; enabled: boolean }> | null
|
|
118
|
+
|
|
119
|
+
// Navigation actions
|
|
120
|
+
open: () => void
|
|
121
|
+
close: () => void
|
|
122
|
+
setInputValue: (value: string) => void
|
|
123
|
+
setSelectedIndex: (index: number) => void
|
|
124
|
+
|
|
125
|
+
// Intelligent routing - submit natural language query
|
|
126
|
+
handleSubmit: (query: string) => Promise<void>
|
|
127
|
+
reset: () => void
|
|
128
|
+
|
|
129
|
+
// Page navigation (legacy, kept for compatibility)
|
|
130
|
+
goToToolChat: (tool: ToolInfo) => void
|
|
131
|
+
goBack: () => void
|
|
132
|
+
|
|
133
|
+
// Tool execution
|
|
134
|
+
executeTool: (toolName: string, args?: Record<string, unknown>) => Promise<ToolExecutionResult>
|
|
135
|
+
approveToolCall: (toolCallId: string) => Promise<void>
|
|
136
|
+
rejectToolCall: (toolCallId: string) => void
|
|
137
|
+
|
|
138
|
+
// Chat actions
|
|
139
|
+
sendMessage: (content: string) => Promise<void>
|
|
140
|
+
sendAgenticMessage: (content: string) => Promise<void>
|
|
141
|
+
clearMessages: () => void
|
|
142
|
+
|
|
143
|
+
// Legacy compatibility
|
|
144
|
+
setMode: (mode: CommandPaletteMode) => void
|
|
145
|
+
setIsOpen: (isOpen: boolean) => void
|
|
146
|
+
|
|
147
|
+
// Debug mode
|
|
148
|
+
debugEvents: DebugEvent[]
|
|
149
|
+
showDebug: boolean
|
|
150
|
+
setShowDebug: (show: boolean) => void
|
|
151
|
+
clearDebugEvents: () => void
|
|
152
|
+
|
|
153
|
+
// OpenCode question handling
|
|
154
|
+
pendingQuestion: OpenCodeQuestion | null
|
|
155
|
+
answerQuestion: (answer: number) => Promise<void>
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface ChatApiRequest {
|
|
159
|
+
messages: ChatMessage[]
|
|
160
|
+
context: PageContext | null
|
|
161
|
+
mode?: 'default' | 'tool-assist' | 'tool-assist-confirm'
|
|
162
|
+
toolName?: string // For tool-specific chat sessions
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Routing API response
|
|
166
|
+
export interface RouteResult {
|
|
167
|
+
intent: 'tool' | 'general_chat'
|
|
168
|
+
toolName?: string
|
|
169
|
+
confidence: number
|
|
170
|
+
reasoning: string
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ToolsApiResponse {
|
|
174
|
+
tools: ToolInfo[]
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface ToolExecuteRequest {
|
|
178
|
+
toolName: string
|
|
179
|
+
args: Record<string, unknown>
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface ToolExecuteResponse {
|
|
183
|
+
success: boolean
|
|
184
|
+
result?: unknown
|
|
185
|
+
error?: string
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Stream event types for tool-assist-confirm mode
|
|
189
|
+
export type StreamEventType = 'text' | 'tool-call' | 'tool-result' | 'error' | 'done'
|
|
190
|
+
|
|
191
|
+
export interface StreamEvent {
|
|
192
|
+
type: StreamEventType
|
|
193
|
+
content?: string
|
|
194
|
+
toolCall?: {
|
|
195
|
+
id: string
|
|
196
|
+
toolName: string
|
|
197
|
+
args: Record<string, unknown>
|
|
198
|
+
}
|
|
199
|
+
toolResult?: {
|
|
200
|
+
id: string
|
|
201
|
+
result: unknown
|
|
202
|
+
}
|
|
203
|
+
error?: string
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Debug event types for debugging the AI chat
|
|
207
|
+
export type DebugEventType =
|
|
208
|
+
| 'thinking'
|
|
209
|
+
| 'tool-call'
|
|
210
|
+
| 'tool-result'
|
|
211
|
+
| 'text'
|
|
212
|
+
| 'error'
|
|
213
|
+
| 'done'
|
|
214
|
+
| 'message'
|
|
215
|
+
| 'connection'
|
|
216
|
+
| 'metadata'
|
|
217
|
+
| 'debug'
|
|
218
|
+
| 'question'
|
|
219
|
+
| 'session-authorized'
|
|
220
|
+
|
|
221
|
+
export interface DebugEvent {
|
|
222
|
+
id: string
|
|
223
|
+
timestamp: Date
|
|
224
|
+
type: DebugEventType
|
|
225
|
+
data: unknown
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Question option from OpenCode
|
|
229
|
+
export interface OpenCodeQuestionOption {
|
|
230
|
+
label: string
|
|
231
|
+
description: string
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Question from OpenCode requiring user confirmation
|
|
235
|
+
export interface OpenCodeQuestion {
|
|
236
|
+
id: string
|
|
237
|
+
sessionID: string
|
|
238
|
+
questions: Array<{
|
|
239
|
+
question: string
|
|
240
|
+
header: string
|
|
241
|
+
options: OpenCodeQuestionOption[]
|
|
242
|
+
}>
|
|
243
|
+
tool: {
|
|
244
|
+
messageID: string
|
|
245
|
+
callID: string
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// SSE event types from chat API (OpenCode integration)
|
|
250
|
+
export type ChatSSEEvent =
|
|
251
|
+
| { type: 'thinking' }
|
|
252
|
+
| { type: 'text'; content: string }
|
|
253
|
+
| { type: 'tool-call'; id: string; toolName: string; args: unknown }
|
|
254
|
+
| { type: 'tool-result'; id: string; result: unknown }
|
|
255
|
+
| { type: 'question'; question: OpenCodeQuestion }
|
|
256
|
+
| { type: 'metadata'; model?: string; provider?: string; tokens?: { input: number; output: number }; timing?: { created: number; completed?: number }; durationMs?: number }
|
|
257
|
+
| { type: 'debug'; partType: string; data: unknown }
|
|
258
|
+
| { type: 'done'; sessionId?: string }
|
|
259
|
+
| { type: 'error'; error: string }
|
|
260
|
+
| { type: 'session-authorized'; sessionToken?: string }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { filterTools, groupToolsByModule, humanizeToolName } from './toolMatcher'
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { ToolInfo } from '../types'
|
|
2
|
+
|
|
3
|
+
function normalizeString(str: string): string {
|
|
4
|
+
return str.toLowerCase().replace(/[._-]/g, ' ')
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function fuzzyMatch(query: string, target: string): boolean {
|
|
8
|
+
const normalizedQuery = normalizeString(query)
|
|
9
|
+
const normalizedTarget = normalizeString(target)
|
|
10
|
+
|
|
11
|
+
// Check if all query chars appear in order in target
|
|
12
|
+
let queryIndex = 0
|
|
13
|
+
for (let i = 0; i < normalizedTarget.length && queryIndex < normalizedQuery.length; i++) {
|
|
14
|
+
if (normalizedTarget[i] === normalizedQuery[queryIndex]) {
|
|
15
|
+
queryIndex++
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return queryIndex === normalizedQuery.length
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function scoreMatch(query: string, tool: ToolInfo): number {
|
|
22
|
+
const normalizedQuery = normalizeString(query)
|
|
23
|
+
const normalizedName = normalizeString(tool.name)
|
|
24
|
+
const normalizedDesc = normalizeString(tool.description)
|
|
25
|
+
|
|
26
|
+
let score = 0
|
|
27
|
+
|
|
28
|
+
// Exact match in name = highest score
|
|
29
|
+
if (normalizedName.includes(normalizedQuery)) {
|
|
30
|
+
score += 100
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Starts with query
|
|
34
|
+
if (normalizedName.startsWith(normalizedQuery)) {
|
|
35
|
+
score += 50
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Word boundary match
|
|
39
|
+
const nameWords = normalizedName.split(' ')
|
|
40
|
+
if (nameWords.some((word) => word.startsWith(normalizedQuery))) {
|
|
41
|
+
score += 30
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Fuzzy match in name
|
|
45
|
+
if (fuzzyMatch(normalizedQuery, normalizedName)) {
|
|
46
|
+
score += 20
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Match in description
|
|
50
|
+
if (normalizedDesc.includes(normalizedQuery)) {
|
|
51
|
+
score += 10
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return score
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function filterTools(tools: ToolInfo[], query: string): ToolInfo[] {
|
|
58
|
+
if (!query.trim()) {
|
|
59
|
+
return tools
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const normalizedQuery = query.trim().toLowerCase()
|
|
63
|
+
|
|
64
|
+
// Score and filter tools
|
|
65
|
+
const scoredTools = tools
|
|
66
|
+
.map((tool) => ({
|
|
67
|
+
tool,
|
|
68
|
+
score: scoreMatch(normalizedQuery, tool),
|
|
69
|
+
}))
|
|
70
|
+
.filter(({ score }) => score > 0)
|
|
71
|
+
.sort((a, b) => b.score - a.score)
|
|
72
|
+
|
|
73
|
+
return scoredTools.map(({ tool }) => tool)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function groupToolsByModule(tools: ToolInfo[]): Map<string, ToolInfo[]> {
|
|
77
|
+
const grouped = new Map<string, ToolInfo[]>()
|
|
78
|
+
|
|
79
|
+
for (const tool of tools) {
|
|
80
|
+
const module = tool.module || extractModuleFromName(tool.name)
|
|
81
|
+
const existing = grouped.get(module) || []
|
|
82
|
+
existing.push(tool)
|
|
83
|
+
grouped.set(module, existing)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return grouped
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function extractModuleFromName(name: string): string {
|
|
90
|
+
const parts = name.split('.')
|
|
91
|
+
return parts[0] || 'other'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function humanizeToolName(name: string): string {
|
|
95
|
+
// customers.people.create -> Create Person
|
|
96
|
+
const parts = name.split('.')
|
|
97
|
+
if (parts.length < 2) return name
|
|
98
|
+
|
|
99
|
+
const action = parts[parts.length - 1]
|
|
100
|
+
const resource = parts[parts.length - 2]
|
|
101
|
+
|
|
102
|
+
const humanAction = capitalize(action)
|
|
103
|
+
const humanResource = singularize(humanize(resource))
|
|
104
|
+
|
|
105
|
+
return `${humanAction} ${humanResource}`
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function capitalize(str: string): string {
|
|
109
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function humanize(str: string): string {
|
|
113
|
+
return str.replace(/[_-]/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function singularize(str: string): string {
|
|
117
|
+
if (str.endsWith('ies')) {
|
|
118
|
+
return str.slice(0, -3) + 'y'
|
|
119
|
+
}
|
|
120
|
+
if (str.endsWith('es') && !str.endsWith('ses')) {
|
|
121
|
+
return str.slice(0, -2)
|
|
122
|
+
}
|
|
123
|
+
if (str.endsWith('s') && !str.endsWith('ss')) {
|
|
124
|
+
return str.slice(0, -1)
|
|
125
|
+
}
|
|
126
|
+
return str
|
|
127
|
+
}
|