@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,285 +0,0 @@
1
- import { McpServerSettings, getLogger } from "@xalia/xmcp/sdk";
2
- import { Configuration as SudoMcpConfiguration } from "@xalia/xmcp/sdk";
3
-
4
- import { Agent, AgentProfile } from "./agent";
5
- import { IAgentEventHandler } from "./iAgentEventHandler";
6
- import { IPlatform } from "./iplatform";
7
- import { SkillManager } from "./sudoMcpServerManager";
8
- import { OpenAILLM } from "./openAILLM";
9
- import { OpenAILLMStreaming } from "./openAILLMStreaming";
10
- import { DummyLLM } from "./dummyLLM";
11
- import { ContentPartImage, ILLM, MessageParam } from "./llm";
12
- import { strict as assert } from "assert";
13
- import { RepeatLLM } from "./repeatLLM";
14
- import { ContextManager, IContextManager } from "./context";
15
-
16
- const logger = getLogger();
17
-
18
- export async function createAgentWithoutSkills(
19
- llmUrl: string,
20
- model: string,
21
- eventHandler: IAgentEventHandler,
22
- platform: IPlatform,
23
- contextManager: IContextManager,
24
- llmApiKey: string | undefined,
25
- sudomcpConfig: SudoMcpConfiguration,
26
- authorizedUrl: string | undefined,
27
- stream: boolean = false
28
- ): Promise<[Agent, SkillManager]> {
29
- // Init SudoMcpServerManager
30
- logger.debug("[createAgentWithSkills] creating SudoMcpServerManager.");
31
- const sudoMcpServerManager = await SkillManager.initialize(
32
- (url: string, authResultP: Promise<boolean>, displayName: string) => {
33
- platform.openUrl(url, authResultP, displayName);
34
- },
35
- sudomcpConfig.backend_url,
36
- sudomcpConfig.api_key,
37
- authorizedUrl
38
- );
39
-
40
- // Create agent using the event handler
41
- const agent = await createAgentFromSkillManager(
42
- llmUrl,
43
- model,
44
- eventHandler,
45
- platform,
46
- contextManager,
47
- llmApiKey,
48
- sudoMcpServerManager,
49
- stream
50
- );
51
-
52
- return [agent, sudoMcpServerManager];
53
- }
54
-
55
- /**
56
- * Create and initialize an Agent given an AgentProfile using the
57
- * IAgentEventHandler interface. This is the preferred way to create
58
- * agents.
59
- */
60
- export async function createAgentWithSkills(
61
- llmUrl: string,
62
- model: string,
63
- eventHandler: IAgentEventHandler,
64
- platform: IPlatform,
65
- contextManager: IContextManager,
66
- llmApiKey: string | undefined,
67
- sudomcpConfig: SudoMcpConfiguration,
68
- mcpSettings: McpServerSettings,
69
- authorizedUrl: string | undefined,
70
- stream: boolean = false
71
- ): Promise<[Agent, SkillManager]> {
72
- const [agent, sudoMcpServerManager] = await createAgentWithoutSkills(
73
- llmUrl,
74
- model,
75
- eventHandler,
76
- platform,
77
- contextManager,
78
- llmApiKey,
79
- sudomcpConfig,
80
- authorizedUrl,
81
- stream
82
- );
83
-
84
- logger.debug(
85
- `[createAgentWithSkills] skilles: ${JSON.stringify(mcpSettings)}`
86
- );
87
- await sudoMcpServerManager.restoreMcpSettings(mcpSettings);
88
-
89
- return [agent, sudoMcpServerManager];
90
- }
91
-
92
- export async function createAgentFromSkillManager(
93
- llmUrl: string,
94
- model: string,
95
- eventHandler: IAgentEventHandler,
96
- platform: IPlatform,
97
- contextManager: IContextManager,
98
- llmApiKey: string | undefined,
99
- skillManager: SkillManager | undefined,
100
- stream: boolean = false
101
- ): Promise<Agent> {
102
- // Create agent
103
- logger.debug("[createAgentFromSkillManager] creating agent ...");
104
- const llm = await createLLM(llmUrl, llmApiKey, model, stream, platform);
105
- const agent = Agent.initializeWithLLM(
106
- eventHandler,
107
- llm,
108
- contextManager,
109
- skillManager
110
- );
111
-
112
- logger.debug("[createAgentFromSkillManager] done");
113
- return agent;
114
- }
115
-
116
- /**
117
- * Interpret the `model` string to create a specialized agent (dummy, repeat,
118
- * etc) or return undefined if a specialized agent has not been requested.
119
- */
120
- export async function createSpecializedLLM(
121
- model: string,
122
- platform: IPlatform
123
- ): Promise<ILLM | undefined> {
124
- let llm: ILLM | undefined;
125
-
126
- if (model && model.startsWith("dummy:")) {
127
- llm = await DummyLLM.initFromModelUrl(model, platform);
128
- } else if (model === "repeat") {
129
- llm = new RepeatLLM();
130
- }
131
- return llm;
132
- }
133
-
134
- export async function createLLM(
135
- llmUrl: string | undefined,
136
- llmApiKey: string | undefined,
137
- model: string,
138
- stream: boolean = false,
139
- platform: IPlatform
140
- ): Promise<ILLM> {
141
- let llm = await createSpecializedLLM(model, platform);
142
- if (llm) {
143
- return llm;
144
- }
145
-
146
- // Regular Agent
147
- if (!llmApiKey) {
148
- throw new Error("Missing OpenAI API Key");
149
- }
150
-
151
- logger.debug(`Initializing Agent: ${llmUrl ?? "unknown"} - ${model}`);
152
- if (stream) {
153
- llm = new OpenAILLMStreaming(llmApiKey, llmUrl, model);
154
- } else {
155
- llm = new OpenAILLM(llmApiKey, llmUrl, model);
156
- }
157
-
158
- assert(llm);
159
- return llm;
160
- }
161
-
162
- /**
163
- * An "non-interactive" agent is one which is not intended to be used
164
- * interactively (settings cannot be dyanmically adjusted, intermediate
165
- * messages are not used by the caller, the user does not need to approve tool
166
- * calls, etc).
167
- */
168
- export async function createNonInteractiveAgent(
169
- url: string,
170
- agentProfile: AgentProfile,
171
- defaultModel: string,
172
- conversation: MessageParam[] | undefined,
173
- platform: IPlatform,
174
- openaiApiKey: string | undefined,
175
- sudomcpConfig: SudoMcpConfiguration,
176
- approveToolsUpTo: number
177
- ): Promise<Agent> {
178
- let remainingToolCalls = approveToolsUpTo;
179
- const eventHandler: IAgentEventHandler = {
180
- onCompletion: () => {},
181
- onImage: () => {},
182
- onAgentMessage: async () => {},
183
- onReasoning: async () => {},
184
- // eslint-disable-next-line @typescript-eslint/require-await
185
- onToolCall: async () => {
186
- if (remainingToolCalls !== 0) {
187
- --remainingToolCalls;
188
- return true;
189
- }
190
- return false;
191
- },
192
- onToolCallResult: () => {},
193
- };
194
-
195
- const contextManager = new ContextManager(
196
- agentProfile.system_prompt,
197
- conversation || []
198
- );
199
- const [agent, _] = await createAgentWithSkills(
200
- url,
201
- agentProfile.model || defaultModel,
202
- eventHandler,
203
- platform,
204
- contextManager,
205
- openaiApiKey,
206
- sudomcpConfig,
207
- agentProfile.mcp_settings,
208
- undefined
209
- );
210
-
211
- return agent;
212
- }
213
-
214
- /**
215
- * Create an Agent (from the AgentProfile), pass it a single prompt and output
216
- * the response.
217
- */
218
- export async function runOneShot(
219
- url: string,
220
- agentProfile: AgentProfile,
221
- defaultModel: string,
222
- conversation: MessageParam[] | undefined,
223
- platform: IPlatform,
224
- prompt: string,
225
- image: string | undefined,
226
- llmApiKey: string | undefined,
227
- sudomcpConfig: SudoMcpConfiguration,
228
- approveToolsUpTo: number
229
- ): Promise<{
230
- response: string;
231
- conversation: MessageParam[];
232
- images: ContentPartImage[] | undefined;
233
- }> {
234
- logger.debug("[runOneShot]: start");
235
-
236
- // Create a non-interactive agent and pass any prompt/ image to it. Return
237
- // the first answer.
238
-
239
- const agent = await createNonInteractiveAgent(
240
- url,
241
- agentProfile,
242
- defaultModel,
243
- conversation,
244
- platform,
245
- llmApiKey,
246
- sudomcpConfig,
247
- approveToolsUpTo
248
- );
249
-
250
- const agentResponse = await agent.userMessageEx(prompt, image);
251
- await agent.shutdown();
252
- logger.debug("[runOneShot]: shutdown done");
253
-
254
- if (!agentResponse) {
255
- throw new Error("No message returned from agent");
256
- }
257
-
258
- // Handle different content types
259
- const response = agentResponse.message;
260
- let responseText = "";
261
- if (typeof response.content === "string") {
262
- responseText = response.content;
263
- } else if (response.content === null || response.content === undefined) {
264
- responseText = "";
265
- } else if (Array.isArray(response.content)) {
266
- // Handle array of content parts
267
- responseText = response.content
268
- .map((part) => {
269
- if ("text" in part) {
270
- return part.text;
271
- }
272
- return "";
273
- })
274
- .join("");
275
- } else {
276
- // Fallback for other types
277
- responseText = String(response.content);
278
- }
279
-
280
- return {
281
- response: responseText,
282
- conversation: agent.getConversation(),
283
- images: agentResponse.images,
284
- };
285
- }
@@ -1,129 +0,0 @@
1
- import { strict as assert } from "assert";
2
- import { getLogger } from "@xalia/xmcp/sdk";
3
-
4
- import { Agent } from "./agent";
5
- import { UserMessageParam, MessageParam, ILLM } from "./llm";
6
- import { ContextManager } from "./context";
7
- import { NULL_AGENT_EVENT_HANDLER } from "./nullAgentEventHandler";
8
- import { ContextManagerWithWorkspace } from "./contextWithWorkspace";
9
-
10
- const logger = getLogger();
11
-
12
- /**
13
- * System prompt used to generate a conversation summary.
14
- */
15
- const COMPRESSION_SYSTEM_PROMPT =
16
- // eslint-disable-next-line max-len
17
- "You are a context summarizer, creating MINIMAL conversation digests which can be used to CONTINUE the conversation at a later date. TOKEN EFFICIENCY is a HIGH PRIORITY. Summaries will only be seen by LLMs and should USE ANY AND ALL ABBREVIATIONS to keep them as CONCISE as possible, while PRESERVING IMPORTANT DETAILS of the CONVERSATION.";
18
-
19
- /**
20
- * Text prepended to a summary to create the checkpoint message
21
- */
22
- const CHECKPOINT_MESSAGE_PREFIX =
23
- // eslint-disable-next-line max-len
24
- "We are continuing an earlier conversation. The remainder of this message is a summary of the conversation so far: ";
25
-
26
- export function createCheckpointMessage(summary: string): UserMessageParam {
27
- return {
28
- role: "user",
29
- content: CHECKPOINT_MESSAGE_PREFIX + summary,
30
- };
31
- }
32
-
33
- export function createCompressionAgent(llm: ILLM): Agent {
34
- return Agent.initializeWithLLM(
35
- NULL_AGENT_EVENT_HANDLER,
36
- llm,
37
- new ContextManager(COMPRESSION_SYSTEM_PROMPT, [])
38
- );
39
- }
40
-
41
- export async function createSummary(
42
- llm: ILLM,
43
- conversation: MessageParam[]
44
- ): Promise<string> {
45
- const agent = createCompressionAgent(llm);
46
- const agentResp = await agent.userMessageEx(JSON.stringify(conversation));
47
- if (!agentResp) {
48
- throw new Error("compression agent returned null");
49
- }
50
-
51
- const resp = agentResp.message;
52
- assert(resp.role === "assistant");
53
- assert(
54
- typeof resp.content === "string",
55
- "expected string content from compression agent"
56
- );
57
-
58
- return resp.content;
59
- }
60
-
61
- /**
62
- * Can perform compression on the committed part of the context. Caller (not
63
- * the Agent) is responsible for committing the conversation and triggering
64
- * compression.
65
- */
66
- export class CompressingContextManager extends ContextManagerWithWorkspace {
67
- readonly getLLM: () => Promise<ILLM>;
68
- compressingMessages: number | undefined;
69
-
70
- constructor(
71
- systemPrompt: string,
72
- messages: MessageParam[],
73
- getLLM: () => Promise<ILLM>
74
- ) {
75
- super(systemPrompt, messages);
76
- this.getLLM = getLLM;
77
- this.compressingMessages = undefined;
78
-
79
- // Sanity check the conversation form.
80
- //
81
- // Ordinarily, the committed context should end with an "assistant"
82
- // message (i.e. user messages, an agent loop terminating in a final agent
83
- // response). However, if a conversation has been compressed, we may have
84
- // only the summary, which is a "user" message. In this case, this should
85
- // be the only message.
86
-
87
- const numMessages = this.numMessages();
88
- const lastMessage = this.lastMessage();
89
- if (lastMessage) {
90
- const finalRole = lastMessage.role;
91
- if (finalRole === "user") {
92
- assert(numMessages === 1);
93
- } else {
94
- assert(finalRole === "assistant", `unexpected final role ${finalRole}`);
95
- }
96
- }
97
- }
98
-
99
- async compress(): Promise<string> {
100
- const numToCompress = super.numMessages();
101
- const messagesToCompress = this.leadingMessages(numToCompress);
102
- assert(messagesToCompress.length === numToCompress);
103
- this.compressingMessages = numToCompress;
104
- assert(this.compressingMessages > 1, "<2 messages commited in the context");
105
-
106
- logger.debug(
107
- `[CompressingContextManager] start (${String(this.compressingMessages)})`
108
- );
109
-
110
- try {
111
- const llm = await this.getLLM();
112
- const summary = await createSummary(llm, messagesToCompress);
113
-
114
- logger.debug(`[CompressingContextManager] summary: ${summary}`);
115
-
116
- // Replace the context `messages` and update `lastCommittedMessage`
117
- // index.
118
-
119
- const checkpointMessage = createCheckpointMessage(summary);
120
- assert(typeof checkpointMessage.content === "string");
121
- this.replaceLeadingMessages(numToCompress, checkpointMessage);
122
-
123
- return summary;
124
- } finally {
125
- this.compressingMessages = undefined;
126
- logger.debug(`[CompressingContextManager] compression done`);
127
- }
128
- }
129
- }
@@ -1,265 +0,0 @@
1
- import { strict as assert } from "assert";
2
- import { MessageParam, UserMessageParam } from "./llm";
3
- import { SystemPromptProvider } from "./promptProvider";
4
-
5
- /**
6
- * ContextWriter
7
- */
8
- export interface IContextTransaction {
9
- /**
10
- * Add messages to the context. Return handle to the first message.
11
- */
12
- addMessages(messages: MessageParam[]): number;
13
- /**
14
- * Add a single message to the context. Returns a handle which can be used to
15
- * overwrite the message before it it committed.
16
- */
17
- addMessage(message: MessageParam): number;
18
- /**
19
- * Retrieve message by handle. Returns a reference to the message itself,
20
- * so changes made by the caller will be committed.
21
- */
22
- getMessage(handle: number): MessageParam;
23
- /**
24
- * Get all LLM messages including system message (for LLM calls). Owned by
25
- * the transaction.
26
- */
27
- getLLMContext(): MessageParam[];
28
-
29
- /**
30
- * Length of context, including messages in this tx
31
- */
32
- getLLMContextLength(): number;
33
- }
34
-
35
- /**
36
- * Implementations of IContextManager manage the context, exposing it as an
37
- * array of `ChatCompletionMessageParam[]`. The system prompt is managed
38
- * elsewhere, and set to this object by the caller.
39
- */
40
- export interface IContextManager {
41
- /**
42
- * Get all LLM messages including system message (for LLM calls).
43
- */
44
- getLLMContext(): MessageParam[];
45
-
46
- /**
47
- * Get the "fixed" part of the system prompt
48
- */
49
- getAgentPrompt(): string;
50
-
51
- /**
52
- * Set the "fixed" part of the system prompt
53
- */
54
- setAgentPrompt(prompt: string): void;
55
-
56
- setPromptFragment(fragmentID: string, prompt: string): void;
57
-
58
- removePromptFragment(fragmentID: string): void;
59
-
60
- /**
61
- * Start a transaction
62
- */
63
- startTx(userMessages: UserMessageParam[]): Promise<IContextTransaction>;
64
-
65
- /**
66
- * Commit
67
- */
68
- commit(writer: IContextTransaction): Promise<void>;
69
- }
70
-
71
- export class ContextTransaction implements IContextTransaction {
72
- private messages: MessageParam[];
73
- private origLength: number;
74
-
75
- constructor(baseMessages: MessageParam[], userMessages: UserMessageParam[]) {
76
- assert(baseMessages[0].role === "system");
77
- assert(baseMessages[1]?.role !== "system");
78
-
79
- this.messages = baseMessages;
80
- this.origLength = this.messages.length;
81
- this.messages.push(...userMessages);
82
- }
83
-
84
- // IContextTransaction.addMessages
85
- addMessages(messages: MessageParam[]): number {
86
- assert(messages.every((m) => m.role !== "user"));
87
- const handle = this.messages.length;
88
- for (const message of messages) {
89
- this.messages.push(message);
90
- }
91
- return handle;
92
- }
93
-
94
- // IContextTransaction.addMessage
95
- addMessage(message: MessageParam): number {
96
- assert(message.role !== "user");
97
- const handle = this.messages.length;
98
- this.messages.push(message);
99
- return handle;
100
- }
101
-
102
- // IContextTransaction.getMessage
103
- getMessage(handle: number): MessageParam {
104
- assert(handle < this.messages.length);
105
- return this.messages[handle];
106
- }
107
-
108
- // IContextTransaction.getLLMContext
109
- getLLMContext(): MessageParam[] {
110
- return this.messages;
111
- }
112
-
113
- // IContextTransaction.getLLMContextLength
114
- getLLMContextLength(): number {
115
- return this.messages.length;
116
- }
117
-
118
- newMessages(): MessageParam[] {
119
- return this.messages.slice(this.origLength);
120
- }
121
-
122
- protected getLLMContextOrigLength(): number {
123
- return this.origLength;
124
- }
125
-
126
- protected lastMessage(): MessageParam | undefined {
127
- if (this.messages.length > 1) {
128
- return this.messages[this.messages.length - 1];
129
- }
130
- return undefined;
131
- }
132
-
133
- popMessage(): MessageParam | undefined {
134
- if (this.messages.length > this.origLength) {
135
- return this.messages.pop();
136
- }
137
- return undefined;
138
- }
139
- }
140
-
141
- /**
142
- * Trivial implementation of IContextManage which just manages the context as
143
- * an array of `ChatCompletionMessageParam[]`
144
- */
145
- export class ContextManager implements IContextManager {
146
- private messages: MessageParam[];
147
- private systemPromptProvider: SystemPromptProvider;
148
-
149
- constructor(systemPrompt: string, messages: MessageParam[]) {
150
- assert(messages.length === 0 || messages[0].role !== "system");
151
-
152
- // Insert system message at the beginning if not present
153
- if (messages.length === 0 || messages[0].role !== "system") {
154
- this.messages = [
155
- {
156
- role: "system",
157
- content: "",
158
- } as MessageParam,
159
- ...messages,
160
- ];
161
- } else {
162
- assert(false);
163
- // this.messages = [...conversationMessages];
164
- // this.messages[0] = {systemPrompt;
165
- }
166
-
167
- this.systemPromptProvider = new SystemPromptProvider(systemPrompt);
168
- }
169
-
170
- // IContextManager.getLLMContext
171
- getLLMContext(): MessageParam[] {
172
- return this.getLLMContextRaw().slice();
173
- }
174
-
175
- // IContextManager.getAgentPrompt
176
- getAgentPrompt(): string {
177
- return this.systemPromptProvider.getAgentPrompt();
178
- }
179
-
180
- // IContextManager.setAgentPrompt
181
- setAgentPrompt(prompt: string): void {
182
- this.systemPromptProvider.setAgentPrompt(prompt);
183
- }
184
-
185
- // IContextManager.setPromptFragment
186
- setPromptFragment(fragmentID: string, prompt: string) {
187
- this.systemPromptProvider.setFragment(fragmentID, prompt);
188
- }
189
-
190
- // IContextManager.removePromptFragment
191
- removePromptFragment(fragmentID: string) {
192
- this.systemPromptProvider.removeFragment(fragmentID);
193
- }
194
-
195
- // IContextManager.startTx
196
- startTx(userMessages: UserMessageParam[]): Promise<ContextTransaction> {
197
- return Promise.resolve(
198
- new ContextTransaction(this.getLLMContext(), userMessages)
199
- );
200
- }
201
-
202
- // IContextManager.commit
203
- commit(writer: IContextTransaction): Promise<void> {
204
- assert(writer instanceof ContextTransaction);
205
- const newMessages = writer.newMessages();
206
- this.messages.push(...newMessages);
207
- return Promise.resolve();
208
- }
209
-
210
- clear(): void {
211
- this.messages = [
212
- {
213
- role: "system",
214
- content: "",
215
- },
216
- ];
217
- }
218
-
219
- protected lastMessage(): MessageParam | undefined {
220
- if (this.messages.length > 1) {
221
- return this.messages[this.messages.length - 1];
222
- }
223
- return undefined;
224
- }
225
-
226
- protected numMessages(): number {
227
- return this.messages.length - 1;
228
- }
229
-
230
- protected leadingMessages(numMessages: number): MessageParam[] {
231
- return this.messages.slice(1, numMessages + 1);
232
- }
233
-
234
- protected trailingMessagesFrom(idx: number): MessageParam[] {
235
- return this.messages.slice(1 + idx);
236
- }
237
-
238
- protected replaceLeadingMessages(
239
- numMessagesToReplace: number,
240
- replacement: MessageParam
241
- ): void {
242
- assert(this.messages.length >= 1 + numMessagesToReplace);
243
- const remainingMsgs = this.messages.slice(1 + numMessagesToReplace);
244
- this.messages = [this.messages[0], replacement, ...remainingMsgs];
245
- }
246
-
247
- protected popMessage(): MessageParam | undefined {
248
- if (this.messages.length > 1) {
249
- return this.messages.pop();
250
- }
251
- return undefined;
252
- }
253
-
254
- protected pushMessage(...msgs: MessageParam[]) {
255
- this.messages.push(...msgs);
256
- }
257
-
258
- /// Returns the actual array (so that child classes can avoid an unnecessary
259
- /// copy)
260
- protected getLLMContextRaw(): MessageParam[] {
261
- assert(this.messages[0].role === "system");
262
- this.messages[0].content = this.systemPromptProvider.getSystemPrompt();
263
- return this.messages;
264
- }
265
- }