@xalia/agent 0.6.10 → 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 (161) hide show
  1. package/package.json +5 -2
  2. package/.env.development +0 -6
  3. package/.env.test +0 -7
  4. package/.prettierrc.json +0 -11
  5. package/context_system.md +0 -498
  6. package/eslint.config.mjs +0 -38
  7. package/scripts/chat_server +0 -8
  8. package/scripts/git_message +0 -31
  9. package/scripts/git_wip +0 -21
  10. package/scripts/pr_message +0 -18
  11. package/scripts/pr_review +0 -16
  12. package/scripts/setup_chat +0 -90
  13. package/scripts/shutdown_chat_server +0 -42
  14. package/scripts/start_chat_server +0 -24
  15. package/scripts/sudomcp_import +0 -23
  16. package/scripts/test_chat +0 -327
  17. package/src/agent/agent.ts +0 -699
  18. package/src/agent/agentUtils.ts +0 -286
  19. package/src/agent/compressingContextManager.ts +0 -129
  20. package/src/agent/context.ts +0 -265
  21. package/src/agent/contextWithWorkspace.ts +0 -162
  22. package/src/agent/documentSummarizer.ts +0 -157
  23. package/src/agent/dummyLLM.ts +0 -130
  24. package/src/agent/iAgentEventHandler.ts +0 -64
  25. package/src/agent/imageGenLLM.ts +0 -101
  26. package/src/agent/imageGenerator.ts +0 -45
  27. package/src/agent/iplatform.ts +0 -18
  28. package/src/agent/llm.ts +0 -74
  29. package/src/agent/mcpServerManager.ts +0 -541
  30. package/src/agent/nullAgentEventHandler.ts +0 -26
  31. package/src/agent/nullPlatform.ts +0 -13
  32. package/src/agent/openAI.ts +0 -123
  33. package/src/agent/openAILLM.ts +0 -99
  34. package/src/agent/openAILLMStreaming.ts +0 -648
  35. package/src/agent/promptProvider.ts +0 -87
  36. package/src/agent/repeatLLM.ts +0 -62
  37. package/src/agent/sudoMcpServerManager.ts +0 -361
  38. package/src/agent/test_data/harrypotter.txt +0 -6065
  39. package/src/agent/tokenAuth.ts +0 -50
  40. package/src/agent/tokenCounter.test.ts +0 -243
  41. package/src/agent/tokenCounter.ts +0 -483
  42. package/src/agent/toolSettings.ts +0 -24
  43. package/src/agent/tools/calculatorTool.ts +0 -50
  44. package/src/agent/tools/contentExtractors/htmlToText.ts +0 -61
  45. package/src/agent/tools/contentExtractors/pdfToText.ts +0 -60
  46. package/src/agent/tools/datetimeTool.ts +0 -41
  47. package/src/agent/tools/fileManager/fileManagerTool.ts +0 -199
  48. package/src/agent/tools/fileManager/index.ts +0 -50
  49. package/src/agent/tools/fileManager/memoryFileManager.ts +0 -120
  50. package/src/agent/tools/fileManager/mimeTypes.ts +0 -60
  51. package/src/agent/tools/fileManager/prompt.ts +0 -38
  52. package/src/agent/tools/fileManager/types.ts +0 -189
  53. package/src/agent/tools/index.ts +0 -49
  54. package/src/agent/tools/openUrlTool.ts +0 -62
  55. package/src/agent/tools/renderTool.ts +0 -92
  56. package/src/agent/tools/utils.ts +0 -74
  57. package/src/agent/tools/webSearch.ts +0 -138
  58. package/src/agent/tools/webSearchTool.ts +0 -44
  59. package/src/chat/client/chatClient.ts +0 -967
  60. package/src/chat/client/connection.test.ts +0 -241
  61. package/src/chat/client/connection.ts +0 -286
  62. package/src/chat/client/constants.ts +0 -1
  63. package/src/chat/client/index.ts +0 -21
  64. package/src/chat/client/interfaces.ts +0 -34
  65. package/src/chat/client/sessionClient.ts +0 -574
  66. package/src/chat/client/sessionFiles.ts +0 -142
  67. package/src/chat/client/teamManager.ts +0 -29
  68. package/src/chat/constants.ts +0 -6
  69. package/src/chat/data/apiKeyManager.ts +0 -76
  70. package/src/chat/data/dataModels.ts +0 -107
  71. package/src/chat/data/database.ts +0 -997
  72. package/src/chat/data/dbMcpServerConfigs.ts +0 -59
  73. package/src/chat/data/dbSessionFiles.ts +0 -107
  74. package/src/chat/data/dbSessionMessages.ts +0 -102
  75. package/src/chat/protocol/connectionMessages.ts +0 -49
  76. package/src/chat/protocol/constants.ts +0 -55
  77. package/src/chat/protocol/errors.ts +0 -16
  78. package/src/chat/protocol/messages.ts +0 -899
  79. package/src/chat/server/README.md +0 -127
  80. package/src/chat/server/chatContextManager.ts +0 -660
  81. package/src/chat/server/connectionManager.test.ts +0 -246
  82. package/src/chat/server/connectionManager.ts +0 -506
  83. package/src/chat/server/conversation.ts +0 -319
  84. package/src/chat/server/errorUtils.ts +0 -28
  85. package/src/chat/server/imageGeneratorTools.ts +0 -179
  86. package/src/chat/server/openAIRouterLLM.ts +0 -168
  87. package/src/chat/server/openSession.ts +0 -1945
  88. package/src/chat/server/openSessionMessageSender.ts +0 -4
  89. package/src/chat/server/promptRefiner.ts +0 -106
  90. package/src/chat/server/server.ts +0 -178
  91. package/src/chat/server/sessionFileManager.ts +0 -151
  92. package/src/chat/server/sessionRegistry.test.ts +0 -137
  93. package/src/chat/server/sessionRegistry.ts +0 -1553
  94. package/src/chat/server/test-utils/mockFactories.ts +0 -422
  95. package/src/chat/server/titleGenerator.test.ts +0 -103
  96. package/src/chat/server/titleGenerator.ts +0 -143
  97. package/src/chat/server/tools.ts +0 -170
  98. package/src/chat/utils/agentSessionMap.ts +0 -76
  99. package/src/chat/utils/approvalManager.ts +0 -189
  100. package/src/chat/utils/asyncLock.ts +0 -43
  101. package/src/chat/utils/asyncQueue.ts +0 -62
  102. package/src/chat/utils/multiAsyncQueue.ts +0 -66
  103. package/src/chat/utils/responseAwaiter.ts +0 -181
  104. package/src/chat/utils/userResolver.ts +0 -48
  105. package/src/chat/utils/websocket.ts +0 -16
  106. package/src/index.ts +0 -0
  107. package/src/test/agent.test.ts +0 -584
  108. package/src/test/approvalManager.test.ts +0 -141
  109. package/src/test/chatContextManager.test.ts +0 -552
  110. package/src/test/clientServerConnection.test.ts +0 -205
  111. package/src/test/compressingContextManager.test.ts +0 -77
  112. package/src/test/context.test.ts +0 -150
  113. package/src/test/contextTestTools.ts +0 -95
  114. package/src/test/conversation.test.ts +0 -109
  115. package/src/test/db.test.ts +0 -363
  116. package/src/test/dbMcpServerConfigs.test.ts +0 -112
  117. package/src/test/dbSessionFiles.test.ts +0 -258
  118. package/src/test/dbSessionMessages.test.ts +0 -85
  119. package/src/test/dbTestTools.ts +0 -157
  120. package/src/test/imageLoad.test.ts +0 -15
  121. package/src/test/mcpServerManager.test.ts +0 -114
  122. package/src/test/multiAsyncQueue.test.ts +0 -183
  123. package/src/test/openaiStreaming.test.ts +0 -177
  124. package/src/test/prompt.test.ts +0 -27
  125. package/src/test/promptProvider.test.ts +0 -33
  126. package/src/test/responseAwaiter.test.ts +0 -103
  127. package/src/test/sudoMcpServerManager.test.ts +0 -63
  128. package/src/test/testTools.ts +0 -176
  129. package/src/test/tools.test.ts +0 -64
  130. package/src/tool/agentChat.ts +0 -203
  131. package/src/tool/agentMain.ts +0 -180
  132. package/src/tool/chatMain.ts +0 -621
  133. package/src/tool/commandPrompt.ts +0 -264
  134. package/src/tool/files.ts +0 -82
  135. package/src/tool/main.ts +0 -25
  136. package/src/tool/nodePlatform.ts +0 -73
  137. package/src/tool/options.ts +0 -144
  138. package/src/tool/prompt.ts +0 -101
  139. package/test_data/background_test_profile.json +0 -6
  140. package/test_data/background_test_script.json +0 -11
  141. package/test_data/dummyllm_script_crash.json +0 -32
  142. package/test_data/dummyllm_script_image_gen.json +0 -19
  143. package/test_data/dummyllm_script_image_gen_fe.json +0 -29
  144. package/test_data/dummyllm_script_invoke_image_gen_tool.json +0 -37
  145. package/test_data/dummyllm_script_render_tool.json +0 -29
  146. package/test_data/dummyllm_script_simplecalc.json +0 -28
  147. package/test_data/dummyllm_script_test_auto_approve.json +0 -81
  148. package/test_data/dummyllm_script_test_simplecalc_addition.json +0 -29
  149. package/test_data/frog.png +0 -0
  150. package/test_data/frog.png.b64 +0 -1
  151. package/test_data/git_message_profile.json +0 -4
  152. package/test_data/git_wip_system.txt +0 -5
  153. package/test_data/image_gen_test_profile.json +0 -5
  154. package/test_data/pr_message_profile.json +0 -4
  155. package/test_data/pr_review_profile.json +0 -4
  156. package/test_data/prompt_simplecalc.txt +0 -1
  157. package/test_data/simplecalc_profile.json +0 -4
  158. package/test_data/sudomcp_import_profile.json +0 -4
  159. package/test_data/test_script_profile.json +0 -8
  160. package/tsconfig.json +0 -13
  161. package/vitest.config.ts +0 -39
@@ -1,176 +0,0 @@
1
- import {
2
- AssistantMessageParam,
3
- MessageParam,
4
- MessageToolCall,
5
- ToolMessageParam,
6
- ContentPartImage,
7
- ToolDescriptor,
8
- Completion,
9
- } from "../agent/llm";
10
- import { IAgentEventHandler } from "../agent/iAgentEventHandler";
11
- import { IPlatform } from "../agent/iplatform";
12
- import { DummyLLM, DummyLLMScriptEntry } from "../agent/dummyLLM";
13
- import { ILLM } from "../agent/llm";
14
-
15
- export const DUMMY_PLATFORM: IPlatform = {
16
- openUrl: () => {
17
- throw Error("unimpl");
18
- },
19
- load: () => {
20
- throw Error("unimpl");
21
- },
22
- renderHTML: () => {
23
- throw Error("unimpl");
24
- },
25
- };
26
-
27
- /**
28
- * IAgentEventHandler which records all calls, for testing.
29
- */
30
- export class TestAgentEventHandler implements IAgentEventHandler {
31
- private all: MessageParam[] = [];
32
- private completions: AssistantMessageParam[] = [];
33
- private images: ContentPartImage[] = [];
34
- private agentMessages: string[] = [""];
35
- private reasoning: string[] = [""];
36
- private toolCalls: MessageToolCall[] = [];
37
- private agentToolCalls: MessageToolCall[] = [];
38
- private toolCallResults: ToolMessageParam[] = [];
39
-
40
- onCompletion(result: AssistantMessageParam) {
41
- this.completions.push(result);
42
- this.all.push(result);
43
- }
44
-
45
- onImage(image: ContentPartImage) {
46
- this.images.push(image);
47
- }
48
-
49
- onToolCallResult(result: ToolMessageParam): void {
50
- this.toolCallResults.push(result);
51
- this.all.push(result);
52
- }
53
-
54
- onAgentMessage(chunk: string, isEnd: boolean): Promise<void> {
55
- this.agentMessages[this.agentMessages.length - 1] += chunk;
56
- if (isEnd) {
57
- this.agentMessages.push("");
58
- this.reasoning.push("");
59
- }
60
- return new Promise<void>((r) => {
61
- r();
62
- });
63
- }
64
-
65
- onReasoning(reasoning: string): Promise<void> {
66
- this.reasoning[this.reasoning.length - 1] += reasoning;
67
- return new Promise<void>((r) => {
68
- r();
69
- });
70
- }
71
-
72
- onToolCall(
73
- toolCall: MessageToolCall,
74
- isAgentTool: boolean
75
- ): Promise<boolean> {
76
- return new Promise<boolean>((r) => {
77
- this.toolCalls.push(toolCall);
78
- if (isAgentTool) {
79
- this.agentToolCalls.push(toolCall);
80
- }
81
- r(true);
82
- });
83
- }
84
-
85
- getAll(): MessageParam[] {
86
- return this.all;
87
- }
88
-
89
- getCompletions(): AssistantMessageParam[] {
90
- return this.completions;
91
- }
92
-
93
- getAgentMessages(): string[] {
94
- return this.agentMessages.slice(0, this.agentMessages.length - 1);
95
- }
96
-
97
- getToolCalls(): MessageToolCall[] {
98
- return this.toolCalls;
99
- }
100
-
101
- getAgentToolCalls(): MessageToolCall[] {
102
- return this.agentToolCalls;
103
- }
104
-
105
- getToolCallResults(): ToolMessageParam[] {
106
- return this.toolCallResults;
107
- }
108
-
109
- getReasoning(): string[] {
110
- return this.reasoning;
111
- }
112
- }
113
-
114
- export type TestLLMRequest = {
115
- messages: MessageParam[];
116
- tools?: ToolDescriptor[];
117
- };
118
-
119
- /**
120
- * Extension of Dummy which records requests. Doesn't inherit due to the
121
- * static initializers.
122
- */
123
- export class TestDummyLLM extends DummyLLM {
124
- private readonly requests: TestLLMRequest[];
125
-
126
- constructor(script: DummyLLMScriptEntry[]) {
127
- super(script);
128
- this.requests = [];
129
- }
130
-
131
- override getConversationResponse(
132
- messages: MessageParam[],
133
- tools?: ToolDescriptor[],
134
- onMessage?: (msg: string, end: boolean) => Promise<void>,
135
- onReasoning?: (reasoning: string) => Promise<void>
136
- ): Promise<{ stop: (msg: string) => void; completion: Promise<Completion> }> {
137
- this.requests.push({ messages, tools });
138
- return super.getConversationResponse(
139
- messages,
140
- tools,
141
- onMessage,
142
- onReasoning
143
- );
144
- }
145
-
146
- getRequests(): TestLLMRequest[] {
147
- return this.requests;
148
- }
149
- }
150
-
151
- /**
152
- * An LLM which always errors out.
153
- */
154
- export class TestErrorLLM implements ILLM {
155
- getModel(): string {
156
- return "error";
157
- }
158
- getUrl(): string {
159
- return "error://";
160
- }
161
- setModel(_model: string): void {
162
- throw new Error("cannot set model");
163
- }
164
- getConversationResponse(
165
- _messages: MessageParam[],
166
- _tools?: ToolDescriptor[],
167
- _onMessage?: (msg: string, end: boolean) => Promise<void>
168
- ): Promise<{ stop: (msg: string) => void; completion: Promise<Completion> }> {
169
- return Promise.resolve({
170
- stop: () => {},
171
- completion: ((): Promise<Completion> => {
172
- throw new Error("LLM test error");
173
- })(),
174
- });
175
- }
176
- }
@@ -1,64 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { readFileSync } from "fs";
3
- import { join } from "path";
4
- import {
5
- calculatorEval,
6
- isoWithTimezone,
7
- makeParseArgsFn,
8
- openURL,
9
- } from "../agent/tools";
10
- import { pdfToText } from "../agent/tools/contentExtractors/pdfToText";
11
-
12
- describe("Tools", () => {
13
- it("time_now should contain timezone", function () {
14
- let time = isoWithTimezone("UTC");
15
- console.log(time);
16
- expect(time).includes("UTC");
17
-
18
- time = isoWithTimezone("Asia/Tokyo");
19
- console.log(time);
20
- expect(time).includes("GMT+9");
21
- expect(time).includes("Asia/Tokyo");
22
- });
23
-
24
- it("arithmetic should eval expressions", function () {
25
- expect(calculatorEval("(3*4)-2")).toEqual("10");
26
- });
27
-
28
- it("openUrl grabs text from url without error", async function () {
29
- const text = await openURL("https://www.google.com/");
30
- console.log(text);
31
- expect(text).not.contains("rror");
32
- });
33
-
34
- it("makeParseArgsFn behaves correctly", () => {
35
- const parseNameValue = makeParseArgsFn(["name", "value"] as const);
36
- expect(parseNameValue({ name: "adsf", value: "qwer" })).toEqual({
37
- name: "adsf",
38
- value: "qwer",
39
- });
40
- expect(() => parseNameValue({ name: "adsf" })).toThrow();
41
- });
42
- });
43
-
44
- describe("PDF extraction", () => {
45
- // GAIA benchmark test case: extract fish bag volume from Leicester paper
46
- it("extracts text from PDF file (GAIA benchmark)", async function () {
47
- // Use local PDF file instead of fetching from external URL
48
- // The PDF is the University of Leicester paper about dragon diet
49
- const pdfPath = join(__dirname, "../../..", "frontend/tests/paper.pdf");
50
- const pdfBuffer = readFileSync(pdfPath);
51
- const arrayBuffer = pdfBuffer.buffer.slice(
52
- pdfBuffer.byteOffset,
53
- pdfBuffer.byteOffset + pdfBuffer.byteLength
54
- );
55
-
56
- const text = await pdfToText(arrayBuffer);
57
-
58
- // The paper should contain the fish bag volume calculation: 0.1777 m³
59
- expect(text).toContain("0.1777");
60
-
61
- // Should also contain key terms from the paper
62
- expect(text.toLowerCase()).toContain("fish");
63
- });
64
- });
@@ -1,203 +0,0 @@
1
- import chalk from "chalk";
2
-
3
- type Spinner = {
4
- start: () => Spinner;
5
- stop: () => Spinner;
6
- clear: () => Spinner;
7
- };
8
-
9
- async function createSpinner(): Promise<Spinner> {
10
- const { default: yocto } = await import("yocto-spinner");
11
- return yocto();
12
- }
13
-
14
- import { configuration, utils } from "@xalia/xmcp/tool";
15
- import { getLogger } from "@xalia/xmcp/sdk";
16
-
17
- import { AgentProfile } from "../agent/agent";
18
- import { createAgentWithSkills } from "../agent/agentUtils";
19
- import { IAgentEventHandler } from "../agent/iAgentEventHandler";
20
-
21
- import { loadImageAsDataUrlOrUndefined } from "./files";
22
- import { NODE_PLATFORM } from "./nodePlatform";
23
- import { CommandPrompt } from "./commandPrompt";
24
- import { IPrompt, Prompt } from "./prompt";
25
- import { ContextManager } from "../agent/context";
26
- import {
27
- ContentPartImage,
28
- MessageParam,
29
- MessageToolCall,
30
- ToolMessageParam,
31
- } from "../agent/llm";
32
-
33
- const logger = getLogger();
34
-
35
- export const DEFAULT_AGENT_LLM_MODEL =
36
- process.env["DEFAULT_LLM_MODEL"] || "anthropic/claude-sonnet-4.5";
37
-
38
- async function write(msg: string): Promise<void> {
39
- return new Promise((resolve, err) => {
40
- process.stdout.write(msg, (e) => {
41
- if (e) {
42
- err(e);
43
- } else {
44
- resolve();
45
- }
46
- });
47
- });
48
- }
49
-
50
- export async function runChat(
51
- llmUrl: string,
52
- agentProfile: AgentProfile,
53
- conversation: MessageParam[] | undefined,
54
- prompt: string | undefined,
55
- image: string | undefined,
56
- llmApiKey: string | undefined,
57
- sudomcpConfig: configuration.Configuration,
58
- approveToolsUpTo: number,
59
- stream: boolean
60
- ) {
61
- // In chat mode, just print the messages. Ask for tool confirmation
62
- // unless approveTools is set.
63
-
64
- const spinner: Spinner = await createSpinner();
65
- let first = true;
66
- let reasoningFirst = true;
67
-
68
- const onAgentMessage = async (msg: string, msgEnd: boolean) => {
69
- if (first) {
70
- first = false;
71
-
72
- if (!reasoningFirst) {
73
- await write(`${chalk.grey("]")}\n`);
74
- }
75
-
76
- await write("AGENT: ");
77
- spinner.stop().clear();
78
- }
79
-
80
- if (msg) {
81
- await write(msg);
82
- }
83
-
84
- if (msgEnd) {
85
- await write("\n");
86
- first = true;
87
- reasoningFirst = true;
88
- }
89
- };
90
-
91
- const onReasoning = async (reasoning: string) => {
92
- logger.debug(`[AgentChat.onReasoning]: ${reasoning}`);
93
- if (reasoningFirst) {
94
- spinner.stop().clear();
95
- await write(chalk.grey("[REASONING: "));
96
- reasoningFirst = false;
97
- }
98
- await write(chalk.grey(reasoning));
99
- };
100
-
101
- const repl: IPrompt = new Prompt();
102
- const cmdPrompt = new CommandPrompt(repl);
103
-
104
- let remainingApprovedToolCalls = approveToolsUpTo;
105
- const onToolCall = async (toolCall: MessageToolCall) => {
106
- if (remainingApprovedToolCalls !== 0) {
107
- --remainingApprovedToolCalls;
108
- return true;
109
- }
110
-
111
- spinner.stop().clear();
112
- const result = await cmdPrompt.promptToolCall(toolCall);
113
- spinner.start();
114
- return result;
115
- };
116
-
117
- const onToolCallResult = (result: ToolMessageParam) => {
118
- // CLI doesn't need to display tool results - they're handled internally
119
- logger.debug(`Tool call result: ${JSON.stringify(result)}`);
120
- };
121
-
122
- const onImage = (image: ContentPartImage) => {
123
- const dataUrl = image.image_url.url;
124
- void NODE_PLATFORM.renderHTML(`<img src="${dataUrl}" />`);
125
- };
126
-
127
- // Create event handler for CLI agent
128
- const eventHandler: IAgentEventHandler = {
129
- onCompletion: () => {},
130
- onImage,
131
- onAgentMessage,
132
- onReasoning,
133
- onToolCall,
134
- onToolCallResult,
135
- };
136
-
137
- // Create agent
138
-
139
- const [agent, sudoMcpServerManager] = await createAgentWithSkills(
140
- llmUrl,
141
- agentProfile.model || DEFAULT_AGENT_LLM_MODEL,
142
- eventHandler,
143
- NODE_PLATFORM,
144
- new ContextManager(agentProfile.system_prompt, conversation || []),
145
- llmApiKey,
146
- sudomcpConfig,
147
- agentProfile.mcp_settings,
148
- utils.FRONTEND_PROD_AUTHORIZED_URL,
149
- stream
150
- );
151
-
152
- // Opening banner
153
- console.log(chalk.green("SudoMCP Agent CLI"));
154
- console.log(
155
- `(Type ${chalk.yellow("/h")} for help, ${chalk.yellow("/q")} to quit.)`
156
- );
157
-
158
- // Display first prompt if supplied
159
- if (prompt) {
160
- console.log(`USER: ${prompt}`);
161
- }
162
-
163
- // Conversation loop
164
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
165
- while (true) {
166
- if (!prompt) {
167
- const [msg, img] = await cmdPrompt.getNextPrompt(
168
- agent,
169
- sudoMcpServerManager
170
- );
171
- if (!msg && !img) {
172
- break;
173
- }
174
- prompt = msg;
175
- image = img;
176
- }
177
-
178
- image = loadImageAsDataUrlOrUndefined(image);
179
-
180
- // Pass the prompt and image to the Agent
181
- try {
182
- spinner.start();
183
- await agent.userMessageEx(prompt, image);
184
- } catch (e) {
185
- console.log(`ERROR: ${String(e)}`);
186
- console.log(` STACK: ${(e as Error).stack ?? "undefined"}`);
187
- }
188
- prompt = undefined;
189
- image = undefined;
190
- }
191
-
192
- // Shutdown the agent
193
-
194
- await cmdPrompt.shutdown();
195
- await agent.shutdown();
196
-
197
- logger.debug("shutdown done");
198
- }
199
-
200
- /**
201
- * Read lines from the prompt, parsing any commands, and return once there is
202
- * a prompt and/or image for the llm. Both undefined means exit.
203
- */
@@ -1,180 +0,0 @@
1
- import * as fs from "fs";
2
- import * as dotenv from "dotenv";
3
- import { command, option, flag, optional, string } from "cmd-ts";
4
- import { strict as assert } from "assert";
5
-
6
- import { configuration, utils } from "@xalia/xmcp/tool";
7
- import { getLogger } from "@xalia/xmcp/sdk";
8
-
9
- import { AgentProfile } from "../agent/agent";
10
- import { runOneShot } from "../agent/agentUtils";
11
-
12
- import {
13
- llmUrl,
14
- llmApiKey,
15
- promptFile,
16
- oneShot,
17
- approveTools,
18
- approveToolsUpTo,
19
- imageFile,
20
- } from "./options";
21
- import {
22
- loadFileOrUndefined,
23
- loadFileOrStdin,
24
- loadImageAsDataUrlOrUndefined,
25
- } from "./files";
26
- import { DEFAULT_AGENT_LLM_MODEL, runChat } from "./agentChat";
27
- import { NODE_PLATFORM } from "./nodePlatform";
28
- import { MessageParam } from "../agent/llm";
29
-
30
- dotenv.config();
31
-
32
- const logger = getLogger();
33
-
34
- export const agentMain = command({
35
- name: "main",
36
- args: {
37
- promptFile,
38
- imageFile,
39
- llmUrl,
40
- llmApiKey,
41
- oneShot,
42
- approveTools,
43
- approveToolsUpTo,
44
- sudomcpConfigFile: option({
45
- type: optional(string),
46
- long: "sudomcp-config-file",
47
- description:
48
- "SudoMCP config file (content or file, default: ~/config/..)",
49
- env: "SUDOMCP_CONFIG",
50
- }),
51
- conversationFile: option({
52
- type: optional(string),
53
- long: "conversation",
54
- description: "Restore conversation (content or file) (see /wc)",
55
- env: "CONVERSATION",
56
- }),
57
- conversationOutputFile: option({
58
- type: optional(string),
59
- long: "conversation-output",
60
- description: "Save final conversation to file (--one-shot mode)",
61
- env: "CONVERSATION_OUTPUT",
62
- }),
63
- agentProfileFile: option({
64
- type: optional(string),
65
- long: "agent-profile",
66
- description: "Agent profile (content or filename)",
67
- env: "AGENT_PROFILE",
68
- }),
69
- noStreaming: flag({
70
- long: "no-stream",
71
- description: "Disable streaming (chat mode only)",
72
- }),
73
- },
74
- handler: async ({
75
- promptFile,
76
- imageFile,
77
- llmUrl,
78
- llmApiKey,
79
- oneShot,
80
- approveTools,
81
- approveToolsUpTo,
82
- sudomcpConfigFile,
83
- conversationFile,
84
- conversationOutputFile,
85
- agentProfileFile,
86
- noStreaming,
87
- }): Promise<void> => {
88
- approveToolsUpTo = (() => {
89
- if (typeof approveToolsUpTo === "undefined") {
90
- // For the non-interactive case, `--approve-tools` is ignored.
91
- if (oneShot) {
92
- return -1;
93
- }
94
- return approveTools ? -1 : 0;
95
- }
96
- return approveToolsUpTo;
97
- })();
98
- assert(typeof approveToolsUpTo === "number");
99
-
100
- // Load the AgentProfile or use a default
101
-
102
- const agentProfile: AgentProfile = (() => {
103
- let agentProfile = utils.loadContentOrFileOrUndefined(
104
- agentProfileFile,
105
- AgentProfile
106
- );
107
- if (!agentProfile) {
108
- agentProfile = new AgentProfile(
109
- undefined,
110
- "You are a helpful agent",
111
- {}
112
- );
113
- }
114
-
115
- return agentProfile;
116
- })();
117
- logger.debug(`agent config: ${JSON.stringify(agentProfile)}`);
118
-
119
- // Read sudomcp config file. This will be used for SudoMcpServerManager,
120
- // and also to fall back to the sudomcp api key if an explicit llmApiKey
121
- // was not given.
122
-
123
- const sudomcpConfig = configuration.loadEnsureConfig(sudomcpConfigFile);
124
- if (!llmApiKey) {
125
- llmApiKey = sudomcpConfig.api_key;
126
- logger.debug(`using xmcp api key: ${llmApiKey}`);
127
- }
128
-
129
- // Restore conversation from value or file.
130
-
131
- const startingConversation: MessageParam[] | undefined =
132
- utils.loadContentOrFileOrUndefined(conversationFile);
133
- logger.debug(
134
- `startingConversation: ${JSON.stringify(startingConversation)}`
135
- );
136
-
137
- // Run in one-shot mode or chat-mode
138
-
139
- if (oneShot) {
140
- if (!promptFile) {
141
- throw new Error("one-shot mode requires a prompt");
142
- }
143
- const prompt = await loadFileOrStdin(promptFile);
144
-
145
- const { response, conversation } = await runOneShot(
146
- llmUrl,
147
- agentProfile,
148
- DEFAULT_AGENT_LLM_MODEL,
149
- startingConversation,
150
- NODE_PLATFORM,
151
- prompt,
152
- loadImageAsDataUrlOrUndefined(imageFile),
153
- llmApiKey,
154
- sudomcpConfig,
155
- approveToolsUpTo
156
- );
157
- console.log(response);
158
-
159
- if (conversationOutputFile) {
160
- logger.debug(`writing conversation to ${conversationOutputFile}:`);
161
- logger.debug(` conversation: ${JSON.stringify(conversation)}`);
162
-
163
- fs.writeFileSync(conversationOutputFile, JSON.stringify(conversation));
164
- }
165
- } else {
166
- const prompt = loadFileOrUndefined(promptFile);
167
- return runChat(
168
- llmUrl,
169
- agentProfile,
170
- startingConversation,
171
- prompt,
172
- imageFile,
173
- llmApiKey,
174
- sudomcpConfig,
175
- approveToolsUpTo,
176
- !noStreaming
177
- );
178
- }
179
- },
180
- });