@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.
Files changed (193) hide show
  1. package/AGENTS.md +1090 -0
  2. package/README.md +607 -0
  3. package/build.mjs +92 -0
  4. package/dist/di.js +8 -0
  5. package/dist/di.js.map +7 -0
  6. package/dist/frontend/components/CommandPalette/CommandFooter.js +80 -0
  7. package/dist/frontend/components/CommandPalette/CommandFooter.js.map +7 -0
  8. package/dist/frontend/components/CommandPalette/CommandHeader.js +53 -0
  9. package/dist/frontend/components/CommandPalette/CommandHeader.js.map +7 -0
  10. package/dist/frontend/components/CommandPalette/CommandInput.js +29 -0
  11. package/dist/frontend/components/CommandPalette/CommandInput.js.map +7 -0
  12. package/dist/frontend/components/CommandPalette/CommandItem.js +92 -0
  13. package/dist/frontend/components/CommandPalette/CommandItem.js.map +7 -0
  14. package/dist/frontend/components/CommandPalette/CommandPalette.js +244 -0
  15. package/dist/frontend/components/CommandPalette/CommandPalette.js.map +7 -0
  16. package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js +42 -0
  17. package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js.map +7 -0
  18. package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js +18 -0
  19. package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js.map +7 -0
  20. package/dist/frontend/components/CommandPalette/DebugPanel.js +215 -0
  21. package/dist/frontend/components/CommandPalette/DebugPanel.js.map +7 -0
  22. package/dist/frontend/components/CommandPalette/MessageBubble.js +64 -0
  23. package/dist/frontend/components/CommandPalette/MessageBubble.js.map +7 -0
  24. package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js +91 -0
  25. package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js.map +7 -0
  26. package/dist/frontend/components/CommandPalette/ToolCallDisplay.js +47 -0
  27. package/dist/frontend/components/CommandPalette/ToolCallDisplay.js.map +7 -0
  28. package/dist/frontend/components/CommandPalette/ToolChatPage.js +74 -0
  29. package/dist/frontend/components/CommandPalette/ToolChatPage.js.map +7 -0
  30. package/dist/frontend/components/CommandPalette/index.js +28 -0
  31. package/dist/frontend/components/CommandPalette/index.js.map +7 -0
  32. package/dist/frontend/constants.js +41 -0
  33. package/dist/frontend/constants.js.map +7 -0
  34. package/dist/frontend/hooks/index.js +13 -0
  35. package/dist/frontend/hooks/index.js.map +7 -0
  36. package/dist/frontend/hooks/useCommandPalette.js +1094 -0
  37. package/dist/frontend/hooks/useCommandPalette.js.map +7 -0
  38. package/dist/frontend/hooks/useMcpTools.js +66 -0
  39. package/dist/frontend/hooks/useMcpTools.js.map +7 -0
  40. package/dist/frontend/hooks/usePageContext.js +48 -0
  41. package/dist/frontend/hooks/usePageContext.js.map +7 -0
  42. package/dist/frontend/hooks/useRecentActions.js +56 -0
  43. package/dist/frontend/hooks/useRecentActions.js.map +7 -0
  44. package/dist/frontend/hooks/useRecentTools.js +55 -0
  45. package/dist/frontend/hooks/useRecentTools.js.map +7 -0
  46. package/dist/frontend/index.js +35 -0
  47. package/dist/frontend/index.js.map +7 -0
  48. package/dist/frontend/types.js +1 -0
  49. package/dist/frontend/types.js.map +7 -0
  50. package/dist/frontend/utils/index.js +7 -0
  51. package/dist/frontend/utils/index.js.map +7 -0
  52. package/dist/frontend/utils/toolMatcher.js +95 -0
  53. package/dist/frontend/utils/toolMatcher.js.map +7 -0
  54. package/dist/index.js +57 -0
  55. package/dist/index.js.map +7 -0
  56. package/dist/modules/ai_assistant/acl.js +14 -0
  57. package/dist/modules/ai_assistant/acl.js.map +7 -0
  58. package/dist/modules/ai_assistant/api/chat/route.js +152 -0
  59. package/dist/modules/ai_assistant/api/chat/route.js.map +7 -0
  60. package/dist/modules/ai_assistant/api/health/route.js +27 -0
  61. package/dist/modules/ai_assistant/api/health/route.js.map +7 -0
  62. package/dist/modules/ai_assistant/api/route/route.js +123 -0
  63. package/dist/modules/ai_assistant/api/route/route.js.map +7 -0
  64. package/dist/modules/ai_assistant/api/settings/route.js +60 -0
  65. package/dist/modules/ai_assistant/api/settings/route.js.map +7 -0
  66. package/dist/modules/ai_assistant/api/tools/execute/route.js +58 -0
  67. package/dist/modules/ai_assistant/api/tools/execute/route.js.map +7 -0
  68. package/dist/modules/ai_assistant/api/tools/route.js +48 -0
  69. package/dist/modules/ai_assistant/api/tools/route.js.map +7 -0
  70. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js +10 -0
  71. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js.map +7 -0
  72. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js +28 -0
  73. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js.map +7 -0
  74. package/dist/modules/ai_assistant/cli.js +192 -0
  75. package/dist/modules/ai_assistant/cli.js.map +7 -0
  76. package/dist/modules/ai_assistant/di.js +11 -0
  77. package/dist/modules/ai_assistant/di.js.map +7 -0
  78. package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js +257 -0
  79. package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js.map +7 -0
  80. package/dist/modules/ai_assistant/index.js +13 -0
  81. package/dist/modules/ai_assistant/index.js.map +7 -0
  82. package/dist/modules/ai_assistant/lib/ai-sdk.js +13 -0
  83. package/dist/modules/ai_assistant/lib/ai-sdk.js.map +7 -0
  84. package/dist/modules/ai_assistant/lib/api-discovery-tools.js +249 -0
  85. package/dist/modules/ai_assistant/lib/api-discovery-tools.js.map +7 -0
  86. package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js +177 -0
  87. package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js.map +7 -0
  88. package/dist/modules/ai_assistant/lib/api-endpoint-index.js +210 -0
  89. package/dist/modules/ai_assistant/lib/api-endpoint-index.js.map +7 -0
  90. package/dist/modules/ai_assistant/lib/auth.js +87 -0
  91. package/dist/modules/ai_assistant/lib/auth.js.map +7 -0
  92. package/dist/modules/ai_assistant/lib/chat-config.js +117 -0
  93. package/dist/modules/ai_assistant/lib/chat-config.js.map +7 -0
  94. package/dist/modules/ai_assistant/lib/client-factory.js +60 -0
  95. package/dist/modules/ai_assistant/lib/client-factory.js.map +7 -0
  96. package/dist/modules/ai_assistant/lib/http-server.js +367 -0
  97. package/dist/modules/ai_assistant/lib/http-server.js.map +7 -0
  98. package/dist/modules/ai_assistant/lib/in-process-client.js +126 -0
  99. package/dist/modules/ai_assistant/lib/in-process-client.js.map +7 -0
  100. package/dist/modules/ai_assistant/lib/mcp-client.js +146 -0
  101. package/dist/modules/ai_assistant/lib/mcp-client.js.map +7 -0
  102. package/dist/modules/ai_assistant/lib/mcp-dev-server.js +283 -0
  103. package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +7 -0
  104. package/dist/modules/ai_assistant/lib/mcp-server-config.js +160 -0
  105. package/dist/modules/ai_assistant/lib/mcp-server-config.js.map +7 -0
  106. package/dist/modules/ai_assistant/lib/mcp-server.js +156 -0
  107. package/dist/modules/ai_assistant/lib/mcp-server.js.map +7 -0
  108. package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js +44 -0
  109. package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js.map +7 -0
  110. package/dist/modules/ai_assistant/lib/opencode-client.js +247 -0
  111. package/dist/modules/ai_assistant/lib/opencode-client.js.map +7 -0
  112. package/dist/modules/ai_assistant/lib/opencode-handlers.js +398 -0
  113. package/dist/modules/ai_assistant/lib/opencode-handlers.js.map +7 -0
  114. package/dist/modules/ai_assistant/lib/schema-utils.js +94 -0
  115. package/dist/modules/ai_assistant/lib/schema-utils.js.map +7 -0
  116. package/dist/modules/ai_assistant/lib/tool-executor.js +55 -0
  117. package/dist/modules/ai_assistant/lib/tool-executor.js.map +7 -0
  118. package/dist/modules/ai_assistant/lib/tool-index-config.js +125 -0
  119. package/dist/modules/ai_assistant/lib/tool-index-config.js.map +7 -0
  120. package/dist/modules/ai_assistant/lib/tool-loader.js +88 -0
  121. package/dist/modules/ai_assistant/lib/tool-loader.js.map +7 -0
  122. package/dist/modules/ai_assistant/lib/tool-registry.js +65 -0
  123. package/dist/modules/ai_assistant/lib/tool-registry.js.map +7 -0
  124. package/dist/modules/ai_assistant/lib/tool-search.js +192 -0
  125. package/dist/modules/ai_assistant/lib/tool-search.js.map +7 -0
  126. package/dist/modules/ai_assistant/lib/types.js +1 -0
  127. package/dist/modules/ai_assistant/lib/types.js.map +7 -0
  128. package/package.json +108 -0
  129. package/src/di.ts +11 -0
  130. package/src/frontend/components/CommandPalette/CommandFooter.tsx +113 -0
  131. package/src/frontend/components/CommandPalette/CommandHeader.tsx +76 -0
  132. package/src/frontend/components/CommandPalette/CommandInput.tsx +50 -0
  133. package/src/frontend/components/CommandPalette/CommandItem.tsx +111 -0
  134. package/src/frontend/components/CommandPalette/CommandPalette.tsx +276 -0
  135. package/src/frontend/components/CommandPalette/CommandPaletteProvider.tsx +60 -0
  136. package/src/frontend/components/CommandPalette/CommandPaletteWrapper.tsx +21 -0
  137. package/src/frontend/components/CommandPalette/DebugPanel.tsx +257 -0
  138. package/src/frontend/components/CommandPalette/MessageBubble.tsx +73 -0
  139. package/src/frontend/components/CommandPalette/ToolCallConfirmation.tsx +130 -0
  140. package/src/frontend/components/CommandPalette/ToolCallDisplay.tsx +57 -0
  141. package/src/frontend/components/CommandPalette/ToolChatPage.tsx +125 -0
  142. package/src/frontend/components/CommandPalette/index.ts +14 -0
  143. package/src/frontend/constants.ts +35 -0
  144. package/src/frontend/hooks/index.ts +5 -0
  145. package/src/frontend/hooks/useCommandPalette.ts +1389 -0
  146. package/src/frontend/hooks/useMcpTools.ts +73 -0
  147. package/src/frontend/hooks/usePageContext.ts +61 -0
  148. package/src/frontend/hooks/useRecentActions.ts +64 -0
  149. package/src/frontend/hooks/useRecentTools.ts +69 -0
  150. package/src/frontend/index.ts +39 -0
  151. package/src/frontend/types.ts +260 -0
  152. package/src/frontend/utils/index.ts +1 -0
  153. package/src/frontend/utils/toolMatcher.ts +127 -0
  154. package/src/index.ts +92 -0
  155. package/src/modules/ai_assistant/acl.ts +10 -0
  156. package/src/modules/ai_assistant/api/chat/route.ts +213 -0
  157. package/src/modules/ai_assistant/api/health/route.ts +30 -0
  158. package/src/modules/ai_assistant/api/route/route.ts +149 -0
  159. package/src/modules/ai_assistant/api/settings/route.ts +73 -0
  160. package/src/modules/ai_assistant/api/tools/execute/route.ts +71 -0
  161. package/src/modules/ai_assistant/api/tools/route.ts +57 -0
  162. package/src/modules/ai_assistant/backend/config/ai-assistant/page.meta.ts +26 -0
  163. package/src/modules/ai_assistant/backend/config/ai-assistant/page.tsx +12 -0
  164. package/src/modules/ai_assistant/cli.ts +233 -0
  165. package/src/modules/ai_assistant/di.ts +9 -0
  166. package/src/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.tsx +418 -0
  167. package/src/modules/ai_assistant/index.ts +11 -0
  168. package/src/modules/ai_assistant/lib/ai-sdk.ts +5 -0
  169. package/src/modules/ai_assistant/lib/api-discovery-tools.ts +334 -0
  170. package/src/modules/ai_assistant/lib/api-endpoint-index-config.ts +243 -0
  171. package/src/modules/ai_assistant/lib/api-endpoint-index.ts +381 -0
  172. package/src/modules/ai_assistant/lib/auth.ts +185 -0
  173. package/src/modules/ai_assistant/lib/chat-config.ts +152 -0
  174. package/src/modules/ai_assistant/lib/client-factory.ts +130 -0
  175. package/src/modules/ai_assistant/lib/http-server.ts +498 -0
  176. package/src/modules/ai_assistant/lib/in-process-client.ts +205 -0
  177. package/src/modules/ai_assistant/lib/mcp-client.ts +221 -0
  178. package/src/modules/ai_assistant/lib/mcp-dev-server.ts +373 -0
  179. package/src/modules/ai_assistant/lib/mcp-server-config.ts +287 -0
  180. package/src/modules/ai_assistant/lib/mcp-server.ts +214 -0
  181. package/src/modules/ai_assistant/lib/mcp-tool-adapter.ts +76 -0
  182. package/src/modules/ai_assistant/lib/opencode-client.ts +426 -0
  183. package/src/modules/ai_assistant/lib/opencode-handlers.ts +676 -0
  184. package/src/modules/ai_assistant/lib/schema-utils.ts +142 -0
  185. package/src/modules/ai_assistant/lib/tool-executor.ts +71 -0
  186. package/src/modules/ai_assistant/lib/tool-index-config.ts +178 -0
  187. package/src/modules/ai_assistant/lib/tool-loader.ts +149 -0
  188. package/src/modules/ai_assistant/lib/tool-registry.ts +114 -0
  189. package/src/modules/ai_assistant/lib/tool-search.ts +308 -0
  190. package/src/modules/ai_assistant/lib/types.ts +147 -0
  191. package/test-schema.ts +37 -0
  192. package/tsconfig.json +10 -0
  193. package/watch.mjs +6 -0
@@ -0,0 +1,1094 @@
1
+ "use client";
2
+ import { useState, useCallback, useEffect, useMemo, useRef } from "react";
3
+ import { COMMAND_PALETTE_SHORTCUT } from "../constants.js";
4
+ import { filterTools } from "../utils/toolMatcher.js";
5
+ import { useMcpTools } from "./useMcpTools.js";
6
+ import { useRecentActions } from "./useRecentActions.js";
7
+ import { useRecentTools } from "./useRecentTools.js";
8
+ function generateId() {
9
+ return Math.random().toString(36).substring(2, 15);
10
+ }
11
+ const SAFE_TOOL_PATTERNS = [
12
+ /^search_/,
13
+ // search_query, search_schema, search_get, search_aggregate, search_status
14
+ /^get_/,
15
+ // get_ operations are read-only
16
+ /^list_/,
17
+ // list_ operations are read-only
18
+ /^view_/,
19
+ // view_ operations are read-only
20
+ /^context_/,
21
+ // context_whoami etc.
22
+ /_get$/,
23
+ // tools ending with _get
24
+ /_list$/,
25
+ // tools ending with _list
26
+ /_status$/,
27
+ // tools ending with _status
28
+ /_schema$/
29
+ // tools ending with _schema
30
+ ];
31
+ const DANGEROUS_TOOL_PATTERNS = [
32
+ /^delete_/,
33
+ /^remove_/,
34
+ /_delete$/,
35
+ /_remove$/,
36
+ /^reindex_/,
37
+ /_reindex$/
38
+ ];
39
+ function isToolSafeToAutoExecute(toolName) {
40
+ if (DANGEROUS_TOOL_PATTERNS.some((p) => p.test(toolName))) {
41
+ return false;
42
+ }
43
+ return SAFE_TOOL_PATTERNS.some((p) => p.test(toolName));
44
+ }
45
+ function getToolPrompt(tool) {
46
+ const schema = tool.inputSchema;
47
+ if (!schema || typeof schema !== "object") {
48
+ return "What would you like to do?";
49
+ }
50
+ const properties = schema.properties;
51
+ if (!properties || Object.keys(properties).length === 0) {
52
+ return "This tool has no parameters. Ready to execute?";
53
+ }
54
+ const paramNames = Object.keys(properties).slice(0, 3);
55
+ return `Please provide the following: ${paramNames.join(", ")}.`;
56
+ }
57
+ function useCommandPalette(options) {
58
+ const { pageContext, selectedEntities = [], disableKeyboardShortcut = false } = options;
59
+ const [state, setState] = useState({
60
+ isOpen: false,
61
+ phase: "idle",
62
+ inputValue: "",
63
+ selectedIndex: 0,
64
+ isLoading: false,
65
+ isStreaming: false,
66
+ connectionStatus: "disconnected",
67
+ // Legacy fields for backwards compatibility
68
+ page: "home",
69
+ mode: "commands"
70
+ });
71
+ const { tools, isLoading: toolsLoading, executeTool: executeToolApi } = useMcpTools();
72
+ const { recentActions, addRecentAction } = useRecentActions();
73
+ const { recentTools, saveRecentTool } = useRecentTools(tools);
74
+ const [selectedTool, setSelectedTool] = useState(null);
75
+ const [messages, setMessages] = useState([]);
76
+ const [pendingToolCalls, setPendingToolCalls] = useState([]);
77
+ const [initialContext, setInitialContext] = useState(null);
78
+ const [availableEntities, setAvailableEntities] = useState(null);
79
+ const [debugEvents, setDebugEvents] = useState([]);
80
+ const [showDebug, setShowDebug] = useState(false);
81
+ const [opencodeSessionId, setOpencodeSessionId] = useState(null);
82
+ const opencodeSessionIdRef = useRef(null);
83
+ const [isThinking, setIsThinking] = useState(false);
84
+ const [isSessionAuthorized, setIsSessionAuthorized] = useState(false);
85
+ const [pendingQuestion, setPendingQuestion] = useState(null);
86
+ const answeredQuestionIds = useRef(/* @__PURE__ */ new Set());
87
+ const shouldStartNewMessage = useRef(false);
88
+ const currentStreamController = useRef(null);
89
+ const updateOpencodeSessionId = useCallback((id) => {
90
+ opencodeSessionIdRef.current = id;
91
+ setOpencodeSessionId(id);
92
+ }, []);
93
+ const addDebugEvent = useCallback((type, data) => {
94
+ const clonedData = JSON.parse(JSON.stringify(data));
95
+ setDebugEvents((prev) => [
96
+ ...prev.slice(-999),
97
+ // Keep last 1000 events
98
+ {
99
+ id: generateId(),
100
+ timestamp: /* @__PURE__ */ new Date(),
101
+ type,
102
+ data: clonedData
103
+ }
104
+ ]);
105
+ }, []);
106
+ const clearDebugEvents = useCallback(() => {
107
+ setDebugEvents([]);
108
+ }, []);
109
+ useEffect(() => {
110
+ if (toolsLoading) {
111
+ setState((prev) => ({ ...prev, connectionStatus: "connecting" }));
112
+ } else if (tools.length > 0) {
113
+ setState((prev) => ({ ...prev, connectionStatus: "connected" }));
114
+ } else {
115
+ setState((prev) => ({ ...prev, connectionStatus: "disconnected" }));
116
+ }
117
+ }, [tools, toolsLoading]);
118
+ useEffect(() => {
119
+ if (state.isOpen && tools.length > 0) {
120
+ if (!initialContext) {
121
+ console.log("[CommandPalette] Fetching initial context via context_whoami...");
122
+ executeToolApi("context_whoami", {}).then((result) => {
123
+ if (result.success && result.result) {
124
+ console.log("[CommandPalette] Got initial context:", result.result);
125
+ const ctx = result.result;
126
+ setInitialContext(ctx);
127
+ }
128
+ }).catch((err) => {
129
+ console.error("[CommandPalette] Failed to fetch initial context:", err);
130
+ });
131
+ }
132
+ if (!availableEntities) {
133
+ console.log("[CommandPalette] Fetching available entities via search_schema...");
134
+ executeToolApi("search_schema", {}).then((result) => {
135
+ if (result.success && result.result) {
136
+ const schemaResult = result.result;
137
+ console.log("[CommandPalette] Got entity schema:", schemaResult.entities?.length, "entities");
138
+ if (schemaResult.entities) {
139
+ setAvailableEntities(schemaResult.entities.filter((e) => e.enabled));
140
+ }
141
+ }
142
+ }).catch((err) => {
143
+ console.error("[CommandPalette] Failed to fetch entity schema:", err);
144
+ });
145
+ }
146
+ }
147
+ }, [state.isOpen, tools.length, initialContext, availableEntities, executeToolApi]);
148
+ const filteredTools = useMemo(() => {
149
+ const query = state.inputValue.startsWith("/") ? state.inputValue.slice(1) : state.inputValue;
150
+ return filterTools(tools, query);
151
+ }, [tools, state.inputValue]);
152
+ useEffect(() => {
153
+ if (disableKeyboardShortcut) return;
154
+ const handleKeyDown = (event) => {
155
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === COMMAND_PALETTE_SHORTCUT.key) {
156
+ event.preventDefault();
157
+ setState((prev) => {
158
+ if (prev.isOpen) {
159
+ return {
160
+ ...prev,
161
+ isOpen: false,
162
+ phase: "idle",
163
+ inputValue: "",
164
+ page: "home",
165
+ selectedIndex: 0,
166
+ mode: "commands"
167
+ };
168
+ } else {
169
+ return {
170
+ ...prev,
171
+ isOpen: true
172
+ };
173
+ }
174
+ });
175
+ if (state.isOpen) {
176
+ setSelectedTool(null);
177
+ setMessages([]);
178
+ setPendingToolCalls([]);
179
+ }
180
+ }
181
+ if (event.key === "Escape" && state.isOpen) {
182
+ event.preventDefault();
183
+ if (state.phase !== "idle") {
184
+ setState((prev) => ({
185
+ ...prev,
186
+ phase: "idle",
187
+ inputValue: "",
188
+ page: "home",
189
+ selectedIndex: 0,
190
+ mode: "commands"
191
+ }));
192
+ setSelectedTool(null);
193
+ setMessages([]);
194
+ setPendingToolCalls([]);
195
+ } else {
196
+ setState((prev) => ({
197
+ ...prev,
198
+ isOpen: false,
199
+ phase: "idle",
200
+ inputValue: "",
201
+ page: "home",
202
+ selectedIndex: 0,
203
+ mode: "commands"
204
+ }));
205
+ setSelectedTool(null);
206
+ setMessages([]);
207
+ setPendingToolCalls([]);
208
+ }
209
+ }
210
+ };
211
+ window.addEventListener("keydown", handleKeyDown);
212
+ return () => window.removeEventListener("keydown", handleKeyDown);
213
+ }, [state.isOpen, state.phase, state.inputValue, disableKeyboardShortcut]);
214
+ const open = useCallback(() => {
215
+ setState((prev) => ({ ...prev, isOpen: true }));
216
+ }, []);
217
+ const close = useCallback(() => {
218
+ setState((prev) => ({
219
+ ...prev,
220
+ isOpen: false,
221
+ phase: "idle",
222
+ inputValue: "",
223
+ page: "home",
224
+ selectedIndex: 0,
225
+ mode: "commands"
226
+ }));
227
+ setSelectedTool(null);
228
+ setMessages([]);
229
+ setPendingToolCalls([]);
230
+ updateOpencodeSessionId(null);
231
+ setIsSessionAuthorized(false);
232
+ }, [updateOpencodeSessionId]);
233
+ const reset = useCallback(() => {
234
+ setState((prev) => ({
235
+ ...prev,
236
+ phase: "idle",
237
+ inputValue: "",
238
+ page: "home",
239
+ selectedIndex: 0,
240
+ mode: "commands"
241
+ }));
242
+ setSelectedTool(null);
243
+ setMessages([]);
244
+ setPendingToolCalls([]);
245
+ updateOpencodeSessionId(null);
246
+ setIsSessionAuthorized(false);
247
+ }, [updateOpencodeSessionId]);
248
+ const setIsOpen = useCallback(
249
+ (isOpen) => {
250
+ if (isOpen) {
251
+ open();
252
+ } else {
253
+ close();
254
+ }
255
+ },
256
+ [open, close]
257
+ );
258
+ const setMode = useCallback((mode) => {
259
+ setState((prev) => ({ ...prev, mode }));
260
+ }, []);
261
+ const setInputValue = useCallback((value) => {
262
+ setState((prev) => ({ ...prev, inputValue: value, selectedIndex: 0 }));
263
+ }, []);
264
+ const setSelectedIndex = useCallback((index) => {
265
+ setState((prev) => ({ ...prev, selectedIndex: index }));
266
+ }, []);
267
+ const goToToolChat = useCallback(
268
+ (tool) => {
269
+ setSelectedTool(tool);
270
+ saveRecentTool(tool.name);
271
+ const initialMessage = {
272
+ id: generateId(),
273
+ role: "assistant",
274
+ content: `I'll help you with "${tool.name}". ${getToolPrompt(tool)}`,
275
+ createdAt: /* @__PURE__ */ new Date()
276
+ };
277
+ setMessages([initialMessage]);
278
+ setPendingToolCalls([]);
279
+ setState((prev) => ({
280
+ ...prev,
281
+ page: "tool-chat",
282
+ inputValue: "",
283
+ selectedIndex: 0,
284
+ mode: "chat"
285
+ }));
286
+ },
287
+ [saveRecentTool]
288
+ );
289
+ const goBack = useCallback(() => {
290
+ if (state.phase !== "idle") {
291
+ setState((prev) => ({
292
+ ...prev,
293
+ phase: "idle",
294
+ page: "home",
295
+ inputValue: "",
296
+ selectedIndex: 0,
297
+ mode: "commands"
298
+ }));
299
+ setSelectedTool(null);
300
+ setMessages([]);
301
+ setPendingToolCalls([]);
302
+ }
303
+ }, [state.phase]);
304
+ const routeQuery = useCallback(
305
+ async (query) => {
306
+ const response = await fetch("/api/ai_assistant/route", {
307
+ method: "POST",
308
+ headers: { "Content-Type": "application/json" },
309
+ body: JSON.stringify({
310
+ query,
311
+ availableTools: tools.map((t) => ({
312
+ name: t.name,
313
+ description: t.description
314
+ }))
315
+ })
316
+ });
317
+ if (!response.ok) {
318
+ throw new Error(`Routing failed: ${response.status}`);
319
+ }
320
+ return response.json();
321
+ },
322
+ [tools]
323
+ );
324
+ const startAgenticChat = useCallback(
325
+ async (initialQuery) => {
326
+ console.log("[startAgenticChat] Starting with query:", initialQuery);
327
+ setState((prev) => ({
328
+ ...prev,
329
+ phase: "chatting",
330
+ page: "tool-chat",
331
+ inputValue: "",
332
+ mode: "chat"
333
+ }));
334
+ setState((prev) => ({ ...prev, isStreaming: true }));
335
+ try {
336
+ const userMessage = {
337
+ id: generateId(),
338
+ role: "user",
339
+ content: initialQuery,
340
+ createdAt: /* @__PURE__ */ new Date()
341
+ };
342
+ setMessages([userMessage]);
343
+ currentStreamController.current?.abort();
344
+ const controller = new AbortController();
345
+ currentStreamController.current = controller;
346
+ console.log("[startAgenticChat] Sending request to /api/ai_assistant/chat with mode: agentic");
347
+ const response = await fetch("/api/ai_assistant/chat", {
348
+ method: "POST",
349
+ headers: { "Content-Type": "application/json" },
350
+ body: JSON.stringify({
351
+ messages: [{ role: "user", content: initialQuery }],
352
+ context: pageContext,
353
+ authContext: initialContext,
354
+ availableEntities: availableEntities?.map((e) => e.entityId),
355
+ mode: "agentic"
356
+ }),
357
+ signal: controller.signal
358
+ });
359
+ console.log("[startAgenticChat] Response status:", response.status, "ok:", response.ok);
360
+ if (!response.ok) {
361
+ const errorText = await response.text();
362
+ console.error("[startAgenticChat] Error response body:", errorText);
363
+ throw new Error(`Chat request failed: ${response.status} - ${errorText}`);
364
+ }
365
+ const reader = response.body?.getReader();
366
+ if (!reader) {
367
+ throw new Error("No response body");
368
+ }
369
+ const decoder = new TextDecoder();
370
+ let assistantContent = "";
371
+ let buffer = "";
372
+ let chunkCount = 0;
373
+ while (true) {
374
+ const { done, value } = await reader.read();
375
+ if (done) {
376
+ console.log("[startAgenticChat] Stream done after", chunkCount, "chunks");
377
+ break;
378
+ }
379
+ chunkCount++;
380
+ const rawChunk = decoder.decode(value, { stream: true });
381
+ buffer += rawChunk;
382
+ console.log("[startAgenticChat] Chunk", chunkCount, "raw:", rawChunk.substring(0, 200));
383
+ const lines = buffer.split("\n");
384
+ buffer = lines.pop() || "";
385
+ for (const line of lines) {
386
+ console.log("[startAgenticChat] Processing line:", line.substring(0, 100));
387
+ if (line.startsWith("data: ")) {
388
+ const data = line.slice(6);
389
+ if (data === "[DONE]") {
390
+ console.log("[startAgenticChat] Received [DONE]");
391
+ continue;
392
+ }
393
+ try {
394
+ const event = JSON.parse(data);
395
+ console.log("[startAgenticChat] Parsed event:", event.type, event);
396
+ if (event.type !== "question") {
397
+ addDebugEvent(event.type, event);
398
+ }
399
+ if (event.type === "text") {
400
+ setIsThinking(false);
401
+ if (shouldStartNewMessage.current) {
402
+ shouldStartNewMessage.current = false;
403
+ setMessages((prev) => prev.map((m) => m.id === "streaming" ? { ...m, id: generateId() } : m));
404
+ assistantContent = "";
405
+ }
406
+ assistantContent += event.content || "";
407
+ console.log("[startAgenticChat] Text content now:", assistantContent.substring(0, 100));
408
+ setMessages((prev) => {
409
+ const existingAssistant = prev.find((m) => m.id === "streaming");
410
+ if (existingAssistant) {
411
+ return prev.map(
412
+ (m) => m.id === "streaming" ? { ...m, content: assistantContent } : m
413
+ );
414
+ } else {
415
+ return [
416
+ ...prev,
417
+ {
418
+ id: "streaming",
419
+ role: "assistant",
420
+ content: assistantContent,
421
+ createdAt: /* @__PURE__ */ new Date()
422
+ }
423
+ ];
424
+ }
425
+ });
426
+ } else if (event.type === "tool-call") {
427
+ console.log("[startAgenticChat] Tool call event (executed server-side):", event.toolName);
428
+ const toolName = event.toolName;
429
+ const toolArgs = event.args ?? {};
430
+ if (!isToolSafeToAutoExecute(toolName)) {
431
+ console.log("[startAgenticChat] Dangerous tool was executed:", toolName);
432
+ setPendingToolCalls((prev) => [
433
+ ...prev,
434
+ {
435
+ id: event.id,
436
+ toolName,
437
+ args: toolArgs,
438
+ status: "completed"
439
+ // Already executed server-side
440
+ }
441
+ ]);
442
+ }
443
+ } else if (event.type === "error") {
444
+ console.error("[startAgenticChat] Error event:", event.error);
445
+ } else if (event.type === "done") {
446
+ console.log("[startAgenticChat] Done event received, sessionId:", event.sessionId);
447
+ console.log("[startAgenticChat] DIAGNOSTIC - Done event:", {
448
+ eventSessionId: event.sessionId,
449
+ currentRefValue: opencodeSessionIdRef.current,
450
+ willUpdate: !!event.sessionId
451
+ });
452
+ setIsThinking(false);
453
+ setState((prev) => ({ ...prev, isStreaming: false }));
454
+ if (event.sessionId) {
455
+ updateOpencodeSessionId(event.sessionId);
456
+ console.log("[startAgenticChat] DIAGNOSTIC - After update, ref value:", opencodeSessionIdRef.current);
457
+ }
458
+ } else if (event.type === "question") {
459
+ const question = event.question;
460
+ if (answeredQuestionIds.current.has(question.id)) {
461
+ console.log("[startAgenticChat] Skipping already-answered question:", question.id);
462
+ } else {
463
+ console.log("[startAgenticChat] Question event:", question.id, "questions:", question.questions);
464
+ setIsThinking(false);
465
+ setState((prev) => ({ ...prev, isStreaming: false }));
466
+ setPendingQuestion(question);
467
+ if (question.sessionID) {
468
+ updateOpencodeSessionId(question.sessionID);
469
+ }
470
+ addDebugEvent("question", {
471
+ type: "question",
472
+ questionId: question.id,
473
+ sessionID: question.sessionID,
474
+ questionText: question.questions?.[0]?.question || "No question text",
475
+ header: question.questions?.[0]?.header || "Confirmation",
476
+ options: question.questions?.[0]?.options?.map((o) => o.label) || [],
477
+ fullQuestion: question
478
+ });
479
+ }
480
+ } else if (event.type === "session-authorized") {
481
+ console.log("[startAgenticChat] Session authorized:", event.sessionToken);
482
+ setIsSessionAuthorized(true);
483
+ }
484
+ } catch (parseError) {
485
+ console.warn("[startAgenticChat] Failed to parse event:", data, parseError);
486
+ }
487
+ }
488
+ }
489
+ }
490
+ console.log("[startAgenticChat] Final assistant content:", assistantContent);
491
+ setMessages((prev) => prev.map((m) => m.id === "streaming" ? { ...m, id: generateId() } : m));
492
+ } catch (error) {
493
+ if (error instanceof Error && error.name === "AbortError") {
494
+ console.log("[startAgenticChat] Stream aborted (intentional)");
495
+ return;
496
+ }
497
+ console.error("[startAgenticChat] Error:", error);
498
+ setMessages((prev) => [
499
+ ...prev,
500
+ {
501
+ id: generateId(),
502
+ role: "assistant",
503
+ content: `Sorry, I encountered an error: ${error instanceof Error ? error.message : "Unknown error"}`,
504
+ createdAt: /* @__PURE__ */ new Date()
505
+ }
506
+ ]);
507
+ } finally {
508
+ setState((prev) => ({ ...prev, isStreaming: false }));
509
+ }
510
+ },
511
+ [pageContext, initialContext, availableEntities, executeToolApi, addDebugEvent, updateOpencodeSessionId]
512
+ );
513
+ const startGeneralChat = useCallback(
514
+ async (initialQuery) => {
515
+ setState((prev) => ({
516
+ ...prev,
517
+ phase: "chatting",
518
+ page: "tool-chat",
519
+ inputValue: "",
520
+ mode: "chat"
521
+ }));
522
+ setState((prev) => ({ ...prev, isStreaming: true }));
523
+ try {
524
+ const userMessage = {
525
+ id: generateId(),
526
+ role: "user",
527
+ content: initialQuery,
528
+ createdAt: /* @__PURE__ */ new Date()
529
+ };
530
+ setMessages([userMessage]);
531
+ const response = await fetch("/api/ai_assistant/chat", {
532
+ method: "POST",
533
+ headers: { "Content-Type": "application/json" },
534
+ body: JSON.stringify({
535
+ messages: [{ role: "user", content: initialQuery }],
536
+ context: pageContext,
537
+ authContext: initialContext,
538
+ availableEntities: availableEntities?.map((e) => e.entityId),
539
+ mode: "default"
540
+ })
541
+ });
542
+ if (!response.ok) {
543
+ throw new Error(`Chat request failed: ${response.status}`);
544
+ }
545
+ const reader = response.body?.getReader();
546
+ if (!reader) {
547
+ throw new Error("No response body");
548
+ }
549
+ const decoder = new TextDecoder();
550
+ let assistantContent = "";
551
+ while (true) {
552
+ const { done, value } = await reader.read();
553
+ if (done) break;
554
+ const chunk = decoder.decode(value, { stream: true });
555
+ assistantContent += chunk;
556
+ setMessages((prev) => {
557
+ const existingAssistant = prev.find((m) => m.id === "streaming");
558
+ if (existingAssistant) {
559
+ return prev.map(
560
+ (m) => m.id === "streaming" ? { ...m, content: assistantContent } : m
561
+ );
562
+ } else {
563
+ return [
564
+ ...prev,
565
+ {
566
+ id: "streaming",
567
+ role: "assistant",
568
+ content: assistantContent,
569
+ createdAt: /* @__PURE__ */ new Date()
570
+ }
571
+ ];
572
+ }
573
+ });
574
+ }
575
+ setMessages((prev) => prev.map((m) => m.id === "streaming" ? { ...m, id: generateId() } : m));
576
+ } catch (error) {
577
+ console.error("General chat error:", error);
578
+ setMessages((prev) => [
579
+ ...prev,
580
+ {
581
+ id: generateId(),
582
+ role: "assistant",
583
+ content: "Sorry, I encountered an error. Please try again.",
584
+ createdAt: /* @__PURE__ */ new Date()
585
+ }
586
+ ]);
587
+ } finally {
588
+ setState((prev) => ({ ...prev, isStreaming: false }));
589
+ }
590
+ },
591
+ [pageContext, initialContext, availableEntities]
592
+ );
593
+ const executeTool = useCallback(
594
+ async (toolName, args = {}) => {
595
+ setState((prev) => ({ ...prev, isLoading: true }));
596
+ try {
597
+ const result = await executeToolApi(toolName, args);
598
+ if (result.success) {
599
+ const tool = tools.find((t) => t.name === toolName);
600
+ addRecentAction({
601
+ toolName,
602
+ displayName: tool?.description || toolName,
603
+ args
604
+ });
605
+ }
606
+ return result;
607
+ } finally {
608
+ setState((prev) => ({ ...prev, isLoading: false }));
609
+ }
610
+ },
611
+ [executeToolApi, tools, addRecentAction]
612
+ );
613
+ const approveToolCall = useCallback(
614
+ async (toolCallId) => {
615
+ const toolCall = pendingToolCalls.find((tc) => tc.id === toolCallId);
616
+ if (!toolCall) return;
617
+ setPendingToolCalls(
618
+ (prev) => prev.map((tc) => tc.id === toolCallId ? { ...tc, status: "executing" } : tc)
619
+ );
620
+ try {
621
+ const result = await executeToolApi(toolCall.toolName, toolCall.args);
622
+ setPendingToolCalls(
623
+ (prev) => prev.map(
624
+ (tc) => tc.id === toolCallId ? {
625
+ ...tc,
626
+ status: result.success ? "completed" : "error",
627
+ result: result.result,
628
+ error: result.error
629
+ } : tc
630
+ )
631
+ );
632
+ if (result.success) {
633
+ const tool = tools.find((t) => t.name === toolCall.toolName);
634
+ addRecentAction({
635
+ toolName: toolCall.toolName,
636
+ displayName: tool?.description || toolCall.toolName,
637
+ args: toolCall.args
638
+ });
639
+ setMessages((prev) => [
640
+ ...prev,
641
+ {
642
+ id: generateId(),
643
+ role: "assistant",
644
+ content: `Done! The ${toolCall.toolName.replace(/_/g, " ")} operation completed successfully.`,
645
+ createdAt: /* @__PURE__ */ new Date()
646
+ }
647
+ ]);
648
+ } else {
649
+ setMessages((prev) => [
650
+ ...prev,
651
+ {
652
+ id: generateId(),
653
+ role: "assistant",
654
+ content: `I encountered an issue: ${result.error || "Unknown error"}`,
655
+ createdAt: /* @__PURE__ */ new Date()
656
+ }
657
+ ]);
658
+ }
659
+ } catch (error) {
660
+ setPendingToolCalls(
661
+ (prev) => prev.map(
662
+ (tc) => tc.id === toolCallId ? {
663
+ ...tc,
664
+ status: "error",
665
+ error: error instanceof Error ? error.message : "Unknown error"
666
+ } : tc
667
+ )
668
+ );
669
+ }
670
+ },
671
+ [pendingToolCalls, executeToolApi, tools, addRecentAction]
672
+ );
673
+ const rejectToolCall = useCallback((toolCallId) => {
674
+ setPendingToolCalls(
675
+ (prev) => prev.map((tc) => tc.id === toolCallId ? { ...tc, status: "rejected" } : tc)
676
+ );
677
+ setMessages((prev) => [
678
+ ...prev,
679
+ {
680
+ id: generateId(),
681
+ role: "assistant",
682
+ content: "Tool call cancelled. How else can I help?",
683
+ createdAt: /* @__PURE__ */ new Date()
684
+ }
685
+ ]);
686
+ }, []);
687
+ const sendAgenticMessage = useCallback(
688
+ async (content) => {
689
+ if (!content.trim()) return;
690
+ const userMessage = {
691
+ id: generateId(),
692
+ role: "user",
693
+ content: content.trim(),
694
+ createdAt: /* @__PURE__ */ new Date()
695
+ };
696
+ setMessages((prev) => [...prev, userMessage]);
697
+ setState((prev) => ({ ...prev, isStreaming: true }));
698
+ setIsThinking(true);
699
+ try {
700
+ console.log("[sendAgenticMessage] DIAGNOSTIC - About to send request:", {
701
+ sessionId: opencodeSessionIdRef.current,
702
+ messagesLength: messages.length + 1,
703
+ lastMessageContent: userMessage.content.substring(0, 50) + "..."
704
+ });
705
+ const response = await fetch("/api/ai_assistant/chat", {
706
+ method: "POST",
707
+ headers: { "Content-Type": "application/json" },
708
+ body: JSON.stringify({
709
+ messages: [...messages, userMessage].map((m) => ({
710
+ role: m.role,
711
+ content: m.content
712
+ })),
713
+ sessionId: opencodeSessionIdRef.current
714
+ })
715
+ });
716
+ if (!response.ok) {
717
+ throw new Error(`Chat request failed: ${response.status}`);
718
+ }
719
+ const reader = response.body?.getReader();
720
+ if (!reader) {
721
+ throw new Error("No response body");
722
+ }
723
+ const decoder = new TextDecoder();
724
+ let assistantContent = "";
725
+ let buffer = "";
726
+ while (true) {
727
+ const { done, value } = await reader.read();
728
+ if (done) break;
729
+ buffer += decoder.decode(value, { stream: true });
730
+ const lines = buffer.split("\n");
731
+ buffer = lines.pop() || "";
732
+ for (const line of lines) {
733
+ if (line.startsWith("data: ")) {
734
+ const data = line.slice(6);
735
+ if (data === "[DONE]") continue;
736
+ try {
737
+ const event = JSON.parse(data);
738
+ if (event.type !== "question") {
739
+ addDebugEvent(event.type, event);
740
+ }
741
+ if (event.type === "thinking") {
742
+ setIsThinking(true);
743
+ } else if (event.type === "session-authorized") {
744
+ console.log("[sendAgenticMessage] Session authorized:", event.sessionToken);
745
+ setIsSessionAuthorized(true);
746
+ } else if (event.type === "text") {
747
+ if (shouldStartNewMessage.current) {
748
+ shouldStartNewMessage.current = false;
749
+ setMessages((prev) => prev.map((m) => m.id === "streaming" ? { ...m, id: generateId() } : m));
750
+ assistantContent = "";
751
+ }
752
+ setIsThinking(false);
753
+ assistantContent += event.content || "";
754
+ setMessages((prev) => {
755
+ const existingAssistant = prev.find(
756
+ (m) => m.role === "assistant" && m.id === "streaming"
757
+ );
758
+ if (existingAssistant) {
759
+ return prev.map(
760
+ (m) => m.id === "streaming" ? { ...m, content: assistantContent } : m
761
+ );
762
+ } else {
763
+ return [
764
+ ...prev,
765
+ {
766
+ id: "streaming",
767
+ role: "assistant",
768
+ content: assistantContent,
769
+ createdAt: /* @__PURE__ */ new Date()
770
+ }
771
+ ];
772
+ }
773
+ });
774
+ } else if (event.type === "done") {
775
+ setIsThinking(false);
776
+ setState((prev) => ({ ...prev, isStreaming: false }));
777
+ if (event.sessionId) {
778
+ updateOpencodeSessionId(event.sessionId);
779
+ }
780
+ } else if (event.type === "error") {
781
+ setIsThinking(false);
782
+ setMessages((prev) => [
783
+ ...prev,
784
+ {
785
+ id: generateId(),
786
+ role: "assistant",
787
+ content: `Error: ${event.error || "Unknown error occurred"}`,
788
+ createdAt: /* @__PURE__ */ new Date()
789
+ }
790
+ ]);
791
+ } else if (event.type === "tool-call") {
792
+ console.log("[sendAgenticMessage] Tool call event (executed server-side):", event.toolName);
793
+ const toolName = event.toolName;
794
+ const toolArgs = event.args ?? {};
795
+ if (!isToolSafeToAutoExecute(toolName)) {
796
+ console.log("[sendAgenticMessage] Dangerous tool was executed:", toolName);
797
+ setPendingToolCalls((prev) => [
798
+ ...prev,
799
+ { id: event.id, toolName, args: toolArgs, status: "completed" }
800
+ ]);
801
+ }
802
+ } else if (event.type === "question") {
803
+ const question = event.question;
804
+ if (answeredQuestionIds.current.has(question.id)) {
805
+ console.log("[sendAgenticMessage] Skipping already-answered question:", question.id);
806
+ } else {
807
+ console.log("[sendAgenticMessage] Question event:", question.id, "questions:", question.questions);
808
+ setIsThinking(false);
809
+ setState((prev) => ({ ...prev, isStreaming: false }));
810
+ setPendingQuestion(question);
811
+ addDebugEvent("question", {
812
+ type: "question",
813
+ questionId: question.id,
814
+ sessionID: question.sessionID,
815
+ questionText: question.questions?.[0]?.question || "No question text",
816
+ header: question.questions?.[0]?.header || "Confirmation",
817
+ options: question.questions?.[0]?.options?.map((o) => o.label) || [],
818
+ fullQuestion: question
819
+ });
820
+ }
821
+ }
822
+ } catch {
823
+ assistantContent += data;
824
+ setMessages((prev) => {
825
+ const existingAssistant = prev.find(
826
+ (m) => m.role === "assistant" && m.id === "streaming"
827
+ );
828
+ if (existingAssistant) {
829
+ return prev.map(
830
+ (m) => m.id === "streaming" ? { ...m, content: assistantContent } : m
831
+ );
832
+ } else {
833
+ return [
834
+ ...prev,
835
+ {
836
+ id: "streaming",
837
+ role: "assistant",
838
+ content: assistantContent,
839
+ createdAt: /* @__PURE__ */ new Date()
840
+ }
841
+ ];
842
+ }
843
+ });
844
+ }
845
+ } else if (line.trim() && !line.startsWith(":")) {
846
+ assistantContent += line;
847
+ setMessages((prev) => {
848
+ const existingAssistant = prev.find(
849
+ (m) => m.role === "assistant" && m.id === "streaming"
850
+ );
851
+ if (existingAssistant) {
852
+ return prev.map(
853
+ (m) => m.id === "streaming" ? { ...m, content: assistantContent } : m
854
+ );
855
+ } else {
856
+ return [
857
+ ...prev,
858
+ {
859
+ id: "streaming",
860
+ role: "assistant",
861
+ content: assistantContent,
862
+ createdAt: /* @__PURE__ */ new Date()
863
+ }
864
+ ];
865
+ }
866
+ });
867
+ }
868
+ }
869
+ }
870
+ setMessages((prev) => prev.map((m) => m.id === "streaming" ? { ...m, id: generateId() } : m));
871
+ } catch (error) {
872
+ console.error("Tool chat error:", error);
873
+ setMessages((prev) => [
874
+ ...prev,
875
+ {
876
+ id: generateId(),
877
+ role: "assistant",
878
+ content: "Sorry, I encountered an error. Please try again.",
879
+ createdAt: /* @__PURE__ */ new Date()
880
+ }
881
+ ]);
882
+ } finally {
883
+ setState((prev) => ({ ...prev, isStreaming: false }));
884
+ setIsThinking(false);
885
+ }
886
+ },
887
+ [messages, addDebugEvent, updateOpencodeSessionId]
888
+ );
889
+ const handleSubmit = useCallback(
890
+ async (query) => {
891
+ if (!query.trim()) return;
892
+ console.log("[handleSubmit] DIAGNOSTIC - Session check:", {
893
+ refValue: opencodeSessionIdRef.current,
894
+ willContinue: !!opencodeSessionIdRef.current,
895
+ query: query.substring(0, 50) + "..."
896
+ });
897
+ if (opencodeSessionIdRef.current) {
898
+ console.log("[handleSubmit] Continuing session with query:", query, "sessionId:", opencodeSessionIdRef.current);
899
+ await sendAgenticMessage(query);
900
+ } else {
901
+ console.log("[handleSubmit] Starting new agentic chat with query:", query);
902
+ await startAgenticChat(query);
903
+ }
904
+ },
905
+ [startAgenticChat, sendAgenticMessage]
906
+ );
907
+ const answerQuestion = useCallback(
908
+ async (answer) => {
909
+ if (!pendingQuestion) return;
910
+ console.log("[answerQuestion] Answering question:", pendingQuestion.id, "with:", answer);
911
+ const questionId = pendingQuestion.id;
912
+ answeredQuestionIds.current.add(questionId);
913
+ shouldStartNewMessage.current = true;
914
+ setPendingQuestion(null);
915
+ setIsThinking(true);
916
+ const selectedOption = pendingQuestion.questions[0]?.options[answer];
917
+ setMessages((prev) => [
918
+ ...prev,
919
+ {
920
+ id: generateId(),
921
+ role: "user",
922
+ content: `[Confirmed: ${selectedOption?.label || "Yes"}]`,
923
+ createdAt: /* @__PURE__ */ new Date()
924
+ }
925
+ ]);
926
+ try {
927
+ const sessionId = pendingQuestion.sessionID;
928
+ const response = await fetch("/api/ai_assistant/chat", {
929
+ method: "POST",
930
+ headers: { "Content-Type": "application/json" },
931
+ body: JSON.stringify({
932
+ answerQuestion: {
933
+ questionId,
934
+ answer,
935
+ sessionId
936
+ }
937
+ })
938
+ });
939
+ if (!response.ok) {
940
+ const errorData = await response.json().catch(() => ({}));
941
+ throw new Error(errorData.error || `Answer request failed: ${response.status}`);
942
+ }
943
+ console.log("[answerQuestion] Answer sent, original stream will receive response");
944
+ } catch (error) {
945
+ console.error("[answerQuestion] Error:", error);
946
+ setIsThinking(false);
947
+ setMessages((prev) => [
948
+ ...prev,
949
+ {
950
+ id: generateId(),
951
+ role: "assistant",
952
+ content: `Error: ${error instanceof Error ? error.message : "Failed to send answer"}`,
953
+ createdAt: /* @__PURE__ */ new Date()
954
+ }
955
+ ]);
956
+ }
957
+ },
958
+ [pendingQuestion]
959
+ );
960
+ const sendMessage = useCallback(
961
+ async (content) => {
962
+ if (!content.trim()) return;
963
+ const userMessage = {
964
+ id: generateId(),
965
+ role: "user",
966
+ content: content.trim(),
967
+ createdAt: /* @__PURE__ */ new Date()
968
+ };
969
+ setMessages((prev) => [...prev, userMessage]);
970
+ setState((prev) => ({ ...prev, isStreaming: true }));
971
+ try {
972
+ const response = await fetch("/api/ai_assistant/chat", {
973
+ method: "POST",
974
+ headers: { "Content-Type": "application/json" },
975
+ body: JSON.stringify({
976
+ messages: [...messages, userMessage].map((m) => ({
977
+ role: m.role,
978
+ content: m.content
979
+ })),
980
+ context: pageContext
981
+ })
982
+ });
983
+ if (!response.ok) {
984
+ throw new Error(`Chat request failed: ${response.status}`);
985
+ }
986
+ const reader = response.body?.getReader();
987
+ if (!reader) {
988
+ throw new Error("No response body");
989
+ }
990
+ const decoder = new TextDecoder();
991
+ let assistantContent = "";
992
+ while (true) {
993
+ const { done, value } = await reader.read();
994
+ if (done) break;
995
+ const chunk = decoder.decode(value, { stream: true });
996
+ assistantContent += chunk;
997
+ setMessages((prev) => {
998
+ const existingAssistant = prev.find(
999
+ (m) => m.role === "assistant" && m.id === "streaming"
1000
+ );
1001
+ if (existingAssistant) {
1002
+ return prev.map(
1003
+ (m) => m.id === "streaming" ? { ...m, content: assistantContent } : m
1004
+ );
1005
+ } else {
1006
+ return [
1007
+ ...prev,
1008
+ {
1009
+ id: "streaming",
1010
+ role: "assistant",
1011
+ content: assistantContent,
1012
+ createdAt: /* @__PURE__ */ new Date()
1013
+ }
1014
+ ];
1015
+ }
1016
+ });
1017
+ }
1018
+ setMessages((prev) => prev.map((m) => m.id === "streaming" ? { ...m, id: generateId() } : m));
1019
+ } catch (error) {
1020
+ console.error("Chat error:", error);
1021
+ setMessages((prev) => [
1022
+ ...prev,
1023
+ {
1024
+ id: generateId(),
1025
+ role: "assistant",
1026
+ content: "Sorry, I encountered an error. Please try again.",
1027
+ createdAt: /* @__PURE__ */ new Date()
1028
+ }
1029
+ ]);
1030
+ } finally {
1031
+ setState((prev) => ({ ...prev, isStreaming: false }));
1032
+ }
1033
+ },
1034
+ [messages, pageContext]
1035
+ );
1036
+ const clearMessages = useCallback(() => {
1037
+ setMessages([]);
1038
+ setPendingToolCalls([]);
1039
+ }, []);
1040
+ return {
1041
+ // State
1042
+ state: {
1043
+ ...state,
1044
+ isLoading: state.isLoading || toolsLoading
1045
+ },
1046
+ isThinking,
1047
+ isSessionAuthorized,
1048
+ pageContext,
1049
+ selectedEntities,
1050
+ tools,
1051
+ filteredTools,
1052
+ recentActions,
1053
+ recentTools,
1054
+ messages,
1055
+ pendingToolCalls,
1056
+ selectedTool,
1057
+ initialContext,
1058
+ availableEntities,
1059
+ // Navigation actions
1060
+ open,
1061
+ close,
1062
+ setIsOpen,
1063
+ setInputValue,
1064
+ setSelectedIndex,
1065
+ // Intelligent routing - submit natural language query
1066
+ handleSubmit,
1067
+ reset,
1068
+ // Page navigation (legacy, kept for compatibility)
1069
+ goToToolChat,
1070
+ goBack,
1071
+ // Tool execution
1072
+ executeTool,
1073
+ approveToolCall,
1074
+ rejectToolCall,
1075
+ // Chat actions
1076
+ sendMessage,
1077
+ sendAgenticMessage,
1078
+ clearMessages,
1079
+ // Legacy compatibility
1080
+ setMode,
1081
+ // Debug mode
1082
+ debugEvents,
1083
+ showDebug,
1084
+ setShowDebug,
1085
+ clearDebugEvents,
1086
+ // OpenCode question handling
1087
+ pendingQuestion,
1088
+ answerQuestion
1089
+ };
1090
+ }
1091
+ export {
1092
+ useCommandPalette
1093
+ };
1094
+ //# sourceMappingURL=useCommandPalette.js.map