@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
package/AGENTS.md
ADDED
|
@@ -0,0 +1,1090 @@
|
|
|
1
|
+
# AI Assistant Module - Agent Guide
|
|
2
|
+
|
|
3
|
+
> **IMPORTANT**: This file must be updated with every major change to this module. When implementing new features, modifying architecture, or changing key interfaces, update the relevant sections of this document to keep it accurate for future agents.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `ai-assistant` module provides AI-powered assistance capabilities for Open Mercato. It includes:
|
|
8
|
+
|
|
9
|
+
1. **OpenCode Agent** - AI backend that processes natural language and executes tools
|
|
10
|
+
2. **MCP (Model Context Protocol) Server** - Exposes tools to OpenCode via HTTP
|
|
11
|
+
3. **API Discovery Tools** - Meta-tools for dynamic API access (replaces 600+ individual tools)
|
|
12
|
+
4. **Command Palette UI** - Raycast-style interface for users to interact with AI
|
|
13
|
+
5. **Hybrid Tool Discovery** - Combines search-based discovery with OpenAPI introspection
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
19
|
+
│ AI ASSISTANT MODULE │
|
|
20
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
21
|
+
│ │
|
|
22
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
23
|
+
│ │ Frontend (Command Palette) │ │
|
|
24
|
+
│ │ • Raycast-style dialog (Cmd+K) │ │
|
|
25
|
+
│ │ • Phase-based navigation (idle → routing → chatting → executing) │ │
|
|
26
|
+
│ │ • "Agent is working..." indicator │ │
|
|
27
|
+
│ │ • Debug panel for tool calls │ │
|
|
28
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
29
|
+
│ │ │
|
|
30
|
+
│ ▼ │
|
|
31
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
32
|
+
│ │ POST /api/chat (SSE) │ │
|
|
33
|
+
│ │ • Receives user message │ │
|
|
34
|
+
│ │ • Emits 'thinking' event immediately │ │
|
|
35
|
+
│ │ • Calls OpenCode → waits for response │ │
|
|
36
|
+
│ │ • Emits 'text' and 'done' events │ │
|
|
37
|
+
│ │ • Maintains session ID for conversation context │ │
|
|
38
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
39
|
+
│ │ │
|
|
40
|
+
│ ▼ │
|
|
41
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
42
|
+
│ │ OpenCode Client │ │
|
|
43
|
+
│ │ • handleOpenCodeMessage() - Send message, get response │ │
|
|
44
|
+
│ │ • extractTextFromResponse() - Parse response text │ │
|
|
45
|
+
│ │ • Session management (create, resume) │ │
|
|
46
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
47
|
+
│ │ │
|
|
48
|
+
│ ▼ │
|
|
49
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
50
|
+
│ │ OpenCode Server (Docker :4096) │ │
|
|
51
|
+
│ │ • Go-based AI agent in headless mode │ │
|
|
52
|
+
│ │ • Connects to MCP server for tools │ │
|
|
53
|
+
│ │ • Executes multi-step tool workflows │ │
|
|
54
|
+
│ │ • Uses Anthropic Claude as LLM │ │
|
|
55
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
56
|
+
│ │ │
|
|
57
|
+
│ ▼ │
|
|
58
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
59
|
+
│ │ MCP HTTP Server (:3001) │ │
|
|
60
|
+
│ │ • Exposes 10 tools to OpenCode │ │
|
|
61
|
+
│ │ • API discovery tools (api_discover, api_execute, api_schema) │ │
|
|
62
|
+
│ │ • Search tools (search, search_status, etc.) │ │
|
|
63
|
+
│ │ • Authentication via x-api-key header │ │
|
|
64
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
65
|
+
│ │
|
|
66
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Directory Structure
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
packages/ai-assistant/
|
|
73
|
+
├── src/
|
|
74
|
+
│ ├── index.ts # Package exports
|
|
75
|
+
│ ├── di.ts # Dependency injection setup
|
|
76
|
+
│ ├── types.ts # Shared TypeScript types
|
|
77
|
+
│ │
|
|
78
|
+
│ ├── modules/ai_assistant/
|
|
79
|
+
│ │ ├── index.ts # Module exports
|
|
80
|
+
│ │ ├── acl.ts # Permission definitions
|
|
81
|
+
│ │ ├── cli.ts # CLI commands (mcp:serve, mcp:serve-http)
|
|
82
|
+
│ │ ├── di.ts # Module DI container
|
|
83
|
+
│ │ │
|
|
84
|
+
│ │ ├── lib/
|
|
85
|
+
│ │ │ ├── opencode-client.ts # OpenCode server client
|
|
86
|
+
│ │ │ ├── opencode-handlers.ts # Request handlers for OpenCode
|
|
87
|
+
│ │ │ ├── api-discovery-tools.ts # api_discover, api_execute, api_schema
|
|
88
|
+
│ │ │ ├── api-endpoint-index.ts # OpenAPI endpoint indexing
|
|
89
|
+
│ │ │ ├── http-server.ts # MCP HTTP server implementation
|
|
90
|
+
│ │ │ ├── mcp-server.ts # MCP stdio server implementation
|
|
91
|
+
│ │ │ ├── tool-registry.ts # Global tool registration
|
|
92
|
+
│ │ │ ├── tool-executor.ts # Tool execution logic
|
|
93
|
+
│ │ │ ├── tool-loader.ts # Discovers tools from modules
|
|
94
|
+
│ │ │ ├── mcp-tool-adapter.ts # Converts MCP tools to AI SDK format
|
|
95
|
+
│ │ │ └── types.ts # Module-specific types
|
|
96
|
+
│ │ │
|
|
97
|
+
│ │ ├── frontend/components/
|
|
98
|
+
│ │ │ ├── AiAssistantSettingsPageClient.tsx # Settings page
|
|
99
|
+
│ │ │ └── McpServersSection.tsx # MCP server management UI
|
|
100
|
+
│ │ │
|
|
101
|
+
│ │ └── backend/config/ai-assistant/
|
|
102
|
+
│ │ └── page.tsx # Settings page route
|
|
103
|
+
│ │
|
|
104
|
+
│ └── frontend/
|
|
105
|
+
│ ├── index.ts # Frontend exports
|
|
106
|
+
│ ├── types.ts # Frontend TypeScript types
|
|
107
|
+
│ ├── constants.ts # UI constants
|
|
108
|
+
│ │
|
|
109
|
+
│ ├── hooks/
|
|
110
|
+
│ │ ├── useCommandPalette.ts # Main command palette state/logic
|
|
111
|
+
│ │ ├── useMcpTools.ts # Tool fetching and execution
|
|
112
|
+
│ │ ├── useRecentTools.ts # Recent tools tracking
|
|
113
|
+
│ │ ├── useRecentActions.ts # Recent actions tracking
|
|
114
|
+
│ │ └── usePageContext.ts # Page context detection
|
|
115
|
+
│ │
|
|
116
|
+
│ └── components/CommandPalette/
|
|
117
|
+
│ ├── CommandPalette.tsx # Main component
|
|
118
|
+
│ ├── CommandPaletteProvider.tsx # Context provider
|
|
119
|
+
│ ├── CommandHeader.tsx # Back button + phase info
|
|
120
|
+
│ ├── CommandFooter.tsx # Connection status + debug toggle
|
|
121
|
+
│ ├── CommandInput.tsx # Search input
|
|
122
|
+
│ ├── ToolChatPage.tsx # Chat UI with thinking indicator
|
|
123
|
+
│ ├── ToolCallConfirmation.tsx # Tool execution confirmation
|
|
124
|
+
│ ├── MessageBubble.tsx # Chat message display
|
|
125
|
+
│ ├── DebugPanel.tsx # Debug events viewer
|
|
126
|
+
│ └── ...
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Key Concepts
|
|
130
|
+
|
|
131
|
+
### 1. OpenCode as AI Backend
|
|
132
|
+
|
|
133
|
+
OpenCode is a Go-based AI agent that runs in headless mode. It:
|
|
134
|
+
- Processes natural language requests
|
|
135
|
+
- Connects to MCP servers for tool access
|
|
136
|
+
- Executes multi-step workflows autonomously
|
|
137
|
+
- Maintains conversation sessions
|
|
138
|
+
|
|
139
|
+
**Configuration** (`opencode.json` in Docker):
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"mcp": {
|
|
143
|
+
"open-mercato": {
|
|
144
|
+
"type": "sse",
|
|
145
|
+
"url": "http://host.docker.internal:3001/mcp",
|
|
146
|
+
"headers": {
|
|
147
|
+
"x-api-key": "omk_xxx..."
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 2. API Discovery Tools
|
|
155
|
+
|
|
156
|
+
Instead of 600+ individual tools, we expose 3 meta-tools:
|
|
157
|
+
|
|
158
|
+
| Tool | Description |
|
|
159
|
+
|------|-------------|
|
|
160
|
+
| `api_discover` | Search for APIs by keyword, module, or HTTP method |
|
|
161
|
+
| `api_schema` | Get detailed schema for a specific endpoint |
|
|
162
|
+
| `api_execute` | Execute an API call with parameters |
|
|
163
|
+
|
|
164
|
+
**Example workflow**:
|
|
165
|
+
1. Agent receives: "Find all customers in New York"
|
|
166
|
+
2. Agent calls `api_discover("customers search")`
|
|
167
|
+
3. Agent calls `api_schema("/api/v1/customers")` to see parameters
|
|
168
|
+
4. Agent calls `api_execute({ method: "GET", path: "/api/v1/customers", query: { city: "New York" } })`
|
|
169
|
+
|
|
170
|
+
### 3. Hybrid Tool Discovery
|
|
171
|
+
|
|
172
|
+
Tools are discovered through two sources:
|
|
173
|
+
|
|
174
|
+
1. **Search-based**: Semantic search over tool descriptions
|
|
175
|
+
2. **OpenAPI-based**: Direct introspection of API endpoints
|
|
176
|
+
|
|
177
|
+
The `api_discover` tool combines both for comprehensive results.
|
|
178
|
+
|
|
179
|
+
### 4. Chat Flow (Frontend → OpenCode)
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
User types in Command Palette
|
|
183
|
+
│
|
|
184
|
+
▼
|
|
185
|
+
POST /api/chat { messages, sessionId }
|
|
186
|
+
│
|
|
187
|
+
├── Emit SSE: { type: 'thinking' }
|
|
188
|
+
│
|
|
189
|
+
▼
|
|
190
|
+
handleOpenCodeMessage({ message, sessionId })
|
|
191
|
+
│
|
|
192
|
+
├── Create/resume OpenCode session
|
|
193
|
+
├── Send message to OpenCode
|
|
194
|
+
├── OpenCode may call MCP tools
|
|
195
|
+
├── Wait for response
|
|
196
|
+
│
|
|
197
|
+
▼
|
|
198
|
+
extractTextFromResponse(result)
|
|
199
|
+
│
|
|
200
|
+
├── Emit SSE: { type: 'text', content: '...' }
|
|
201
|
+
├── Emit SSE: { type: 'done', sessionId: '...' }
|
|
202
|
+
│
|
|
203
|
+
▼
|
|
204
|
+
Frontend displays response
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 5. Session Management
|
|
208
|
+
|
|
209
|
+
OpenCode maintains conversation sessions for context:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// First message creates a session
|
|
213
|
+
const result1 = await handleOpenCodeMessage({
|
|
214
|
+
message: "Search for customers"
|
|
215
|
+
})
|
|
216
|
+
// result1.sessionId = "ses_abc123"
|
|
217
|
+
|
|
218
|
+
// Subsequent messages reuse the session
|
|
219
|
+
const result2 = await handleOpenCodeMessage({
|
|
220
|
+
message: "Now filter by New York",
|
|
221
|
+
sessionId: "ses_abc123" // Continues conversation
|
|
222
|
+
})
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## API Routes
|
|
226
|
+
|
|
227
|
+
| Route | Method | Description |
|
|
228
|
+
|-------|--------|-------------|
|
|
229
|
+
| `/api/chat` | POST | Chat with OpenCode agent (SSE stream) |
|
|
230
|
+
| `/api/tools` | GET | List available tools |
|
|
231
|
+
| `/api/tools/execute` | POST | Execute a specific tool directly |
|
|
232
|
+
| `/api/settings` | GET/POST | AI provider configuration |
|
|
233
|
+
| `/api/mcp-servers` | GET/POST | External MCP server management |
|
|
234
|
+
|
|
235
|
+
### Chat API Request/Response
|
|
236
|
+
|
|
237
|
+
**Request**:
|
|
238
|
+
```typescript
|
|
239
|
+
{
|
|
240
|
+
messages: Array<{ role: 'user' | 'assistant'; content: string }>
|
|
241
|
+
sessionId?: string // Optional, for continuing conversation
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**SSE Events**:
|
|
246
|
+
```typescript
|
|
247
|
+
type ChatSSEEvent =
|
|
248
|
+
| { type: 'thinking' } // Agent is processing
|
|
249
|
+
| { type: 'text'; content: string } // Response text
|
|
250
|
+
| { type: 'tool-call'; id: string; toolName: string; args: unknown }
|
|
251
|
+
| { type: 'tool-result'; id: string; toolName: string; result: unknown }
|
|
252
|
+
| { type: 'done'; sessionId?: string } // Complete, with session ID
|
|
253
|
+
| { type: 'error'; error: string } // Error occurred
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Frontend State Management
|
|
257
|
+
|
|
258
|
+
The command palette uses phases instead of pages:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
type PalettePhase =
|
|
262
|
+
| 'idle' // Empty, waiting for input
|
|
263
|
+
| 'routing' // Analyzing intent (fast)
|
|
264
|
+
| 'chatting' // Conversational mode
|
|
265
|
+
| 'confirming' // Waiting for tool confirmation
|
|
266
|
+
| 'executing' // Tool running
|
|
267
|
+
|
|
268
|
+
interface CommandPaletteContextValue {
|
|
269
|
+
state: {
|
|
270
|
+
isOpen: boolean
|
|
271
|
+
phase: PalettePhase
|
|
272
|
+
inputValue: string
|
|
273
|
+
isLoading: boolean
|
|
274
|
+
isStreaming: boolean
|
|
275
|
+
connectionStatus: ConnectionStatus
|
|
276
|
+
}
|
|
277
|
+
isThinking: boolean // OpenCode is processing
|
|
278
|
+
|
|
279
|
+
// Actions
|
|
280
|
+
handleSubmit: (query: string) => Promise<void>
|
|
281
|
+
sendAgenticMessage: (content: string) => Promise<void>
|
|
282
|
+
approveToolCall: (id: string) => Promise<void>
|
|
283
|
+
rejectToolCall: (id: string) => void
|
|
284
|
+
|
|
285
|
+
// Debug
|
|
286
|
+
debugEvents: DebugEvent[]
|
|
287
|
+
showDebug: boolean
|
|
288
|
+
setShowDebug: (show: boolean) => void
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Running the Stack
|
|
293
|
+
|
|
294
|
+
### MCP Server Modes
|
|
295
|
+
|
|
296
|
+
The module provides two MCP HTTP server modes:
|
|
297
|
+
|
|
298
|
+
#### Development Server (`yarn mcp:dev`)
|
|
299
|
+
|
|
300
|
+
For local development and Claude Code integration. Authenticates once using an API key - no session tokens required.
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Reads API key from .mcp.json headers.x-api-key or OPEN_MERCATO_API_KEY env
|
|
304
|
+
yarn mcp:dev
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Key characteristics:**
|
|
308
|
+
- API key authentication at startup (no per-request session tokens)
|
|
309
|
+
- Tools filtered by API key permissions once
|
|
310
|
+
- Ideal for Claude Code, MCP Inspector, local testing
|
|
311
|
+
- Configuration via `.mcp.json`:
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"mcpServers": {
|
|
315
|
+
"open-mercato": {
|
|
316
|
+
"type": "http",
|
|
317
|
+
"url": "http://localhost:3001/mcp",
|
|
318
|
+
"headers": {
|
|
319
|
+
"x-api-key": "omk_your_key_here"
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
#### Production Server (`yarn mcp:serve`)
|
|
327
|
+
|
|
328
|
+
For web-based AI chat. Requires two-tier auth: server API key + user session tokens.
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
# Requires MCP_SERVER_API_KEY in .env
|
|
332
|
+
yarn mcp:serve
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Key characteristics:**
|
|
336
|
+
- Server-level auth via `x-api-key` header (validated against `MCP_SERVER_API_KEY`)
|
|
337
|
+
- User-level auth via `_sessionToken` parameter in each tool call
|
|
338
|
+
- Per-request permission checks based on user's session
|
|
339
|
+
- 2-hour session token TTL
|
|
340
|
+
|
|
341
|
+
#### Comparison
|
|
342
|
+
|
|
343
|
+
| Feature | Dev (`mcp:dev`) | Production (`mcp:serve`) |
|
|
344
|
+
|---------|-----------------|-------------------------|
|
|
345
|
+
| Auth | API key only | API key + session tokens |
|
|
346
|
+
| Permission check | Once at startup | Per tool call |
|
|
347
|
+
| Session tokens | Not required | Required |
|
|
348
|
+
| Use case | Claude Code, dev | Web AI chat |
|
|
349
|
+
|
|
350
|
+
### 1. Start MCP Server
|
|
351
|
+
```bash
|
|
352
|
+
# For development/Claude Code:
|
|
353
|
+
yarn mcp:dev
|
|
354
|
+
|
|
355
|
+
# For production/web chat:
|
|
356
|
+
yarn mcp:serve
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### 2. Start OpenCode (Docker)
|
|
360
|
+
```bash
|
|
361
|
+
docker start opencode-mvp
|
|
362
|
+
# Or: docker-compose up opencode
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### 3. Verify Connectivity
|
|
366
|
+
```bash
|
|
367
|
+
# MCP health
|
|
368
|
+
curl http://localhost:3001/health
|
|
369
|
+
# {"status":"ok","mode":"development","tools":10}
|
|
370
|
+
|
|
371
|
+
# OpenCode health
|
|
372
|
+
curl http://localhost:4096/global/health
|
|
373
|
+
# {"healthy":true,"version":"1.1.21"}
|
|
374
|
+
|
|
375
|
+
# OpenCode MCP connection
|
|
376
|
+
curl http://localhost:4096/mcp
|
|
377
|
+
# {"open-mercato":{"status":"connected"}}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 4. Start Next.js
|
|
381
|
+
```bash
|
|
382
|
+
yarn dev
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 5. Test
|
|
386
|
+
- Open browser → Press Cmd+K
|
|
387
|
+
- Type: "What tools do you have?"
|
|
388
|
+
- Should see "Agent is working..." then response
|
|
389
|
+
|
|
390
|
+
## Common Tasks
|
|
391
|
+
|
|
392
|
+
### Adding a New Tool
|
|
393
|
+
|
|
394
|
+
Register tools via `registerMcpTool()`:
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
import { registerMcpTool } from '@open-mercato/ai-assistant'
|
|
398
|
+
import { z } from 'zod'
|
|
399
|
+
|
|
400
|
+
registerMcpTool({
|
|
401
|
+
name: 'mymodule.action',
|
|
402
|
+
description: 'Does something useful',
|
|
403
|
+
inputSchema: z.object({ param: z.string() }),
|
|
404
|
+
requiredFeatures: ['mymodule.view'],
|
|
405
|
+
handler: async (args, ctx) => {
|
|
406
|
+
// Implementation
|
|
407
|
+
return { result: 'done' }
|
|
408
|
+
}
|
|
409
|
+
}, { moduleId: 'mymodule' })
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Modifying OpenCode Configuration
|
|
413
|
+
|
|
414
|
+
1. Edit `docker/opencode/opencode.json`
|
|
415
|
+
2. Rebuild: `docker-compose build opencode`
|
|
416
|
+
3. Restart: `docker-compose up -d opencode`
|
|
417
|
+
|
|
418
|
+
### Adding New API Endpoints to Discovery
|
|
419
|
+
|
|
420
|
+
APIs are automatically discovered from the OpenAPI spec (`openapi.yaml`). To add:
|
|
421
|
+
|
|
422
|
+
1. Define the endpoint in your module's route file
|
|
423
|
+
2. Regenerate the OpenAPI spec
|
|
424
|
+
3. Restart MCP server
|
|
425
|
+
|
|
426
|
+
### Debugging Tool Calls
|
|
427
|
+
|
|
428
|
+
1. Open Command Palette (Cmd+K)
|
|
429
|
+
2. Click "Debug" in footer to toggle debug panel
|
|
430
|
+
3. Debug panel shows all tool calls, results, errors
|
|
431
|
+
|
|
432
|
+
## Permissions (ACL)
|
|
433
|
+
|
|
434
|
+
| Feature ID | Description |
|
|
435
|
+
|------------|-------------|
|
|
436
|
+
| `ai_assistant.view` | View AI Assistant |
|
|
437
|
+
| `ai_assistant.settings.manage` | Manage AI settings |
|
|
438
|
+
| `ai_assistant.mcp.serve` | Start MCP Server |
|
|
439
|
+
| `ai_assistant.tools.list` | List MCP Tools |
|
|
440
|
+
| `ai_assistant.mcp_servers.view` | View MCP server configs |
|
|
441
|
+
| `ai_assistant.mcp_servers.manage` | Manage MCP server configs |
|
|
442
|
+
|
|
443
|
+
## Critical Technical Details
|
|
444
|
+
|
|
445
|
+
### OpenCode Client
|
|
446
|
+
|
|
447
|
+
Located in `lib/opencode-client.ts`:
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
class OpenCodeClient {
|
|
451
|
+
health(): Promise<OpenCodeHealth>
|
|
452
|
+
mcpStatus(): Promise<OpenCodeMcpStatus>
|
|
453
|
+
createSession(): Promise<OpenCodeSession>
|
|
454
|
+
getSession(id: string): Promise<OpenCodeSession>
|
|
455
|
+
sendMessage(sessionId: string, message: string): Promise<OpenCodeMessage>
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Factory function with env defaults
|
|
459
|
+
function createOpenCodeClient(config?: Partial<OpenCodeClientConfig>): OpenCodeClient
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### OpenCode Handlers
|
|
463
|
+
|
|
464
|
+
Located in `lib/opencode-handlers.ts`:
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
// Main handler for chat API
|
|
468
|
+
async function handleOpenCodeMessage(options: {
|
|
469
|
+
message: string
|
|
470
|
+
sessionId?: string
|
|
471
|
+
}): Promise<OpenCodeTestResponse>
|
|
472
|
+
|
|
473
|
+
// Extract text from OpenCode response parts
|
|
474
|
+
function extractTextFromResponse(result: OpenCodeMessage): string
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### API Discovery Tools
|
|
478
|
+
|
|
479
|
+
Located in `lib/api-discovery-tools.ts`:
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
// Registered tools:
|
|
483
|
+
// - api_discover: Search endpoints by keyword
|
|
484
|
+
// - api_schema: Get endpoint details
|
|
485
|
+
// - api_execute: Execute API call
|
|
486
|
+
|
|
487
|
+
// Internal functions:
|
|
488
|
+
function searchEndpoints(query: string, options?: SearchOptions): EndpointMatch[]
|
|
489
|
+
function executeApiCall(params: ExecuteParams, ctx: McpToolContext): Promise<unknown>
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### API Endpoint Index
|
|
493
|
+
|
|
494
|
+
Located in `lib/api-endpoint-index.ts`:
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
class ApiEndpointIndex {
|
|
498
|
+
static getInstance(): ApiEndpointIndex
|
|
499
|
+
searchEndpoints(query: string, options?: SearchOptions): EndpointMatch[]
|
|
500
|
+
getEndpoint(operationId: string): EndpointInfo | null
|
|
501
|
+
getEndpointByPath(method: string, path: string): EndpointInfo | null
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Docker Configuration
|
|
506
|
+
|
|
507
|
+
### OpenCode Container
|
|
508
|
+
|
|
509
|
+
```yaml
|
|
510
|
+
# docker-compose.yml
|
|
511
|
+
services:
|
|
512
|
+
opencode:
|
|
513
|
+
build: ./docker/opencode
|
|
514
|
+
container_name: opencode-mvp
|
|
515
|
+
ports:
|
|
516
|
+
- "4096:4096"
|
|
517
|
+
environment:
|
|
518
|
+
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
|
519
|
+
volumes:
|
|
520
|
+
- ./docker/opencode/opencode.json:/root/.opencode/opencode.json
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### OpenCode Config
|
|
524
|
+
|
|
525
|
+
```json
|
|
526
|
+
{
|
|
527
|
+
"provider": "anthropic",
|
|
528
|
+
"model": "claude-sonnet-4-20250514",
|
|
529
|
+
"mcp": {
|
|
530
|
+
"open-mercato": {
|
|
531
|
+
"type": "sse",
|
|
532
|
+
"url": "http://host.docker.internal:3001/mcp",
|
|
533
|
+
"headers": {
|
|
534
|
+
"x-api-key": "omk_..."
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## Debug Features
|
|
542
|
+
|
|
543
|
+
### Debug Panel
|
|
544
|
+
|
|
545
|
+
Toggle with "Debug" button in Command Palette footer. Shows:
|
|
546
|
+
- `thinking` - Agent processing started
|
|
547
|
+
- `tool-call` - Tool invocation with args
|
|
548
|
+
- `tool-result` - Tool response
|
|
549
|
+
- `text` - Response text chunks
|
|
550
|
+
- `error` - Errors
|
|
551
|
+
- `done` - Completion
|
|
552
|
+
|
|
553
|
+
### Debug Event Types
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
type DebugEventType =
|
|
557
|
+
| 'thinking' // Agent started processing
|
|
558
|
+
| 'tool-call' // Tool called
|
|
559
|
+
| 'tool-result' // Tool result
|
|
560
|
+
| 'text' // Text response
|
|
561
|
+
| 'error' // Error occurred
|
|
562
|
+
| 'done' // Complete
|
|
563
|
+
| 'message' // Chat message
|
|
564
|
+
| 'connection' // Connection status change
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
## Changelog
|
|
568
|
+
|
|
569
|
+
### 2026-01-17 - Session Persistence Fix
|
|
570
|
+
|
|
571
|
+
**Bug fixed**: Chat context lost between messages (AI asked "Who is 'his'?" instead of remembering Taylor).
|
|
572
|
+
|
|
573
|
+
**Root causes**:
|
|
574
|
+
1. **Promise.race bug**: `handleOpenCodeMessageStreaming` used `Promise.race([eventPromise, sendPromise])` which resolved when HTTP completed, before SSE could emit `done` event with sessionId.
|
|
575
|
+
2. **React stale closure**: `handleSubmit` callback captured initial `null` sessionId value.
|
|
576
|
+
|
|
577
|
+
**Fixes applied**:
|
|
578
|
+
- `opencode-handlers.ts`: Removed Promise.race, await only SSE eventPromise
|
|
579
|
+
- `useCommandPalette.ts`: Added `opencodeSessionIdRef` (ref) alongside state to avoid stale closures
|
|
580
|
+
|
|
581
|
+
**Files modified**:
|
|
582
|
+
- `src/modules/ai_assistant/lib/opencode-handlers.ts` - Fixed Promise.race completion bug
|
|
583
|
+
- `src/frontend/hooks/useCommandPalette.ts` - Added ref pattern for sessionId
|
|
584
|
+
|
|
585
|
+
**Diagnostic logging added** (can be removed after verification):
|
|
586
|
+
- `[handleSubmit] DIAGNOSTIC` - Session check before routing
|
|
587
|
+
- `[sendAgenticMessage] DIAGNOSTIC` - Request payload before fetch
|
|
588
|
+
- `[startAgenticChat] DIAGNOSTIC` - Done event handling
|
|
589
|
+
- `[AI Chat] DIAGNOSTIC` - Backend request received
|
|
590
|
+
|
|
591
|
+
### 2026-01 - OpenCode Integration
|
|
592
|
+
|
|
593
|
+
**Major change**: Replaced Vercel AI SDK with OpenCode as the AI backend.
|
|
594
|
+
|
|
595
|
+
**What changed**:
|
|
596
|
+
- Chat API now routes all requests to OpenCode
|
|
597
|
+
- Added session management for conversation context
|
|
598
|
+
- Added "Agent is working..." indicator
|
|
599
|
+
- OpenCode connects to MCP server for tools
|
|
600
|
+
- Removed direct AI provider integration
|
|
601
|
+
|
|
602
|
+
**Files modified**:
|
|
603
|
+
- `src/modules/ai_assistant/api/chat/route.ts` - Complete rewrite to use OpenCode
|
|
604
|
+
- `src/frontend/hooks/useCommandPalette.ts` - Added session state, thinking indicator
|
|
605
|
+
- `src/frontend/components/CommandPalette/ToolChatPage.tsx` - Added thinking UI
|
|
606
|
+
- `src/frontend/types.ts` - Added ChatSSEEvent, isThinking
|
|
607
|
+
|
|
608
|
+
### 2026-01 - API Discovery Tools
|
|
609
|
+
|
|
610
|
+
**Major change**: Replaced 600+ individual tools with 3 meta-tools.
|
|
611
|
+
|
|
612
|
+
**What changed**:
|
|
613
|
+
- Added `api_discover`, `api_execute`, `api_schema` tools
|
|
614
|
+
- Created `ApiEndpointIndex` for OpenAPI introspection
|
|
615
|
+
- Hybrid discovery: search + OpenAPI
|
|
616
|
+
- 405 endpoints available via discovery
|
|
617
|
+
|
|
618
|
+
**Files created**:
|
|
619
|
+
- `lib/api-discovery-tools.ts`
|
|
620
|
+
- `lib/api-endpoint-index.ts`
|
|
621
|
+
|
|
622
|
+
### 2026-01 - Hybrid Tool Discovery
|
|
623
|
+
|
|
624
|
+
**What changed**:
|
|
625
|
+
- Combined semantic search with OpenAPI introspection
|
|
626
|
+
- Tools indexed for fulltext search
|
|
627
|
+
- API endpoints indexed from OpenAPI spec
|
|
628
|
+
|
|
629
|
+
### Previous Changes
|
|
630
|
+
|
|
631
|
+
See git history for earlier changes including:
|
|
632
|
+
- Zod 4 schema handling fixes
|
|
633
|
+
- Debug panel addition
|
|
634
|
+
- CLI tools fixes
|
|
635
|
+
- Raycast-style command palette rewrite
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## Session Management Deep Dive
|
|
640
|
+
|
|
641
|
+
### Architecture Overview
|
|
642
|
+
|
|
643
|
+
The session flow spans multiple layers:
|
|
644
|
+
|
|
645
|
+
```
|
|
646
|
+
Frontend (useCommandPalette.ts)
|
|
647
|
+
↓ sessionId in request body
|
|
648
|
+
Backend (route.ts)
|
|
649
|
+
↓ sessionId passed to handler
|
|
650
|
+
OpenCode Handler (opencode-handlers.ts)
|
|
651
|
+
↓ client.getSession(sessionId) or createSession()
|
|
652
|
+
OpenCode Client (opencode-client.ts)
|
|
653
|
+
↓ GET /session/{id} or POST /session
|
|
654
|
+
OpenCode Server (Docker :4096)
|
|
655
|
+
→ Maintains conversation context
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### Session ID Flow
|
|
659
|
+
|
|
660
|
+
1. **First message**: No sessionId → `startAgenticChat()` creates new session
|
|
661
|
+
2. **OpenCode responds**: SSE stream emits `{ type: 'done', sessionId: 'ses_xxx' }`
|
|
662
|
+
3. **Frontend stores**: `opencodeSessionIdRef.current = sessionId`
|
|
663
|
+
4. **Subsequent messages**: `sendAgenticMessage()` includes sessionId in request body
|
|
664
|
+
5. **Backend receives**: Uses existing session instead of creating new one
|
|
665
|
+
|
|
666
|
+
### Critical: React Refs vs State for Session ID
|
|
667
|
+
|
|
668
|
+
**Problem**: Using `useState` alone for sessionId causes stale closure issues in callbacks.
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
// BAD: Stale closure - callback captures initial null value
|
|
672
|
+
const [sessionId, setSessionId] = useState<string | null>(null)
|
|
673
|
+
const handleSubmit = useCallback(async (query) => {
|
|
674
|
+
if (sessionId) { // Always null in closure!
|
|
675
|
+
await continueSession(query)
|
|
676
|
+
}
|
|
677
|
+
}, [sessionId]) // Even with dependency, timing issues persist
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
**Solution**: Use both state (for React reactivity) AND ref (for callbacks):
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
// GOOD: Ref always has current value
|
|
684
|
+
const [opencodeSessionId, setOpencodeSessionId] = useState<string | null>(null)
|
|
685
|
+
const opencodeSessionIdRef = useRef<string | null>(null)
|
|
686
|
+
|
|
687
|
+
const updateOpencodeSessionId = useCallback((id: string | null) => {
|
|
688
|
+
opencodeSessionIdRef.current = id // Update ref first
|
|
689
|
+
setOpencodeSessionId(id) // Then state for React
|
|
690
|
+
}, [])
|
|
691
|
+
|
|
692
|
+
const handleSubmit = useCallback(async (query) => {
|
|
693
|
+
if (opencodeSessionIdRef.current) { // Ref has latest value!
|
|
694
|
+
await continueSession(query)
|
|
695
|
+
}
|
|
696
|
+
}, []) // No dependency needed - ref is always current
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### SSE Stream Completion Bug (Fixed 2026-01)
|
|
700
|
+
|
|
701
|
+
**Problem**: `done` event with sessionId was never emitted to frontend.
|
|
702
|
+
|
|
703
|
+
**Root Cause**: In `opencode-handlers.ts`, the code used `Promise.race()`:
|
|
704
|
+
|
|
705
|
+
```typescript
|
|
706
|
+
// BUG: sendPromise resolves before SSE emits session.status: idle
|
|
707
|
+
await Promise.race([eventPromise, sendPromise.catch(err => Promise.reject(err))])
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
When `sendMessage()` HTTP call completed, `Promise.race` resolved immediately, BEFORE the SSE handler could receive the `session.status: idle` event and emit `done`.
|
|
711
|
+
|
|
712
|
+
**Fix**: Only wait for SSE completion, catch send errors separately:
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
// FIXED: SSE determines completion, not HTTP response
|
|
716
|
+
client.sendMessage(session.id, message, { model }).catch((err) => {
|
|
717
|
+
console.error('[OpenCode] Send error (SSE should handle):', err)
|
|
718
|
+
})
|
|
719
|
+
await eventPromise // Only SSE determines completion
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
### OpenCode SSE Event Flow
|
|
723
|
+
|
|
724
|
+
OpenCode emits events via Server-Sent Events. The completion flow:
|
|
725
|
+
|
|
726
|
+
1. `session.status: busy` - Processing started
|
|
727
|
+
2. `message.part.updated` - Text chunks, tool calls, tool results
|
|
728
|
+
3. `message.updated` - Message completed (with tokens, timing)
|
|
729
|
+
4. `session.status: idle` - Processing complete → emit `done` event
|
|
730
|
+
|
|
731
|
+
**Key insight**: The `session.status: idle` event triggers `done`, not HTTP completion.
|
|
732
|
+
|
|
733
|
+
### Debugging Session Issues
|
|
734
|
+
|
|
735
|
+
Add diagnostic logging to trace session flow:
|
|
736
|
+
|
|
737
|
+
```typescript
|
|
738
|
+
// Frontend: useCommandPalette.ts
|
|
739
|
+
console.log('[handleSubmit] DIAGNOSTIC - Session check:', {
|
|
740
|
+
refValue: opencodeSessionIdRef.current,
|
|
741
|
+
willContinue: !!opencodeSessionIdRef.current,
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
// Backend: route.ts
|
|
745
|
+
console.log('[AI Chat] DIAGNOSTIC - Request received:', {
|
|
746
|
+
hasSessionId: !!sessionId,
|
|
747
|
+
sessionId: sessionId ? sessionId.substring(0, 20) + '...' : null,
|
|
748
|
+
})
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
**What to check**:
|
|
752
|
+
1. First message: `refValue: null, willContinue: false` ✓
|
|
753
|
+
2. After first response: Look for `Done event` with sessionId
|
|
754
|
+
3. Second message: `refValue: 'ses_xxx', willContinue: true` ✓
|
|
755
|
+
4. Backend: `hasSessionId: true` ✓
|
|
756
|
+
|
|
757
|
+
### Common Session Problems
|
|
758
|
+
|
|
759
|
+
| Symptom | Likely Cause | Fix |
|
|
760
|
+
|---------|--------------|-----|
|
|
761
|
+
| Second message loses context | sessionId not stored | Check `done` event has sessionId |
|
|
762
|
+
| `refValue: null` on second message | Stale closure | Use ref pattern (see above) |
|
|
763
|
+
| Backend `hasSessionId: false` | Request serialization issue | Check JSON.stringify includes sessionId |
|
|
764
|
+
| `done` event never emitted | Promise.race bug | See SSE completion fix above |
|
|
765
|
+
| Multiple `session-authorized` events | Creating new session each time | sessionId not passed to backend |
|
|
766
|
+
|
|
767
|
+
### Testing Session Persistence
|
|
768
|
+
|
|
769
|
+
1. Open browser console (F12)
|
|
770
|
+
2. Open AI Assistant (Cmd+K)
|
|
771
|
+
3. Send: "find customer Taylor"
|
|
772
|
+
4. Check console for `Done event` with sessionId
|
|
773
|
+
5. Send: "find his related companies"
|
|
774
|
+
6. Check: `willContinue: true` and AI knows about Taylor
|
|
775
|
+
|
|
776
|
+
## Troubleshooting
|
|
777
|
+
|
|
778
|
+
| Symptom | Likely Cause | Fix |
|
|
779
|
+
|---------|--------------|-----|
|
|
780
|
+
| "Agent is working..." forever | OpenCode not responding | Check `curl http://localhost:4096/global/health` |
|
|
781
|
+
| "MCP connection failed" | MCP server not running | Start with `yarn mercato ai_assistant mcp:serve-http --port 3001` |
|
|
782
|
+
| Empty response | OpenCode not connected to MCP | Check `curl http://localhost:4096/mcp` |
|
|
783
|
+
| "Unauthorized" error | Missing/invalid API key | Check x-api-key in opencode.json |
|
|
784
|
+
| Tools not found | Endpoint not in OpenAPI | Regenerate OpenAPI spec |
|
|
785
|
+
| Context lost between messages | Session ID not persisted | See "Session Management Deep Dive" above |
|
|
786
|
+
| "Session expired" errors | Session token TTL exceeded | Close and reopen chat (creates new 2-hour token) |
|
|
787
|
+
| Tools fail with UNAUTHORIZED | Missing _sessionToken | Verify AI is passing token in tool args |
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
## Two-Tier Authentication Architecture
|
|
792
|
+
|
|
793
|
+
The MCP HTTP server implements two distinct authentication layers:
|
|
794
|
+
|
|
795
|
+
### Tier 1: Server-Level Authentication
|
|
796
|
+
|
|
797
|
+
**Purpose**: Validates that requests come from an authorized AI agent (e.g., OpenCode)
|
|
798
|
+
|
|
799
|
+
```
|
|
800
|
+
Request → Check x-api-key header → Compare with MCP_SERVER_API_KEY env var
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
| Aspect | Details |
|
|
804
|
+
|--------|---------|
|
|
805
|
+
| **Header** | `x-api-key` |
|
|
806
|
+
| **Value** | Static API key from `MCP_SERVER_API_KEY` environment variable |
|
|
807
|
+
| **Configured In** | `opencode.json` or `opencode.jsonc` |
|
|
808
|
+
| **Validation** | Direct string comparison |
|
|
809
|
+
| **Result** | Grants access to call MCP endpoints (but no user permissions) |
|
|
810
|
+
|
|
811
|
+
**Code reference**: `packages/ai-assistant/src/modules/ai_assistant/lib/http-server.ts:370-391`
|
|
812
|
+
|
|
813
|
+
### Tier 2: User-Level Authentication (Session Tokens)
|
|
814
|
+
|
|
815
|
+
**Purpose**: Identifies the actual user and loads their permissions for each tool call
|
|
816
|
+
|
|
817
|
+
```
|
|
818
|
+
Tool call → Extract _sessionToken → Lookup in DB → Load ACL → Check permissions
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
| Aspect | Details |
|
|
822
|
+
|--------|---------|
|
|
823
|
+
| **Parameter** | `_sessionToken` in tool call args |
|
|
824
|
+
| **Format** | `sess_{32 hex chars}` (e.g., `sess_a1b2c3d4e5f6...`) |
|
|
825
|
+
| **TTL** | 120 minutes (2 hours) |
|
|
826
|
+
| **Storage** | `api_keys` table |
|
|
827
|
+
| **Lookup** | `findApiKeyBySessionToken()` |
|
|
828
|
+
| **ACL** | `rbacService.loadAcl()` |
|
|
829
|
+
|
|
830
|
+
**Code references**:
|
|
831
|
+
- Session creation: `packages/ai-assistant/src/modules/ai_assistant/api/chat/route.ts:133-157`
|
|
832
|
+
- Token lookup: `packages/core/src/modules/api_keys/services/apiKeyService.ts:143-158`
|
|
833
|
+
- Context resolution: `packages/ai-assistant/src/modules/ai_assistant/lib/http-server.ts:32-88`
|
|
834
|
+
|
|
835
|
+
---
|
|
836
|
+
|
|
837
|
+
## Session Token System
|
|
838
|
+
|
|
839
|
+
### Token Generation
|
|
840
|
+
|
|
841
|
+
```typescript
|
|
842
|
+
// packages/core/src/modules/api_keys/services/apiKeyService.ts:99-101
|
|
843
|
+
export function generateSessionToken(): string {
|
|
844
|
+
return `sess_${randomBytes(16).toString('hex')}`
|
|
845
|
+
}
|
|
846
|
+
// Result: "sess_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
### Token Storage (api_keys table)
|
|
850
|
+
|
|
851
|
+
Session tokens create ephemeral API keys with additional columns:
|
|
852
|
+
|
|
853
|
+
| Column | Type | Description |
|
|
854
|
+
|--------|------|-------------|
|
|
855
|
+
| `sessionToken` | string | The `sess_xxx` token for lookup |
|
|
856
|
+
| `sessionUserId` | string | ID of the user this session represents |
|
|
857
|
+
| `rolesJson` | string[] | User's role IDs (inherited from user) |
|
|
858
|
+
| `tenantId` | string | Tenant scope |
|
|
859
|
+
| `organizationId` | string | Organization scope |
|
|
860
|
+
| `expiresAt` | Date | TTL (default: 120 minutes from creation) |
|
|
861
|
+
|
|
862
|
+
### Token Injection Into Messages
|
|
863
|
+
|
|
864
|
+
When a new chat session starts, the backend injects a system instruction:
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
// packages/ai-assistant/src/modules/ai_assistant/api/chat/route.ts:161-164
|
|
868
|
+
let messageToSend = lastUserMessage
|
|
869
|
+
if (sessionToken) {
|
|
870
|
+
messageToSend = `[SYSTEM: Your session token is "${sessionToken}". You MUST include "_sessionToken": "${sessionToken}" in EVERY tool call argument object. Without this, tools will fail with authorization errors.]\n\n${lastUserMessage}`
|
|
871
|
+
}
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
This ensures the AI agent (Claude via OpenCode) includes the token in all tool calls.
|
|
875
|
+
|
|
876
|
+
### Token in Tool Calls
|
|
877
|
+
|
|
878
|
+
The MCP server schema transformation injects `_sessionToken` into every tool:
|
|
879
|
+
|
|
880
|
+
```typescript
|
|
881
|
+
// packages/ai-assistant/src/modules/ai_assistant/lib/http-server.ts:128-131
|
|
882
|
+
properties._sessionToken = {
|
|
883
|
+
type: 'string',
|
|
884
|
+
description: 'Session authorization token (REQUIRED for all tool calls)',
|
|
885
|
+
}
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
AI sees this parameter and includes it:
|
|
889
|
+
|
|
890
|
+
```json
|
|
891
|
+
{
|
|
892
|
+
"method": "tools/call",
|
|
893
|
+
"params": {
|
|
894
|
+
"name": "api_execute",
|
|
895
|
+
"arguments": {
|
|
896
|
+
"_sessionToken": "sess_a1b2c3d4...",
|
|
897
|
+
"method": "GET",
|
|
898
|
+
"path": "/customers/companies"
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
---
|
|
905
|
+
|
|
906
|
+
## Session Context Resolution
|
|
907
|
+
|
|
908
|
+
When a tool call arrives with `_sessionToken`:
|
|
909
|
+
|
|
910
|
+
### Step 1: Extract Token
|
|
911
|
+
|
|
912
|
+
```typescript
|
|
913
|
+
// http-server.ts:169-170
|
|
914
|
+
const sessionToken = toolArgs._sessionToken as string | undefined
|
|
915
|
+
delete toolArgs._sessionToken // Remove before passing to handler
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
### Step 2: Lookup Session Key
|
|
919
|
+
|
|
920
|
+
```typescript
|
|
921
|
+
// http-server.ts:42 → apiKeyService.ts:143-158
|
|
922
|
+
const sessionKey = await findApiKeyBySessionToken(em, sessionToken)
|
|
923
|
+
// Returns null if: not found, deleted, or expired
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
### Step 3: Load ACL
|
|
927
|
+
|
|
928
|
+
```typescript
|
|
929
|
+
// http-server.ts:59-62
|
|
930
|
+
const acl = await rbacService.loadAcl(`api_key:${sessionKey.id}`, {
|
|
931
|
+
tenantId: sessionKey.tenantId ?? null,
|
|
932
|
+
organizationId: sessionKey.organizationId ?? null,
|
|
933
|
+
})
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
### Step 4: Build User Context
|
|
937
|
+
|
|
938
|
+
```typescript
|
|
939
|
+
// http-server.ts:73-81
|
|
940
|
+
return {
|
|
941
|
+
tenantId: sessionKey.tenantId ?? null,
|
|
942
|
+
organizationId: sessionKey.organizationId ?? null,
|
|
943
|
+
userId: sessionKey.sessionUserId,
|
|
944
|
+
container: baseContext.container,
|
|
945
|
+
userFeatures: acl.features,
|
|
946
|
+
isSuperAdmin: acl.isSuperAdmin,
|
|
947
|
+
apiKeySecret: baseContext.apiKeySecret,
|
|
948
|
+
}
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
### Step 5: Check Tool Permissions
|
|
952
|
+
|
|
953
|
+
```typescript
|
|
954
|
+
// http-server.ts:219-238 → auth.ts:127-148
|
|
955
|
+
if (tool.requiredFeatures?.length) {
|
|
956
|
+
const hasAccess = hasRequiredFeatures(
|
|
957
|
+
tool.requiredFeatures,
|
|
958
|
+
effectiveContext.userFeatures,
|
|
959
|
+
effectiveContext.isSuperAdmin
|
|
960
|
+
)
|
|
961
|
+
if (!hasAccess) {
|
|
962
|
+
return { error: `Insufficient permissions. Required: ${tool.requiredFeatures.join(', ')}` }
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
---
|
|
968
|
+
|
|
969
|
+
## Updated SSE Events
|
|
970
|
+
|
|
971
|
+
### Full Event Types
|
|
972
|
+
|
|
973
|
+
```typescript
|
|
974
|
+
// packages/ai-assistant/src/modules/ai_assistant/lib/opencode-handlers.ts:218-227
|
|
975
|
+
export type OpenCodeStreamEvent =
|
|
976
|
+
| { type: 'thinking' }
|
|
977
|
+
| { type: 'text'; content: string }
|
|
978
|
+
| { type: 'tool-call'; id: string; toolName: string; args: unknown }
|
|
979
|
+
| { type: 'tool-result'; id: string; result: unknown }
|
|
980
|
+
| { type: 'question'; question: OpenCodeQuestion }
|
|
981
|
+
| { type: 'metadata'; model?: string; provider?: string; tokens?: { input: number; output: number }; durationMs?: number }
|
|
982
|
+
| { type: 'debug'; partType: string; data: unknown }
|
|
983
|
+
| { type: 'done'; sessionId: string }
|
|
984
|
+
| { type: 'error'; error: string }
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
### Additional Events from Chat API
|
|
988
|
+
|
|
989
|
+
| Event | Emitted By | Purpose |
|
|
990
|
+
|-------|------------|---------|
|
|
991
|
+
| `session-authorized` | `chat/route.ts:170-175` | Confirms session token created for new chat |
|
|
992
|
+
|
|
993
|
+
### Debug Events (partType values)
|
|
994
|
+
|
|
995
|
+
| partType | Description |
|
|
996
|
+
|----------|-------------|
|
|
997
|
+
| `question-asked` | OpenCode asking a confirmation question |
|
|
998
|
+
| `message-completed` | Assistant message with token counts |
|
|
999
|
+
| `step-start` | Agentic step beginning |
|
|
1000
|
+
| `step-finish` | Agentic step complete |
|
|
1001
|
+
|
|
1002
|
+
---
|
|
1003
|
+
|
|
1004
|
+
## MCP HTTP Server Details
|
|
1005
|
+
|
|
1006
|
+
### Stateless Request Model
|
|
1007
|
+
|
|
1008
|
+
Each HTTP request creates a fresh MCP server instance:
|
|
1009
|
+
|
|
1010
|
+
```typescript
|
|
1011
|
+
// http-server.ts:95-278
|
|
1012
|
+
function createMcpServerForRequest(config, toolContext): McpServer {
|
|
1013
|
+
const server = new McpServer(
|
|
1014
|
+
{ name: config.name, version: config.version },
|
|
1015
|
+
{ capabilities: { tools: {} } }
|
|
1016
|
+
)
|
|
1017
|
+
// Register all tools (ACL checked per-call)
|
|
1018
|
+
// ...
|
|
1019
|
+
return server
|
|
1020
|
+
}
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
**Benefits**:
|
|
1024
|
+
- No session state to manage
|
|
1025
|
+
- Clean isolation between requests
|
|
1026
|
+
- Scales horizontally
|
|
1027
|
+
|
|
1028
|
+
### Schema Transformation
|
|
1029
|
+
|
|
1030
|
+
Tool schemas are transformed to include `_sessionToken`:
|
|
1031
|
+
|
|
1032
|
+
1. **Convert** Zod schema → JSON Schema (`z.toJSONSchema()`)
|
|
1033
|
+
2. **Inject** `_sessionToken` property into `properties`
|
|
1034
|
+
3. **Convert** JSON Schema → Zod with `.passthrough()`
|
|
1035
|
+
4. **Result**: AI agent sees token as available parameter
|
|
1036
|
+
|
|
1037
|
+
```typescript
|
|
1038
|
+
// http-server.ts:121-155
|
|
1039
|
+
const jsonSchema = z.toJSONSchema(tool.inputSchema, { unrepresentable: 'any' })
|
|
1040
|
+
const properties = jsonSchema.properties ?? {}
|
|
1041
|
+
properties._sessionToken = {
|
|
1042
|
+
type: 'string',
|
|
1043
|
+
description: 'Session authorization token (REQUIRED for all tool calls)',
|
|
1044
|
+
}
|
|
1045
|
+
jsonSchema.properties = properties
|
|
1046
|
+
const converted = jsonSchemaToZod(jsonSchema)
|
|
1047
|
+
safeSchema = (converted as z.ZodObject<any>).passthrough()
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
### Per-Tool ACL Checks
|
|
1051
|
+
|
|
1052
|
+
Each tool call validates permissions using the session's ACL:
|
|
1053
|
+
|
|
1054
|
+
```typescript
|
|
1055
|
+
// http-server.ts:219-239
|
|
1056
|
+
if (tool.requiredFeatures?.length) {
|
|
1057
|
+
const hasAccess = hasRequiredFeatures(
|
|
1058
|
+
tool.requiredFeatures,
|
|
1059
|
+
effectiveContext.userFeatures,
|
|
1060
|
+
effectiveContext.isSuperAdmin
|
|
1061
|
+
)
|
|
1062
|
+
if (!hasAccess) {
|
|
1063
|
+
return {
|
|
1064
|
+
content: [{ type: 'text', text: JSON.stringify({
|
|
1065
|
+
error: `Insufficient permissions for tool "${tool.name}". Required: ${tool.requiredFeatures.join(', ')}`,
|
|
1066
|
+
code: 'UNAUTHORIZED'
|
|
1067
|
+
})}],
|
|
1068
|
+
isError: true
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
### Error Responses
|
|
1075
|
+
|
|
1076
|
+
| Code | Message | Cause |
|
|
1077
|
+
|------|---------|-------|
|
|
1078
|
+
| `SESSION_EXPIRED` | "Your chat session has expired..." | Token TTL exceeded (>2 hours) |
|
|
1079
|
+
| `UNAUTHORIZED` | "Session token required" | No `_sessionToken` in args |
|
|
1080
|
+
| `UNAUTHORIZED` | "Insufficient permissions" | User lacks required features |
|
|
1081
|
+
|
|
1082
|
+
---
|
|
1083
|
+
|
|
1084
|
+
## Future Development
|
|
1085
|
+
|
|
1086
|
+
See the original AGENTS.md for planned features:
|
|
1087
|
+
- AI Agent Authorization & Impersonation
|
|
1088
|
+
- Actor + Subject model for audit trails
|
|
1089
|
+
- Permission tiers for rate limiting
|
|
1090
|
+
- Enhanced confirmation flow
|