@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.
- package/README.md +11 -0
- package/dist/agent/src/agent/agent.js +77 -18
- package/dist/agent/src/agent/agentUtils.js +3 -2
- package/dist/agent/src/agent/documentSummarizer.js +126 -0
- package/dist/agent/src/agent/dummyLLM.js +25 -22
- package/dist/agent/src/agent/imageGenLLM.js +22 -19
- package/dist/agent/src/agent/llm.js +1 -1
- package/dist/agent/src/agent/openAILLM.js +15 -12
- package/dist/agent/src/agent/openAILLMStreaming.js +68 -37
- package/dist/agent/src/agent/repeatLLM.js +16 -7
- package/dist/agent/src/agent/tokenCounter.js +390 -0
- package/dist/agent/src/agent/tokenCounter.test.js +206 -0
- package/dist/agent/src/agent/toolSettings.js +17 -0
- package/dist/agent/src/agent/tools/calculatorTool.js +45 -0
- package/dist/agent/src/agent/tools/contentExtractors/pdfToText.js +55 -0
- package/dist/agent/src/agent/tools/datetimeTool.js +38 -0
- package/dist/agent/src/agent/tools/fileManager/fileManagerTool.js +156 -0
- package/dist/agent/src/agent/tools/fileManager/index.js +31 -0
- package/dist/agent/src/agent/tools/fileManager/memoryFileManager.js +102 -0
- package/dist/agent/src/{chat/data → agent/tools/fileManager}/mimeTypes.js +3 -1
- package/dist/agent/src/agent/tools/fileManager/prompt.js +33 -0
- package/dist/agent/src/{chat/data/dbSessionFileModels.js → agent/tools/fileManager/types.js} +7 -0
- package/dist/agent/src/agent/tools/index.js +64 -0
- package/dist/agent/src/agent/tools/openUrlTool.js +57 -0
- package/dist/agent/src/agent/tools/renderTool.js +89 -0
- package/dist/agent/src/agent/tools/utils.js +61 -0
- package/dist/agent/src/{chat/utils/search.js → agent/tools/webSearch.js} +1 -2
- package/dist/agent/src/agent/tools/webSearchTool.js +40 -0
- package/dist/agent/src/chat/client/chatClient.js +28 -0
- package/dist/agent/src/chat/client/index.js +4 -1
- package/dist/agent/src/chat/client/sessionClient.js +28 -2
- package/dist/agent/src/chat/constants.js +8 -0
- package/dist/agent/src/chat/data/dbSessionFiles.js +11 -6
- package/dist/agent/src/chat/protocol/messages.js +5 -0
- package/dist/agent/src/chat/server/chatContextManager.js +45 -25
- package/dist/agent/src/chat/server/conversation.js +3 -0
- package/dist/agent/src/chat/server/imageGeneratorTools.js +20 -8
- package/dist/agent/src/chat/server/openAIRouterLLM.js +0 -3
- package/dist/agent/src/chat/server/openSession.js +218 -55
- package/dist/agent/src/chat/server/promptRefiner.js +86 -0
- package/dist/agent/src/chat/server/server.js +5 -1
- package/dist/agent/src/chat/server/sessionFileManager.js +22 -221
- package/dist/agent/src/chat/server/sessionRegistry.js +87 -0
- package/dist/agent/src/chat/server/titleGenerator.js +112 -0
- package/dist/agent/src/chat/server/titleGenerator.test.js +113 -0
- package/dist/agent/src/chat/server/tools.js +63 -287
- package/dist/agent/src/chat/utils/approvalManager.js +6 -3
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +3 -0
- package/dist/agent/src/test/agent.test.js +16 -17
- package/dist/agent/src/test/chatContextManager.test.js +15 -3
- package/dist/agent/src/test/dbMcpServerConfigs.test.js +4 -4
- package/dist/agent/src/test/dbSessionFiles.test.js +17 -17
- package/dist/agent/src/test/testTools.js +6 -1
- package/dist/agent/src/test/tools.test.js +27 -9
- package/dist/agent/src/tool/agentChat.js +5 -2
- package/dist/agent/src/tool/chatMain.js +34 -7
- package/dist/agent/src/tool/commandPrompt.js +2 -2
- package/dist/agent/src/tool/files.js +7 -8
- package/package.json +8 -2
- package/.env.development +0 -1
- package/.prettierrc.json +0 -11
- package/dist/agent/src/agent/tools.js +0 -44
- package/eslint.config.mjs +0 -38
- package/scripts/chat_server +0 -8
- package/scripts/git_message +0 -31
- package/scripts/git_wip +0 -21
- package/scripts/pr_message +0 -18
- package/scripts/pr_review +0 -16
- package/scripts/setup_chat +0 -90
- package/scripts/shutdown_chat_server +0 -42
- package/scripts/start_chat_server +0 -24
- package/scripts/sudomcp_import +0 -23
- package/scripts/test_chat +0 -308
- package/src/agent/agent.ts +0 -624
- package/src/agent/agentUtils.ts +0 -285
- package/src/agent/compressingContextManager.ts +0 -129
- package/src/agent/context.ts +0 -265
- package/src/agent/contextWithWorkspace.ts +0 -162
- package/src/agent/dummyLLM.ts +0 -126
- package/src/agent/iAgentEventHandler.ts +0 -64
- package/src/agent/imageGenLLM.ts +0 -97
- package/src/agent/imageGenerator.ts +0 -45
- package/src/agent/iplatform.ts +0 -18
- package/src/agent/llm.ts +0 -74
- package/src/agent/mcpServerManager.ts +0 -541
- package/src/agent/nullAgentEventHandler.ts +0 -26
- package/src/agent/nullPlatform.ts +0 -13
- package/src/agent/openAI.ts +0 -123
- package/src/agent/openAILLM.ts +0 -95
- package/src/agent/openAILLMStreaming.ts +0 -609
- package/src/agent/promptProvider.ts +0 -87
- package/src/agent/repeatLLM.ts +0 -50
- package/src/agent/sudoMcpServerManager.ts +0 -361
- package/src/agent/tokenAuth.ts +0 -50
- package/src/agent/tools.ts +0 -57
- package/src/chat/client/chatClient.ts +0 -922
- package/src/chat/client/connection.test.ts +0 -241
- package/src/chat/client/connection.ts +0 -286
- package/src/chat/client/constants.ts +0 -1
- package/src/chat/client/index.ts +0 -18
- package/src/chat/client/interfaces.ts +0 -34
- package/src/chat/client/sessionClient.ts +0 -537
- package/src/chat/client/sessionFiles.ts +0 -142
- package/src/chat/client/teamManager.ts +0 -29
- package/src/chat/data/apiKeyManager.ts +0 -76
- package/src/chat/data/dataModels.ts +0 -101
- package/src/chat/data/database.ts +0 -997
- package/src/chat/data/dbMcpServerConfigs.ts +0 -59
- package/src/chat/data/dbSessionFileModels.ts +0 -113
- package/src/chat/data/dbSessionFiles.ts +0 -99
- package/src/chat/data/dbSessionMessages.ts +0 -102
- package/src/chat/data/mimeTypes.ts +0 -58
- package/src/chat/protocol/connectionMessages.ts +0 -49
- package/src/chat/protocol/constants.ts +0 -55
- package/src/chat/protocol/errors.ts +0 -16
- package/src/chat/protocol/messages.ts +0 -846
- package/src/chat/server/README.md +0 -127
- package/src/chat/server/chatContextManager.ts +0 -639
- package/src/chat/server/connectionManager.test.ts +0 -246
- package/src/chat/server/connectionManager.ts +0 -506
- package/src/chat/server/conversation.ts +0 -316
- package/src/chat/server/errorUtils.ts +0 -28
- package/src/chat/server/imageGeneratorTools.ts +0 -160
- package/src/chat/server/openAIRouterLLM.ts +0 -171
- package/src/chat/server/openSession.ts +0 -1689
- package/src/chat/server/openSessionMessageSender.ts +0 -4
- package/src/chat/server/server.ts +0 -175
- package/src/chat/server/sessionFileManager.ts +0 -422
- package/src/chat/server/sessionRegistry.test.ts +0 -137
- package/src/chat/server/sessionRegistry.ts +0 -1425
- package/src/chat/server/test-utils/mockFactories.ts +0 -422
- package/src/chat/server/tools.ts +0 -397
- package/src/chat/utils/agentSessionMap.ts +0 -76
- package/src/chat/utils/approvalManager.ts +0 -183
- package/src/chat/utils/asyncLock.ts +0 -43
- package/src/chat/utils/asyncQueue.ts +0 -62
- package/src/chat/utils/htmlToText.ts +0 -61
- package/src/chat/utils/multiAsyncQueue.ts +0 -62
- package/src/chat/utils/responseAwaiter.ts +0 -181
- package/src/chat/utils/search.ts +0 -139
- package/src/chat/utils/userResolver.ts +0 -48
- package/src/chat/utils/websocket.ts +0 -16
- package/src/index.ts +0 -0
- package/src/test/agent.test.ts +0 -590
- package/src/test/approvalManager.test.ts +0 -141
- package/src/test/chatContextManager.test.ts +0 -527
- package/src/test/clientServerConnection.test.ts +0 -205
- package/src/test/compressingContextManager.test.ts +0 -77
- package/src/test/context.test.ts +0 -150
- package/src/test/contextTestTools.ts +0 -95
- package/src/test/conversation.test.ts +0 -109
- package/src/test/db.test.ts +0 -363
- package/src/test/dbMcpServerConfigs.test.ts +0 -112
- package/src/test/dbSessionFiles.test.ts +0 -258
- package/src/test/dbSessionMessages.test.ts +0 -85
- package/src/test/dbTestTools.ts +0 -157
- package/src/test/imageLoad.test.ts +0 -15
- package/src/test/mcpServerManager.test.ts +0 -114
- package/src/test/multiAsyncQueue.test.ts +0 -183
- package/src/test/openaiStreaming.test.ts +0 -177
- package/src/test/prompt.test.ts +0 -27
- package/src/test/promptProvider.test.ts +0 -33
- package/src/test/responseAwaiter.test.ts +0 -103
- package/src/test/sudoMcpServerManager.test.ts +0 -63
- package/src/test/testTools.ts +0 -171
- package/src/test/tools.test.ts +0 -39
- package/src/tool/agentChat.ts +0 -194
- package/src/tool/agentMain.ts +0 -180
- package/src/tool/chatMain.ts +0 -594
- package/src/tool/commandPrompt.ts +0 -264
- package/src/tool/files.ts +0 -84
- package/src/tool/main.ts +0 -25
- package/src/tool/nodePlatform.ts +0 -73
- package/src/tool/options.ts +0 -144
- package/src/tool/prompt.ts +0 -101
- package/test_data/background_test_profile.json +0 -6
- package/test_data/background_test_script.json +0 -11
- package/test_data/dummyllm_script_crash.json +0 -32
- package/test_data/dummyllm_script_image_gen.json +0 -19
- package/test_data/dummyllm_script_image_gen_fe.json +0 -29
- package/test_data/dummyllm_script_invoke_image_gen_tool.json +0 -37
- package/test_data/dummyllm_script_render_tool.json +0 -29
- package/test_data/dummyllm_script_simplecalc.json +0 -28
- package/test_data/dummyllm_script_test_auto_approve.json +0 -81
- package/test_data/dummyllm_script_test_simplecalc_addition.json +0 -29
- package/test_data/frog.png +0 -0
- package/test_data/frog.png.b64 +0 -1
- package/test_data/git_message_profile.json +0 -4
- package/test_data/git_wip_system.txt +0 -5
- package/test_data/image_gen_test_profile.json +0 -5
- package/test_data/pr_message_profile.json +0 -4
- package/test_data/pr_review_profile.json +0 -4
- package/test_data/prompt_simplecalc.txt +0 -1
- package/test_data/simplecalc_profile.json +0 -4
- package/test_data/sudomcp_import_profile.json +0 -4
- package/test_data/test_script_profile.json +0 -8
- package/tsconfig.json +0 -13
- package/vitest.config.ts +0 -39
- /package/dist/agent/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.js +0 -0
package/src/chat/server/tools.ts
DELETED
|
@@ -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
|
-
}
|