@xalia/agent 0.6.9 → 0.6.11

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 (199) hide show
  1. package/README.md +11 -0
  2. package/dist/agent/src/agent/agent.js +77 -18
  3. package/dist/agent/src/agent/agentUtils.js +3 -2
  4. package/dist/agent/src/agent/documentSummarizer.js +126 -0
  5. package/dist/agent/src/agent/dummyLLM.js +25 -22
  6. package/dist/agent/src/agent/imageGenLLM.js +22 -19
  7. package/dist/agent/src/agent/llm.js +1 -1
  8. package/dist/agent/src/agent/openAILLM.js +15 -12
  9. package/dist/agent/src/agent/openAILLMStreaming.js +68 -37
  10. package/dist/agent/src/agent/repeatLLM.js +16 -7
  11. package/dist/agent/src/agent/tokenCounter.js +390 -0
  12. package/dist/agent/src/agent/tokenCounter.test.js +206 -0
  13. package/dist/agent/src/agent/toolSettings.js +17 -0
  14. package/dist/agent/src/agent/tools/calculatorTool.js +45 -0
  15. package/dist/agent/src/agent/tools/contentExtractors/pdfToText.js +55 -0
  16. package/dist/agent/src/agent/tools/datetimeTool.js +38 -0
  17. package/dist/agent/src/agent/tools/fileManager/fileManagerTool.js +156 -0
  18. package/dist/agent/src/agent/tools/fileManager/index.js +31 -0
  19. package/dist/agent/src/agent/tools/fileManager/memoryFileManager.js +102 -0
  20. package/dist/agent/src/{chat/data → agent/tools/fileManager}/mimeTypes.js +3 -1
  21. package/dist/agent/src/agent/tools/fileManager/prompt.js +33 -0
  22. package/dist/agent/src/{chat/data/dbSessionFileModels.js → agent/tools/fileManager/types.js} +7 -0
  23. package/dist/agent/src/agent/tools/index.js +64 -0
  24. package/dist/agent/src/agent/tools/openUrlTool.js +57 -0
  25. package/dist/agent/src/agent/tools/renderTool.js +89 -0
  26. package/dist/agent/src/agent/tools/utils.js +61 -0
  27. package/dist/agent/src/{chat/utils/search.js → agent/tools/webSearch.js} +1 -2
  28. package/dist/agent/src/agent/tools/webSearchTool.js +40 -0
  29. package/dist/agent/src/chat/client/chatClient.js +28 -0
  30. package/dist/agent/src/chat/client/index.js +4 -1
  31. package/dist/agent/src/chat/client/sessionClient.js +28 -2
  32. package/dist/agent/src/chat/constants.js +8 -0
  33. package/dist/agent/src/chat/data/dbSessionFiles.js +11 -6
  34. package/dist/agent/src/chat/protocol/messages.js +5 -0
  35. package/dist/agent/src/chat/server/chatContextManager.js +45 -25
  36. package/dist/agent/src/chat/server/conversation.js +3 -0
  37. package/dist/agent/src/chat/server/imageGeneratorTools.js +20 -8
  38. package/dist/agent/src/chat/server/openAIRouterLLM.js +0 -3
  39. package/dist/agent/src/chat/server/openSession.js +218 -55
  40. package/dist/agent/src/chat/server/promptRefiner.js +86 -0
  41. package/dist/agent/src/chat/server/server.js +5 -1
  42. package/dist/agent/src/chat/server/sessionFileManager.js +22 -221
  43. package/dist/agent/src/chat/server/sessionRegistry.js +87 -0
  44. package/dist/agent/src/chat/server/titleGenerator.js +112 -0
  45. package/dist/agent/src/chat/server/titleGenerator.test.js +113 -0
  46. package/dist/agent/src/chat/server/tools.js +63 -287
  47. package/dist/agent/src/chat/utils/approvalManager.js +6 -3
  48. package/dist/agent/src/chat/utils/multiAsyncQueue.js +3 -0
  49. package/dist/agent/src/test/agent.test.js +16 -17
  50. package/dist/agent/src/test/chatContextManager.test.js +15 -3
  51. package/dist/agent/src/test/dbMcpServerConfigs.test.js +4 -4
  52. package/dist/agent/src/test/dbSessionFiles.test.js +17 -17
  53. package/dist/agent/src/test/testTools.js +6 -1
  54. package/dist/agent/src/test/tools.test.js +27 -9
  55. package/dist/agent/src/tool/agentChat.js +5 -2
  56. package/dist/agent/src/tool/chatMain.js +34 -7
  57. package/dist/agent/src/tool/commandPrompt.js +2 -2
  58. package/dist/agent/src/tool/files.js +7 -8
  59. package/package.json +8 -2
  60. package/.env.development +0 -1
  61. package/.prettierrc.json +0 -11
  62. package/dist/agent/src/agent/tools.js +0 -44
  63. package/eslint.config.mjs +0 -38
  64. package/scripts/chat_server +0 -8
  65. package/scripts/git_message +0 -31
  66. package/scripts/git_wip +0 -21
  67. package/scripts/pr_message +0 -18
  68. package/scripts/pr_review +0 -16
  69. package/scripts/setup_chat +0 -90
  70. package/scripts/shutdown_chat_server +0 -42
  71. package/scripts/start_chat_server +0 -24
  72. package/scripts/sudomcp_import +0 -23
  73. package/scripts/test_chat +0 -308
  74. package/src/agent/agent.ts +0 -624
  75. package/src/agent/agentUtils.ts +0 -285
  76. package/src/agent/compressingContextManager.ts +0 -129
  77. package/src/agent/context.ts +0 -265
  78. package/src/agent/contextWithWorkspace.ts +0 -162
  79. package/src/agent/dummyLLM.ts +0 -126
  80. package/src/agent/iAgentEventHandler.ts +0 -64
  81. package/src/agent/imageGenLLM.ts +0 -97
  82. package/src/agent/imageGenerator.ts +0 -45
  83. package/src/agent/iplatform.ts +0 -18
  84. package/src/agent/llm.ts +0 -74
  85. package/src/agent/mcpServerManager.ts +0 -541
  86. package/src/agent/nullAgentEventHandler.ts +0 -26
  87. package/src/agent/nullPlatform.ts +0 -13
  88. package/src/agent/openAI.ts +0 -123
  89. package/src/agent/openAILLM.ts +0 -95
  90. package/src/agent/openAILLMStreaming.ts +0 -609
  91. package/src/agent/promptProvider.ts +0 -87
  92. package/src/agent/repeatLLM.ts +0 -50
  93. package/src/agent/sudoMcpServerManager.ts +0 -361
  94. package/src/agent/tokenAuth.ts +0 -50
  95. package/src/agent/tools.ts +0 -57
  96. package/src/chat/client/chatClient.ts +0 -922
  97. package/src/chat/client/connection.test.ts +0 -241
  98. package/src/chat/client/connection.ts +0 -286
  99. package/src/chat/client/constants.ts +0 -1
  100. package/src/chat/client/index.ts +0 -18
  101. package/src/chat/client/interfaces.ts +0 -34
  102. package/src/chat/client/sessionClient.ts +0 -537
  103. package/src/chat/client/sessionFiles.ts +0 -142
  104. package/src/chat/client/teamManager.ts +0 -29
  105. package/src/chat/data/apiKeyManager.ts +0 -76
  106. package/src/chat/data/dataModels.ts +0 -101
  107. package/src/chat/data/database.ts +0 -997
  108. package/src/chat/data/dbMcpServerConfigs.ts +0 -59
  109. package/src/chat/data/dbSessionFileModels.ts +0 -113
  110. package/src/chat/data/dbSessionFiles.ts +0 -99
  111. package/src/chat/data/dbSessionMessages.ts +0 -102
  112. package/src/chat/data/mimeTypes.ts +0 -58
  113. package/src/chat/protocol/connectionMessages.ts +0 -49
  114. package/src/chat/protocol/constants.ts +0 -55
  115. package/src/chat/protocol/errors.ts +0 -16
  116. package/src/chat/protocol/messages.ts +0 -846
  117. package/src/chat/server/README.md +0 -127
  118. package/src/chat/server/chatContextManager.ts +0 -639
  119. package/src/chat/server/connectionManager.test.ts +0 -246
  120. package/src/chat/server/connectionManager.ts +0 -506
  121. package/src/chat/server/conversation.ts +0 -316
  122. package/src/chat/server/errorUtils.ts +0 -28
  123. package/src/chat/server/imageGeneratorTools.ts +0 -160
  124. package/src/chat/server/openAIRouterLLM.ts +0 -171
  125. package/src/chat/server/openSession.ts +0 -1689
  126. package/src/chat/server/openSessionMessageSender.ts +0 -4
  127. package/src/chat/server/server.ts +0 -175
  128. package/src/chat/server/sessionFileManager.ts +0 -422
  129. package/src/chat/server/sessionRegistry.test.ts +0 -137
  130. package/src/chat/server/sessionRegistry.ts +0 -1425
  131. package/src/chat/server/test-utils/mockFactories.ts +0 -422
  132. package/src/chat/server/tools.ts +0 -397
  133. package/src/chat/utils/agentSessionMap.ts +0 -76
  134. package/src/chat/utils/approvalManager.ts +0 -183
  135. package/src/chat/utils/asyncLock.ts +0 -43
  136. package/src/chat/utils/asyncQueue.ts +0 -62
  137. package/src/chat/utils/htmlToText.ts +0 -61
  138. package/src/chat/utils/multiAsyncQueue.ts +0 -62
  139. package/src/chat/utils/responseAwaiter.ts +0 -181
  140. package/src/chat/utils/search.ts +0 -139
  141. package/src/chat/utils/userResolver.ts +0 -48
  142. package/src/chat/utils/websocket.ts +0 -16
  143. package/src/index.ts +0 -0
  144. package/src/test/agent.test.ts +0 -590
  145. package/src/test/approvalManager.test.ts +0 -141
  146. package/src/test/chatContextManager.test.ts +0 -527
  147. package/src/test/clientServerConnection.test.ts +0 -205
  148. package/src/test/compressingContextManager.test.ts +0 -77
  149. package/src/test/context.test.ts +0 -150
  150. package/src/test/contextTestTools.ts +0 -95
  151. package/src/test/conversation.test.ts +0 -109
  152. package/src/test/db.test.ts +0 -363
  153. package/src/test/dbMcpServerConfigs.test.ts +0 -112
  154. package/src/test/dbSessionFiles.test.ts +0 -258
  155. package/src/test/dbSessionMessages.test.ts +0 -85
  156. package/src/test/dbTestTools.ts +0 -157
  157. package/src/test/imageLoad.test.ts +0 -15
  158. package/src/test/mcpServerManager.test.ts +0 -114
  159. package/src/test/multiAsyncQueue.test.ts +0 -183
  160. package/src/test/openaiStreaming.test.ts +0 -177
  161. package/src/test/prompt.test.ts +0 -27
  162. package/src/test/promptProvider.test.ts +0 -33
  163. package/src/test/responseAwaiter.test.ts +0 -103
  164. package/src/test/sudoMcpServerManager.test.ts +0 -63
  165. package/src/test/testTools.ts +0 -171
  166. package/src/test/tools.test.ts +0 -39
  167. package/src/tool/agentChat.ts +0 -194
  168. package/src/tool/agentMain.ts +0 -180
  169. package/src/tool/chatMain.ts +0 -594
  170. package/src/tool/commandPrompt.ts +0 -264
  171. package/src/tool/files.ts +0 -84
  172. package/src/tool/main.ts +0 -25
  173. package/src/tool/nodePlatform.ts +0 -73
  174. package/src/tool/options.ts +0 -144
  175. package/src/tool/prompt.ts +0 -101
  176. package/test_data/background_test_profile.json +0 -6
  177. package/test_data/background_test_script.json +0 -11
  178. package/test_data/dummyllm_script_crash.json +0 -32
  179. package/test_data/dummyllm_script_image_gen.json +0 -19
  180. package/test_data/dummyllm_script_image_gen_fe.json +0 -29
  181. package/test_data/dummyllm_script_invoke_image_gen_tool.json +0 -37
  182. package/test_data/dummyllm_script_render_tool.json +0 -29
  183. package/test_data/dummyllm_script_simplecalc.json +0 -28
  184. package/test_data/dummyllm_script_test_auto_approve.json +0 -81
  185. package/test_data/dummyllm_script_test_simplecalc_addition.json +0 -29
  186. package/test_data/frog.png +0 -0
  187. package/test_data/frog.png.b64 +0 -1
  188. package/test_data/git_message_profile.json +0 -4
  189. package/test_data/git_wip_system.txt +0 -5
  190. package/test_data/image_gen_test_profile.json +0 -5
  191. package/test_data/pr_message_profile.json +0 -4
  192. package/test_data/pr_review_profile.json +0 -4
  193. package/test_data/prompt_simplecalc.txt +0 -1
  194. package/test_data/simplecalc_profile.json +0 -4
  195. package/test_data/sudomcp_import_profile.json +0 -4
  196. package/test_data/test_script_profile.json +0 -8
  197. package/tsconfig.json +0 -13
  198. package/vitest.config.ts +0 -39
  199. /package/dist/agent/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.js +0 -0
@@ -1,397 +0,0 @@
1
- /**
2
- * Collection of simple Agent tools.
3
- */
4
-
5
- import { Parser } from "expr-eval";
6
-
7
- import {
8
- Agent,
9
- AgentEx,
10
- IAgentToolProvider,
11
- ToolCallResult,
12
- } from "../../agent/agent";
13
- import { getLogger } from "@xalia/xmcp/sdk";
14
-
15
- import { htmlToText } from "../utils/htmlToText";
16
- import { webSearch } from "../utils/search";
17
- import {
18
- ChatSessionFileManager,
19
- ToolCallResultWithFileRef,
20
- fileManagerTool,
21
- } from "./sessionFileManager";
22
- import { genImageFileTool } from "./imageGeneratorTools";
23
- import { ToolDescriptor } from "../../agent/llm";
24
- import { IPlatform } from "../../agent/iplatform";
25
-
26
- const logger = getLogger();
27
-
28
- const DEVELOPMENT: boolean = process.env.DEVELOPMENT === "1";
29
-
30
- /**
31
- * Returns a function which parses an `args` struct and attempts to extract
32
- * multiple string parameters with the given names. e.g.
33
- *
34
- * const parseFn = makeParseArgsFn(
35
- * ["arg0", "arg1"] as const,
36
- * ["opt0"] as const)
37
- *
38
- * creates
39
- *
40
- * parseFn: (args: unknown) => {
41
- * arg0: string,
42
- * arg1: string,
43
- * opt0: string|undefined
44
- * }
45
- *
46
- * which can be used to parse tool arguments.
47
- *
48
- * NOTE, the complex type parameters ensures that the name list is a
49
- * compile-time value, which in turn ensures that the return value of this
50
- * function is well-typed.
51
- */
52
- export function makeParseArgsFn<
53
- T extends readonly string[] & (string extends T[number] ? never : unknown),
54
- U extends readonly string[] & (string extends U[number] ? never : unknown),
55
- >(
56
- names: T,
57
- optNames?: U
58
- ): (
59
- args: unknown
60
- ) => { [K in T[number]]: string } & { [K in U[number]]: string | undefined } {
61
- return (args: unknown) => {
62
- if (!args || typeof args !== "object") {
63
- throw new Error(`invalid args: ${typeof args}`);
64
- }
65
- const argsObj = args as Record<string, string | undefined>;
66
- for (const name of names) {
67
- const val = argsObj[name];
68
- if (typeof val !== "string") {
69
- throw new Error(`invalid expr args.${name}: ${typeof val}`);
70
- }
71
- }
72
- if (optNames) {
73
- for (const name of optNames) {
74
- const val = argsObj[name];
75
- if (typeof val !== "undefined" && typeof val !== "string") {
76
- throw new Error(`invalid expr args.${name}: ${typeof val}`);
77
- }
78
- }
79
- }
80
-
81
- return argsObj as { [K in T[number]]: string } & {
82
- [K in U[number]]: string | undefined;
83
- };
84
- };
85
- }
86
-
87
- const DATETIME_DESC: ToolDescriptor = {
88
- type: "function",
89
- function: {
90
- name: "time_now",
91
- description: "Current time",
92
- },
93
- };
94
-
95
- export function isoWithTimezone(timeZone: string): string {
96
- return (
97
- new Intl.DateTimeFormat("sv-SE", {
98
- timeZone,
99
- year: "numeric",
100
- month: "2-digit",
101
- day: "2-digit",
102
- hour: "2-digit",
103
- minute: "2-digit",
104
- second: "2-digit",
105
- hour12: false,
106
- timeZoneName: "short",
107
- })
108
- .format(new Date())
109
- .replace(" ", "T") + ` (${timeZone})`
110
- );
111
- }
112
-
113
- export function datetimeTool(timezone: string): IAgentToolProvider {
114
- // eslint-disable-next-line @typescript-eslint/require-await
115
- const toolFn = async () => {
116
- return { response: isoWithTimezone(timezone) };
117
- };
118
- return {
119
- // eslint-disable-next-line @typescript-eslint/require-await
120
- setup: async (agent: AgentEx) => {
121
- agent.addAgentTool(DATETIME_DESC, toolFn);
122
- },
123
- };
124
- }
125
-
126
- const ARITHMETIC_DESC: ToolDescriptor = {
127
- type: "function",
128
- function: {
129
- name: "arithmetic",
130
- description: "Evaluate arithmetic expression",
131
- parameters: {
132
- type: "object",
133
- properties: {
134
- expr: {
135
- type: "string",
136
- description: "Expression containing +-*/()",
137
- },
138
- },
139
- required: ["expr"],
140
- },
141
- },
142
- };
143
-
144
- export function calculatorEval(args: string): string {
145
- try {
146
- return String(Parser.evaluate(args));
147
- } catch (e) {
148
- if (typeof (e as { message: string }).message === "string") {
149
- return (e as { message: string }).message;
150
- }
151
- return String(e);
152
- }
153
- }
154
-
155
- export const calculatorTool: IAgentToolProvider = {
156
- // eslint-disable-next-line @typescript-eslint/require-await
157
- setup: async (agent: AgentEx) => {
158
- const getExpr = makeParseArgsFn(["expr"] as const);
159
- const toolFn = async (
160
- _: AgentEx,
161
- args: unknown
162
- ): Promise<ToolCallResult> => {
163
- const { expr } = getExpr(args);
164
- return Promise.resolve({ response: calculatorEval(expr) });
165
- };
166
-
167
- agent.addAgentTool(ARITHMETIC_DESC, toolFn);
168
- },
169
- };
170
-
171
- const RENDER_DESC: ToolDescriptor = {
172
- type: "function",
173
- function: {
174
- name: "render",
175
- description: [
176
- "Display HTML using only safe elements",
177
- "Never include <script>, <iframe>, <object>, <embed>, <form>, <meta>,",
178
- "<link>, <style>, event handlers (onclick, etc.), or javascript: URLs.",
179
- "If the tool result is [HTML_SANITIZATION_WARNING], do not mention it.",
180
- "Retry with only safe elements. If warning repeats, tell the user you",
181
- "cannot render unsafe HTML."
182
- ].join(" "),
183
- parameters: {
184
- type: "object",
185
- properties: {
186
- name: { type: "string", description: "Filename for the HTML" },
187
- summary: { type: "string", description: "One line summary" },
188
- html: { type: "string", description: "HTML fragment to render" },
189
- },
190
- required: ["name", "summary", "html"],
191
- },
192
- },
193
- };
194
-
195
- function validateHtmlSafety(html: string): string | undefined {
196
- const issues: string[] = [];
197
- if (/<script[\s>]/i.test(html)) issues.push("<script> tag");
198
- if (/<iframe[\s>]/i.test(html)) issues.push("<iframe> tag");
199
- if (/<object[\s>]/i.test(html)) issues.push("<object> tag");
200
- if (/<embed[\s>]/i.test(html)) issues.push("<embed> tag");
201
- if (/\bon\w+\s*=/.test(html)) issues.push("event handler (e.g., onclick)");
202
- if (/javascript:/i.test(html)) issues.push("javascript: URL");
203
- if (/<meta[\s>]/i.test(html)) issues.push("<meta> tag");
204
- if (/<form[\s>]/i.test(html)) issues.push("<form> tag");
205
- if (/<style[\s>]/i.test(html)) issues.push("<style> tag");
206
- if (/<link[\s>]/i.test(html)) issues.push("<link> tag");
207
-
208
- if (issues.length > 0) {
209
- return `Unsafe HTML: ${issues.join(", ")}`;
210
- }
211
- return undefined;
212
- }
213
-
214
- export function renderTool(
215
- fileManager: ChatSessionFileManager
216
- ): IAgentToolProvider {
217
- const getNameSummeryHtml = makeParseArgsFn([
218
- "name",
219
- "summary",
220
- "html",
221
- ] as const);
222
- const toolFn = async (
223
- _: AgentEx,
224
- args: unknown
225
- ): Promise<ToolCallResultWithFileRef> => {
226
- const { name, summary, html } = getNameSummeryHtml(args);
227
- const safetyError = validateHtmlSafety(html);
228
- if (safetyError) {
229
- return {
230
- response: "[HTML_SANITIZATION_WARNING]",
231
- overwriteResponse: "",
232
- structuredContent: {
233
- kind: "htmlSanitizationWarning",
234
- message: safetyError,
235
- },
236
- };
237
- }
238
- const mimeType = "text/html";
239
- const dataURL = `data:${mimeType},${html}`;
240
- await fileManager.putFileContent(name, summary, dataURL);
241
- const uri = fileManager.getSessionFileRelativeUrl(name);
242
- return {
243
- response: "",
244
- _meta: { "xalia/fileUri": uri, "xalia/fileMimeType": mimeType },
245
- };
246
- };
247
-
248
- return {
249
- // eslint-disable-next-line @typescript-eslint/require-await
250
- setup: async (agent: AgentEx) => {
251
- agent.addAgentTool(RENDER_DESC, toolFn);
252
- },
253
- };
254
- }
255
-
256
- const WEB_SEARCH_DESC: ToolDescriptor = {
257
- type: "function",
258
- function: {
259
- name: "web_search",
260
- description: "Web search",
261
- parameters: {
262
- type: "object",
263
- properties: {
264
- query: {
265
- type: "string",
266
- description: "Search query text",
267
- },
268
- },
269
- required: ["query"],
270
- },
271
- },
272
- };
273
-
274
- export function webSearchTool(): IAgentToolProvider {
275
- const getQuery = makeParseArgsFn(["query"] as const);
276
- const toolFn = async (_: AgentEx, args: unknown): Promise<ToolCallResult> => {
277
- const { query } = getQuery(args);
278
- logger.debug(`[web_search]: query: ${query}`);
279
- const results = await webSearch(query);
280
- logger.debug(`[web_search]: results: ${results}`);
281
- return { response: JSON.stringify(results) };
282
- };
283
-
284
- return {
285
- // eslint-disable-next-line @typescript-eslint/require-await
286
- setup: async (agent: AgentEx) => {
287
- agent.addAgentTool(WEB_SEARCH_DESC, toolFn);
288
- },
289
- };
290
- }
291
-
292
- // open_url
293
-
294
- /**
295
- * For now, this matches the duckduckgo-mcp-server length. Could extend
296
- * depending on the application / model etc.
297
- */
298
- const _OPEN_URL_MAX_LENGTH_STR = process.env["OPEN_URL_MAX_LENGTH"] || "8000";
299
- const OPEN_URL_MAX_LENGTH: number = parseInt(_OPEN_URL_MAX_LENGTH_STR, 10);
300
-
301
- const OPEN_URL_DESC: ToolDescriptor = {
302
- type: "function",
303
- function: {
304
- name: "open_url",
305
- description: "Download content from a URL",
306
- parameters: {
307
- type: "object",
308
- properties: {
309
- url: {
310
- type: "string",
311
- description: "URL to download",
312
- },
313
- },
314
- required: ["url"],
315
- },
316
- },
317
- };
318
-
319
- export async function openURL(url: string): Promise<string> {
320
- const response = await fetch(url);
321
- if (!response.ok) {
322
- const status = String(response.status);
323
- const code = response.statusText;
324
- throw new Error(`Failed to fetch ${url}: ${status} ${code}`);
325
- }
326
- const html = await response.text();
327
- return htmlToText(html, OPEN_URL_MAX_LENGTH);
328
- }
329
-
330
- export function openURLTool(): IAgentToolProvider {
331
- const getURL = makeParseArgsFn(["url"] as const);
332
- const toolFn = async (_: AgentEx, args: unknown): Promise<ToolCallResult> => {
333
- const { url } = getURL(args);
334
- return { response: await openURL(url) };
335
- };
336
-
337
- return {
338
- // eslint-disable-next-line @typescript-eslint/require-await
339
- setup: async (agent: AgentEx) => {
340
- agent.addAgentTool(OPEN_URL_DESC, toolFn);
341
- },
342
- };
343
- }
344
-
345
- const TEST_DESC: ToolDescriptor = {
346
- type: "function",
347
- function: {
348
- name: "test_tool",
349
- description: "Call me when asked to test tools",
350
- parameters: {
351
- type: "object",
352
- properties: {},
353
- required: [],
354
- },
355
- },
356
- };
357
-
358
- export const testTool: IAgentToolProvider = {
359
- setup: (agent: AgentEx) => {
360
- const toolFn = (
361
- _agent: AgentEx,
362
- _args: unknown
363
- ): Promise<ToolCallResult> => {
364
- // Return an object with structuredContent and _meta
365
- return Promise.resolve({
366
- response: "Some text",
367
- structuredContent: { description: "structuredContent example" },
368
- _meta: { description: "_meta example" },
369
- });
370
- };
371
- agent.addAgentTool(TEST_DESC, toolFn);
372
- return Promise.resolve();
373
- },
374
- };
375
-
376
- /**
377
- * Add a set of agent tools for chat sessions.
378
- */
379
- export async function addDefaultChatTools(
380
- agent: AgentEx | Agent,
381
- timezone: string,
382
- platform: IPlatform,
383
- fileManager: ChatSessionFileManager
384
- ): Promise<void> {
385
- await agent.addAgentToolProvider(datetimeTool(timezone));
386
- await agent.addAgentToolProvider(calculatorTool);
387
- await agent.addAgentToolProvider(renderTool(fileManager));
388
- await agent.addAgentToolProvider(webSearchTool());
389
- await agent.addAgentToolProvider(openURLTool());
390
- await agent.addAgentToolProvider(fileManagerTool(fileManager));
391
- await agent.addAgentToolProvider(
392
- await genImageFileTool(platform, fileManager)
393
- );
394
- if (DEVELOPMENT) {
395
- await agent.addAgentToolProvider(testTool);
396
- }
397
- }
@@ -1,76 +0,0 @@
1
- import { SavedAgentProfile, getLogger } from "@xalia/xmcp/sdk";
2
- import { AgentSessionData, SessionDescriptor } from "../data/dataModels";
3
-
4
- const logger = getLogger();
5
-
6
- export function emptyAgentProfile(agentUuid: string): SavedAgentProfile {
7
- return {
8
- uuid: agentUuid,
9
- profile: {
10
- model: undefined,
11
- system_prompt: "",
12
- mcp_settings: {},
13
- },
14
- user_uuid: "",
15
- profile_name: `Deleted Agent (${agentUuid.slice(0, 8)}...)`,
16
- updated_at: "",
17
- preferences: {
18
- auto_approve: {},
19
- },
20
- template_name: undefined,
21
- };
22
- }
23
-
24
- // build agentSessionMap from sessions and agents
25
- export function buildAgentSessionMap(
26
- sessions: Map<string, SessionDescriptor>,
27
- agents: Map<string, SavedAgentProfile>
28
- ): Map<string, AgentSessionData> {
29
- const agentSessionMap: Map<string, AgentSessionData> = new Map();
30
-
31
- logger.debug(`[buildAgentSessionMap] sessions: ${JSON.stringify(sessions)}`);
32
- logger.debug(`[buildAgentSessionMap] agents: ${JSON.stringify(agents)}`);
33
-
34
- // First, add all agents to the map (even those without sessions)
35
- for (const agent of agents.values()) {
36
- agentSessionMap.set(agent.uuid, {
37
- agent_profile: agent,
38
- sessions: [],
39
- updated_at: 0,
40
- });
41
- }
42
-
43
- // Then, add sessions to their corresponding agents
44
- for (const session of sessions.values()) {
45
- const agentUuid = session.agent_profile_uuid;
46
- const updatedAt = session.updated_at
47
- ? new Date(session.updated_at).getTime()
48
- : 0;
49
-
50
- let agentSessions = agentSessionMap.get(agentUuid);
51
-
52
- if (!agentSessions) {
53
- // This is an orphaned session (agent was deleted). Should not happen
54
- // due to DB constraints.
55
- logger.warn(
56
- `session ${session.session_uuid} referes to unknown agent ${agentUuid}`
57
- );
58
- const agentProfile = emptyAgentProfile(agentUuid);
59
- agentSessions = {
60
- agent_profile: agentProfile,
61
- sessions: [session],
62
- updated_at: updatedAt,
63
- };
64
- agentSessionMap.set(agentUuid, agentSessions);
65
- } else {
66
- // Add session to existing agent
67
- agentSessions.sessions.push(session);
68
- // update updatedAt
69
- if (updatedAt > agentSessions.updated_at) {
70
- agentSessions.updated_at = updatedAt;
71
- }
72
- }
73
- }
74
-
75
- return agentSessionMap;
76
- }
@@ -1,183 +0,0 @@
1
- import {
2
- AgentPreferences,
3
- getLogger,
4
- prefsGetAutoApprove,
5
- prefsSetAutoApprove,
6
- } from "@xalia/xmcp/sdk";
7
- import { ResponseAwaiter } from "./responseAwaiter";
8
- import {
9
- ClientToolCallApprovalResult,
10
- ServerToClient,
11
- ServerToolAutoApprovalSet,
12
- } from "../protocol/messages";
13
- import { Database } from "../data/database";
14
- import { MessageToolCall } from "../../agent/llm";
15
- import { ISessionMessageSender } from "../server/openSessionMessageSender";
16
-
17
- const logger = getLogger();
18
-
19
- export interface IAgentPreferencesWriter {
20
- updatePreferences(
21
- agentProfileUUID: string,
22
- settings: AgentPreferences
23
- ): Promise<void>;
24
- }
25
-
26
- export class DbAgentPreferencesWriter implements IAgentPreferencesWriter {
27
- constructor(private db: Database) {}
28
-
29
- updatePreferences(
30
- agentProfileUUID: string,
31
- preferences: AgentPreferences
32
- ): Promise<void> {
33
- return this.db.updateAgentProfilePreferences(agentProfileUUID, preferences);
34
- }
35
- }
36
-
37
- /**
38
- * Handles an in-memory caching / updating of the auto-approve settings for
39
- * tool calls. Also handles querying the client for approval and waiting for
40
- * responses.
41
- */
42
- export class ToolApprovalManager {
43
- private sessionUUID: string;
44
- private agentProfileUUID: string;
45
- private agentProfilePreferences: AgentPreferences;
46
- private sender: ISessionMessageSender<ServerToClient>;
47
- private writer: IAgentPreferencesWriter;
48
- private responseAwaiter: ResponseAwaiter<ClientToolCallApprovalResult>;
49
-
50
- constructor(
51
- sessionUUID: string,
52
- agentProfileUUID: string,
53
- agentProfilePreferences: AgentPreferences,
54
- sender: ISessionMessageSender<ServerToClient>,
55
- writer: IAgentPreferencesWriter,
56
- timeoutMs?: number
57
- ) {
58
- this.sessionUUID = sessionUUID;
59
- this.agentProfileUUID = agentProfileUUID;
60
- this.agentProfilePreferences = agentProfilePreferences;
61
- this.sender = sender;
62
- this.writer = writer;
63
- this.responseAwaiter = ResponseAwaiter.init(
64
- undefined,
65
- (msg) => msg.id,
66
- timeoutMs
67
- );
68
- }
69
-
70
- /**
71
- * Check for auto-approval, or query the client. Handle approval response
72
- * (or timeout) and update auto-approval settings.
73
- *
74
- * The returned `requested` value indicates whether approval was requested.
75
- */
76
- public async getApproval(
77
- serverName: string,
78
- tool: string,
79
- toolCall: MessageToolCall
80
- ): Promise<{ approved: boolean; requested: boolean }> {
81
- const autoApproved = prefsGetAutoApprove(
82
- this.agentProfilePreferences,
83
- serverName,
84
- tool
85
- );
86
- if (autoApproved) {
87
- return { approved: true, requested: false };
88
- }
89
-
90
- // Query the owner for approval
91
-
92
- const id = this.generateUniqueId(toolCall.function.name);
93
- try {
94
- const approvalP = this.responseAwaiter.waitForResponse(id);
95
-
96
- this.sender.broadcast({
97
- type: "approve_tool_call",
98
- id,
99
- tool_call: toolCall,
100
- session_id: this.sessionUUID,
101
- });
102
-
103
- logger.debug(`[ApprovalManager.getApproval] awaiting approval ${id}`);
104
- const approval = await approvalP;
105
- logger.debug(
106
- `[ApprovalManager.getApproval] approval ${JSON.stringify(approval)}`
107
- );
108
-
109
- // Handle any auto-approve update, informing other clients.
110
-
111
- if (approval.auto_approve) {
112
- logger.debug("[ApprovalManager.getApproval] updated preferences");
113
- const autoApprovalMsg = await this.setAutoApprove(
114
- serverName,
115
- tool,
116
- true
117
- );
118
- if (autoApprovalMsg) {
119
- this.sender.broadcast(autoApprovalMsg);
120
- }
121
- }
122
-
123
- // Broadcast the result of the approval
124
-
125
- this.sender.broadcast({
126
- type: "tool_call_approval_result",
127
- id: approval.id,
128
- result: approval.result,
129
- session_id: this.sessionUUID,
130
- });
131
-
132
- return { approved: approval.result, requested: true };
133
- } catch (e) {
134
- logger.debug(
135
- `[OpenSession.onToolCall] error waiting for approval ${id}: ` +
136
- String(e)
137
- );
138
- throw e;
139
- }
140
- }
141
-
142
- /**
143
- * Handle a request to set auto-approval for a given tool. If there was a
144
- * change, return the message to be broadcast.
145
- */
146
- public async setAutoApprove(
147
- serverName: string,
148
- tool: string,
149
- autoApprove: boolean
150
- ): Promise<ServerToolAutoApprovalSet | undefined> {
151
- if (
152
- prefsSetAutoApprove(
153
- this.agentProfilePreferences,
154
- serverName,
155
- tool,
156
- autoApprove
157
- )
158
- ) {
159
- await this.writer.updatePreferences(
160
- this.agentProfileUUID,
161
- this.agentProfilePreferences
162
- );
163
- return {
164
- type: "tool_auto_approval_set",
165
- server_name: serverName,
166
- tool,
167
- auto_approve: autoApprove,
168
- session_id: this.sessionUUID,
169
- };
170
- }
171
- }
172
-
173
- /**
174
- * Forward all approval result messages here.
175
- */
176
- public onApprovalResult(msg: ClientToolCallApprovalResult): void {
177
- this.responseAwaiter.onMessage(msg);
178
- }
179
-
180
- private generateUniqueId(tag: string): string {
181
- return `approval-${tag}-` + Math.random().toString(36).substring(2, 11);
182
- }
183
- }
@@ -1,43 +0,0 @@
1
- import { strict as assert } from "assert";
2
-
3
- /**
4
- * Support awaiting exclusive access to a resource.
5
- */
6
- export class AsyncLock {
7
- private running: boolean = false;
8
- private callbacks: (() => Promise<void>)[] = [];
9
-
10
- lockAndProcess<T>(cb: () => Promise<T>): Promise<T> {
11
- return new Promise<T>((r, e) => {
12
- this.callbacks.push(async () => {
13
- try {
14
- const result = await cb();
15
- r(result);
16
- } catch (err) {
17
- e(err instanceof Error ? err : new Error(String(err)));
18
- }
19
- });
20
-
21
- if (!this.running) {
22
- void this.run();
23
- }
24
- });
25
- }
26
-
27
- private async run() {
28
- assert(!this.running);
29
- this.running = true;
30
-
31
- while (this.callbacks.length > 0) {
32
- const cb = this.callbacks.shift();
33
- assert(cb);
34
- try {
35
- await cb();
36
- } catch (e) {
37
- assert(false, `errors should not propagate: ${String(e)}`);
38
- }
39
- }
40
-
41
- this.running = false;
42
- }
43
- }