wave-agent-sdk 1.0.4 → 1.0.6

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 (194) hide show
  1. package/dist/agent.d.ts +23 -1
  2. package/dist/agent.js +34 -4
  3. package/dist/managers/aiManager.d.ts +36 -0
  4. package/dist/managers/aiManager.js +240 -8
  5. package/dist/managers/messageManager.d.ts +1 -3
  6. package/dist/managers/messageManager.js +19 -9
  7. package/dist/managers/slashCommandManager.js +28 -0
  8. package/dist/managers/subagentManager.d.ts +0 -2
  9. package/dist/services/session.js +2 -9
  10. package/dist/telemetry/instrumentation.d.ts +1 -1
  11. package/dist/telemetry/instrumentation.js +57 -28
  12. package/dist/tools/bashTool.js +16 -6
  13. package/dist/tools/grepTool.js +11 -3
  14. package/dist/types/index.d.ts +3 -0
  15. package/dist/types/messaging.d.ts +2 -0
  16. package/dist/utils/containerSetup.js +0 -3
  17. package/dist/utils/messageOperations.d.ts +10 -1
  18. package/dist/utils/messageOperations.js +19 -1
  19. package/dist/utils/notificationXml.js +5 -0
  20. package/dist/utils/ripgrep.d.ts +4 -4
  21. package/dist/utils/ripgrep.js +4 -27
  22. package/package.json +20 -8
  23. package/bin/rg +0 -79
  24. package/dist/managers/forkedAgentManager.d.ts +0 -50
  25. package/dist/managers/forkedAgentManager.js +0 -130
  26. package/scripts/install_ripgrep.js +0 -111
  27. package/scripts/postinstall.js +0 -38
  28. package/src/agent.ts +0 -1271
  29. package/src/constants/subagents.ts +0 -4
  30. package/src/constants/toolLimits.ts +0 -15
  31. package/src/constants/tools.ts +0 -24
  32. package/src/core/plugin.ts +0 -237
  33. package/src/core/session.ts +0 -9
  34. package/src/index.ts +0 -38
  35. package/src/managers/MemoryRuleManager.ts +0 -198
  36. package/src/managers/aiManager.ts +0 -2429
  37. package/src/managers/backgroundTaskManager.ts +0 -480
  38. package/src/managers/bangManager.ts +0 -119
  39. package/src/managers/cronManager.ts +0 -401
  40. package/src/managers/foregroundTaskManager.ts +0 -30
  41. package/src/managers/forkedAgentManager.ts +0 -216
  42. package/src/managers/hookManager.ts +0 -1089
  43. package/src/managers/liveConfigManager.ts +0 -445
  44. package/src/managers/lspManager.ts +0 -470
  45. package/src/managers/mcpManager.ts +0 -1010
  46. package/src/managers/messageManager.ts +0 -1180
  47. package/src/managers/messageQueue.ts +0 -157
  48. package/src/managers/permissionManager.ts +0 -1140
  49. package/src/managers/planManager.ts +0 -112
  50. package/src/managers/pluginManager.ts +0 -330
  51. package/src/managers/pluginScopeManager.ts +0 -124
  52. package/src/managers/reversionManager.ts +0 -178
  53. package/src/managers/skillManager.ts +0 -578
  54. package/src/managers/slashCommandManager.ts +0 -564
  55. package/src/managers/subagentManager.ts +0 -915
  56. package/src/managers/toolManager.ts +0 -489
  57. package/src/managers/workflowManager.ts +0 -493
  58. package/src/prompts/autoMemory.ts +0 -33
  59. package/src/prompts/autoMemoryExtraction.ts +0 -146
  60. package/src/prompts/index.ts +0 -506
  61. package/src/prompts/planModeReminders.ts +0 -126
  62. package/src/services/GitService.ts +0 -131
  63. package/src/services/MarketplaceService.ts +0 -1052
  64. package/src/services/MemoryRuleService.ts +0 -71
  65. package/src/services/aiService.ts +0 -919
  66. package/src/services/authService.ts +0 -565
  67. package/src/services/autoMemoryService.ts +0 -280
  68. package/src/services/configurationService.ts +0 -1520
  69. package/src/services/fileWatcher.ts +0 -319
  70. package/src/services/hook.ts +0 -348
  71. package/src/services/initializationService.ts +0 -357
  72. package/src/services/interactionService.ts +0 -229
  73. package/src/services/jsonlHandler.ts +0 -295
  74. package/src/services/memory.ts +0 -234
  75. package/src/services/pluginLoader.ts +0 -252
  76. package/src/services/remoteSettingsService.ts +0 -366
  77. package/src/services/reversionService.ts +0 -122
  78. package/src/services/session.ts +0 -922
  79. package/src/services/taskManager.ts +0 -321
  80. package/src/telemetry/events.ts +0 -65
  81. package/src/telemetry/instrumentation.ts +0 -499
  82. package/src/telemetry/sessionTracing.ts +0 -348
  83. package/src/tools/agentTool.ts +0 -322
  84. package/src/tools/askUserQuestion.ts +0 -160
  85. package/src/tools/bashTool.ts +0 -621
  86. package/src/tools/buildTool.ts +0 -61
  87. package/src/tools/cronCreateTool.ts +0 -161
  88. package/src/tools/cronDeleteTool.ts +0 -51
  89. package/src/tools/cronListTool.ts +0 -47
  90. package/src/tools/editTool.ts +0 -313
  91. package/src/tools/enterPlanMode.ts +0 -124
  92. package/src/tools/enterWorktreeTool.ts +0 -190
  93. package/src/tools/exitPlanMode.ts +0 -139
  94. package/src/tools/exitWorktreeTool.ts +0 -236
  95. package/src/tools/globTool.ts +0 -167
  96. package/src/tools/grepTool.ts +0 -399
  97. package/src/tools/lspTool.ts +0 -883
  98. package/src/tools/readTool.ts +0 -426
  99. package/src/tools/skillTool.ts +0 -248
  100. package/src/tools/taskManagementTools.ts +0 -574
  101. package/src/tools/taskStopTool.ts +0 -78
  102. package/src/tools/types.ts +0 -137
  103. package/src/tools/webFetchTool.ts +0 -373
  104. package/src/tools/workflowTool.ts +0 -205
  105. package/src/tools/writeTool.ts +0 -267
  106. package/src/types/agent.ts +0 -128
  107. package/src/types/auth.ts +0 -19
  108. package/src/types/commands.ts +0 -30
  109. package/src/types/config.ts +0 -43
  110. package/src/types/configuration.ts +0 -168
  111. package/src/types/core.ts +0 -105
  112. package/src/types/cron.ts +0 -13
  113. package/src/types/environment.ts +0 -106
  114. package/src/types/fileSearch.ts +0 -4
  115. package/src/types/history.ts +0 -7
  116. package/src/types/hooks.ts +0 -256
  117. package/src/types/index.ts +0 -41
  118. package/src/types/lsp.ts +0 -98
  119. package/src/types/marketplace.ts +0 -71
  120. package/src/types/mcp.ts +0 -43
  121. package/src/types/memoryRule.ts +0 -31
  122. package/src/types/messaging.ts +0 -114
  123. package/src/types/permissions.ts +0 -80
  124. package/src/types/plugins.ts +0 -39
  125. package/src/types/processes.ts +0 -99
  126. package/src/types/reversion.ts +0 -29
  127. package/src/types/session.ts +0 -12
  128. package/src/types/skills.ts +0 -108
  129. package/src/types/tasks.ts +0 -13
  130. package/src/types/telemetry.ts +0 -98
  131. package/src/types/tools.ts +0 -44
  132. package/src/types/workflow.ts +0 -6
  133. package/src/utils/abortUtils.ts +0 -118
  134. package/src/utils/atomicWrite.ts +0 -61
  135. package/src/utils/bashParser.ts +0 -899
  136. package/src/utils/cacheControlUtils.ts +0 -426
  137. package/src/utils/commandArgumentParser.ts +0 -161
  138. package/src/utils/commandPathResolver.ts +0 -88
  139. package/src/utils/configPaths.ts +0 -220
  140. package/src/utils/configValidator.ts +0 -100
  141. package/src/utils/constants.ts +0 -39
  142. package/src/utils/container.ts +0 -92
  143. package/src/utils/containerSetup.ts +0 -371
  144. package/src/utils/convertMessagesForAPI.ts +0 -326
  145. package/src/utils/cronTasks.ts +0 -128
  146. package/src/utils/cronTasksLock.ts +0 -182
  147. package/src/utils/cronToHuman.ts +0 -99
  148. package/src/utils/customCommands.ts +0 -95
  149. package/src/utils/editUtils.ts +0 -20
  150. package/src/utils/fileFormat.ts +0 -40
  151. package/src/utils/fileSearch.ts +0 -151
  152. package/src/utils/fileUtils.ts +0 -223
  153. package/src/utils/gitUtils.ts +0 -315
  154. package/src/utils/globalLogger.ts +0 -128
  155. package/src/utils/groupMessagesByApiRound.ts +0 -121
  156. package/src/utils/hookMatcher.ts +0 -176
  157. package/src/utils/markdownParser.ts +0 -317
  158. package/src/utils/mcpUtils.ts +0 -138
  159. package/src/utils/messageOperations.ts +0 -641
  160. package/src/utils/modelCapabilities.ts +0 -30
  161. package/src/utils/nameGenerator.ts +0 -99
  162. package/src/utils/notificationXml.ts +0 -57
  163. package/src/utils/openaiClient.ts +0 -251
  164. package/src/utils/parseCronExpression.ts +0 -78
  165. package/src/utils/path.ts +0 -72
  166. package/src/utils/pathEncoder.ts +0 -400
  167. package/src/utils/pathSafety.ts +0 -40
  168. package/src/utils/promptHistory.ts +0 -168
  169. package/src/utils/ripgrep.ts +0 -35
  170. package/src/utils/shellResolver.ts +0 -190
  171. package/src/utils/skillParser.ts +0 -246
  172. package/src/utils/stringUtils.ts +0 -156
  173. package/src/utils/subagentParser.ts +0 -279
  174. package/src/utils/taskReminder.ts +0 -96
  175. package/src/utils/tokenCalculation.ts +0 -43
  176. package/src/utils/tokenEstimate.ts +0 -34
  177. package/src/utils/toolResultStorage.ts +0 -117
  178. package/src/utils/worktreeSession.ts +0 -26
  179. package/src/utils/worktreeUtils.ts +0 -786
  180. package/src/workflow/budgetTracker.ts +0 -34
  181. package/src/workflow/concurrencyLimiter.ts +0 -47
  182. package/src/workflow/journal.ts +0 -95
  183. package/src/workflow/progressReporter.ts +0 -141
  184. package/src/workflow/runState.ts +0 -65
  185. package/src/workflow/scriptRuntime.ts +0 -274
  186. package/src/workflow/structuredOutput.ts +0 -123
  187. package/src/workflow/types.ts +0 -101
  188. package/src/workflow/workflowApis.ts +0 -410
  189. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  190. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  191. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  192. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  193. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  194. package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
@@ -1,371 +0,0 @@
1
- import { Container } from "./container.js";
2
- import { ForegroundTaskManager } from "../managers/foregroundTaskManager.js";
3
- import { BackgroundTaskManager } from "../managers/backgroundTaskManager.js";
4
- import { MessageQueue } from "../managers/messageQueue.js";
5
- import { TaskManager } from "../services/taskManager.js";
6
- import { MessageManager } from "../managers/messageManager.js";
7
- import { AIManager } from "../managers/aiManager.js";
8
- import { ToolManager } from "../managers/toolManager.js";
9
- import { McpManager } from "../managers/mcpManager.js";
10
- import { LspManager } from "../managers/lspManager.js";
11
- import { PermissionManager } from "../managers/permissionManager.js";
12
- import { PlanManager } from "../managers/planManager.js";
13
- import { HookManager } from "../managers/hookManager.js";
14
- import { SkillManager } from "../managers/skillManager.js";
15
- import { SlashCommandManager } from "../managers/slashCommandManager.js";
16
- import { PluginManager } from "../managers/pluginManager.js";
17
- import { BangManager } from "../managers/bangManager.js";
18
- import { CronManager } from "../managers/cronManager.js";
19
- import { WorkflowManager } from "../managers/workflowManager.js";
20
- import { MemoryRuleManager } from "../managers/MemoryRuleManager.js";
21
- import { ReversionManager } from "../managers/reversionManager.js";
22
- import { SubagentManager } from "../managers/subagentManager.js";
23
- import { ForkedAgentManager } from "../managers/forkedAgentManager.js";
24
- import { LiveConfigManager } from "../managers/liveConfigManager.js";
25
- import { ConfigurationService } from "../services/configurationService.js";
26
- import { ReversionService } from "../services/reversionService.js";
27
- import { cleanupMetaOnlySessions } from "../services/session.js";
28
- import { MemoryService } from "../services/memory.js";
29
- import { AutoMemoryService } from "../services/autoMemoryService.js";
30
- import { USER_MEMORY_FILE } from "./constants.js";
31
- import { getGitMainRepoRoot } from "./gitUtils.js";
32
- import type { AgentOptions, McpServerConfig } from "../types/index.js";
33
- import type {
34
- PermissionMode,
35
- Usage,
36
- Task,
37
- BackgroundTask,
38
- ToolPermissionContext,
39
- } from "../types/index.js";
40
-
41
- import { logger } from "./globalLogger.js";
42
- import { authService } from "../services/authService.js";
43
- import { remoteSettingsService } from "../services/remoteSettingsService.js";
44
-
45
- export interface AgentContainerSetupOptions {
46
- options: AgentOptions;
47
- workdir: string;
48
- configurationService: ConfigurationService;
49
- systemPrompt?: string;
50
- stream: boolean;
51
-
52
- // Callbacks to Agent methods
53
- onBackgroundTasksChange: (tasks: BackgroundTask[]) => void;
54
- onTasksChange: (tasks: Task[]) => void;
55
- onPermissionModeChange: (mode: PermissionMode) => void;
56
- handlePlanModeTransition: (mode: PermissionMode) => void;
57
- setPermissionMode: (mode: PermissionMode) => void;
58
- addPermissionRule: (rule: string) => Promise<void>;
59
- addUsage: (usage: Usage) => void;
60
- }
61
-
62
- export function setupAgentContainer(
63
- setupOptions: AgentContainerSetupOptions,
64
- ): Container {
65
- const {
66
- options,
67
- workdir,
68
- configurationService,
69
- systemPrompt,
70
- stream,
71
- onBackgroundTasksChange,
72
- onTasksChange,
73
- onPermissionModeChange,
74
- handlePlanModeTransition,
75
- setPermissionMode,
76
- addPermissionRule,
77
- addUsage,
78
- } = setupOptions;
79
-
80
- const callbacks = options.callbacks || {};
81
- const container = new Container();
82
- container.register("AgentOptions", options);
83
- container.register("Workdir", workdir);
84
-
85
- if (options.worktreeName) {
86
- container.register("WorktreeName", options.worktreeName);
87
- container.register("MainRepoRoot", getGitMainRepoRoot(workdir));
88
- }
89
-
90
- // Per-agent worktree session state (EnterWorktree/ExitWorktree). Stored in the
91
- // container so it is isolated per session, not shared process-wide.
92
- container.register("WorktreeSession", null);
93
-
94
- const messageQueue = new MessageQueue();
95
- container.register("MessageQueue", messageQueue);
96
-
97
- const foregroundTaskManager = new ForegroundTaskManager(container);
98
- container.register("ForegroundTaskManager", foregroundTaskManager);
99
- container.register("ConfigurationService", configurationService);
100
-
101
- const memoryService = new MemoryService(container);
102
- container.register("MemoryService", memoryService);
103
-
104
- const autoMemoryService = new AutoMemoryService(container);
105
- container.register("AutoMemoryService", autoMemoryService);
106
-
107
- const memoryRuleManager = new MemoryRuleManager(container, { workdir });
108
- container.register("MemoryRuleManager", memoryRuleManager);
109
-
110
- const messageManager = new MessageManager(container, {
111
- callbacks: {
112
- ...callbacks,
113
- onSessionIdChange: (sessionId) => {
114
- const taskManager = container.get<TaskManager>("TaskManager");
115
- if (taskManager) {
116
- taskManager.syncWithSession().catch((error) => {
117
- logger.error("Failed to sync task list with session:", error);
118
- });
119
- }
120
- memoryService.clearCache();
121
- callbacks.onSessionIdChange?.(sessionId);
122
- },
123
- },
124
- workdir,
125
- });
126
- container.register("MessageManager", messageManager);
127
-
128
- const resolvedTaskListId =
129
- process.env.WAVE_TASK_LIST_ID || messageManager.getRootSessionId();
130
-
131
- const taskManager = new TaskManager(container, resolvedTaskListId);
132
- container.register("TaskManager", taskManager);
133
- taskManager.on("tasksChange", async () => {
134
- const tasks = await taskManager.listTasks();
135
- onTasksChange(tasks);
136
- });
137
- taskManager.cleanupOldTaskLists(30).catch((error) => {
138
- logger.error("Failed to cleanup old task lists:", error);
139
- });
140
-
141
- const backgroundTaskManager = new BackgroundTaskManager(container, {
142
- callbacks: {
143
- ...callbacks,
144
- onBackgroundTasksChange: (tasks) => {
145
- onBackgroundTasksChange(tasks);
146
- callbacks.onBackgroundTasksChange?.(tasks);
147
- },
148
- },
149
- workdir,
150
- });
151
- container.register("BackgroundTaskManager", backgroundTaskManager);
152
-
153
- if (options.serverUrl) {
154
- authService.setServerUrl(options.serverUrl);
155
- }
156
- const mcpManager = new McpManager(container, {
157
- callbacks,
158
- mcpServers: options.mcpServers as
159
- | Record<string, McpServerConfig>
160
- | undefined,
161
- });
162
- container.register("McpManager", mcpManager);
163
-
164
- // Wire up auth change callback to refresh/clear remote settings
165
- authService.onAuthChange(async (event) => {
166
- if (event === "login") {
167
- await remoteSettingsService.refresh();
168
- } else if (event === "logout") {
169
- remoteSettingsService.clear();
170
- }
171
- });
172
-
173
- const lspManager = options.lspManager || new LspManager(container);
174
- container.register("LspManager", lspManager);
175
-
176
- const permissionManager = new PermissionManager(container, {
177
- instanceAllowedRules: options.allowedTools,
178
- instanceDeniedRules: options.disallowedTools,
179
- instanceAdditionalDirectories: options.additionalDirectories,
180
- });
181
- if (configurationService.resolveAutoMemoryEnabled()) {
182
- const autoMemoryDir = memoryService.getAutoMemoryDirectory(workdir);
183
- permissionManager.addSystemAdditionalDirectory(autoMemoryDir);
184
- permissionManager.addSystemAdditionalDirectory(USER_MEMORY_FILE);
185
- }
186
- container.register("PermissionManager", permissionManager);
187
- permissionManager.setOnConfiguredPermissionModeChange((mode) => {
188
- handlePlanModeTransition(mode);
189
- onPermissionModeChange(mode);
190
- });
191
-
192
- const planManager = new PlanManager(container);
193
- container.register("PlanManager", planManager);
194
-
195
- const hookManager = new HookManager(container, workdir);
196
- if (options.hooks) {
197
- hookManager.loadConfiguration(options.hooks);
198
- }
199
- container.register("HookManager", hookManager);
200
-
201
- const skillManager = new SkillManager(container, {
202
- workdir,
203
- watch: options.watchSkills ?? true,
204
- });
205
- container.register("SkillManager", skillManager);
206
-
207
- const rootSessionId = messageManager.getRootSessionId();
208
-
209
- const reversionService = new ReversionService(rootSessionId);
210
- container.register("ReversionService", reversionService);
211
- reversionService.cleanupOldSessions(30).catch((error) => {
212
- logger.error("Failed to cleanup old file history:", error);
213
- });
214
- // Remove pre-existing meta-only session files (SessionStart hook context
215
- // with no real messages) that were persisted before lazy materialization.
216
- cleanupMetaOnlySessions()
217
- .then((count) => {
218
- if (count > 0) {
219
- logger.debug(`Removed ${count} meta-only session file(s)`);
220
- }
221
- })
222
- .catch((error) => {
223
- logger.error("Failed to cleanup meta-only session files:", error);
224
- });
225
- const reversionManager = new ReversionManager(container);
226
- container.register("ReversionManager", reversionManager);
227
-
228
- const canUseToolWithPermissionRequest = options.canUseTool
229
- ? async (context: ToolPermissionContext) => {
230
- try {
231
- const results = await hookManager.executeHooks("PermissionRequest", {
232
- event: "PermissionRequest",
233
- projectDir: workdir,
234
- timestamp: new Date(),
235
- sessionId: messageManager.getSessionId(),
236
- transcriptPath: messageManager.getTranscriptPath(),
237
- cwd: workdir,
238
- toolName: context.toolName,
239
- toolInput: context.toolInput,
240
- planFilePath: permissionManager.getPlanFilePath(),
241
- env: Object.fromEntries(
242
- Object.entries(configurationService.getMergedEnv()).filter(
243
- (e) => e[1] !== undefined,
244
- ),
245
- ) as Record<string, string>,
246
- });
247
-
248
- if (results.length > 0) {
249
- const processResult = hookManager.processHookResults(
250
- "PermissionRequest",
251
- results,
252
- messageManager,
253
- );
254
-
255
- if (processResult.shouldBlock) {
256
- return {
257
- behavior: "deny",
258
- message:
259
- processResult.errorMessage ||
260
- "Permission denied by hook execution",
261
- };
262
- }
263
- }
264
- } catch (error) {
265
- logger.warn("Failed to execute permission request hooks", {
266
- toolName: context.toolName,
267
- error: error instanceof Error ? error.message : String(error),
268
- });
269
- }
270
-
271
- const decision = await options.canUseTool!(context);
272
-
273
- if (decision.newPermissionMode) {
274
- setPermissionMode(decision.newPermissionMode);
275
- }
276
-
277
- if (decision.newPermissionRule) {
278
- await addPermissionRule(decision.newPermissionRule);
279
- }
280
-
281
- return decision;
282
- }
283
- : undefined;
284
-
285
- const toolManager = new ToolManager({
286
- container,
287
- tools: options.tools,
288
- customTools: options.customTools,
289
- });
290
- container.register("ToolManager", toolManager);
291
-
292
- container.register("PermissionMode", options.permissionMode);
293
- // Register the full permission mode transition (Agent.setPermissionMode) so
294
- // tools like EnterPlanMode can trigger planManager.handlePlanModeTransition
295
- // and onPermissionModeChange even in bypass mode (where canUseTool is skipped).
296
- container.register("PermissionModeTransition", setPermissionMode);
297
- logger.info("Registering CanUseToolCallback", {
298
- hasCallback: !!canUseToolWithPermissionRequest,
299
- });
300
- container.register("CanUseToolCallback", canUseToolWithPermissionRequest);
301
-
302
- const liveConfigManager = new LiveConfigManager(container, {
303
- workdir,
304
- onReload: () => {
305
- const models = configurationService.getConfiguredModels();
306
- callbacks.onConfiguredModelsChange?.(models);
307
- },
308
- });
309
- container.register("LiveConfigManager", liveConfigManager);
310
-
311
- // Wire up remote settings hot-update: when polling detects changed settings,
312
- // reload configuration so admin changes propagate to the running agent.
313
- remoteSettingsService.onSettingsUpdate(async () => {
314
- await liveConfigManager.reload();
315
- });
316
-
317
- const subagentManager = new SubagentManager(container, {
318
- workdir,
319
- callbacks: {
320
- onSubagentUserMessageAdded: callbacks.onSubagentUserMessageAdded,
321
- onSubagentAssistantMessageAdded:
322
- callbacks.onSubagentAssistantMessageAdded,
323
- onSubagentAssistantContentUpdated:
324
- callbacks.onSubagentAssistantContentUpdated,
325
- onSubagentAssistantReasoningUpdated:
326
- callbacks.onSubagentAssistantReasoningUpdated,
327
- onSubagentToolBlockUpdated: callbacks.onSubagentToolBlockUpdated,
328
- onSubagentMessagesChange: (subagentId, messages) => {
329
- callbacks.onSubagentMessagesChange?.(subagentId, messages);
330
- },
331
- onSubagentLatestTotalTokensChange:
332
- callbacks.onSubagentLatestTotalTokensChange,
333
- },
334
- onUsageAdded: (usage: Usage) => addUsage(usage),
335
- stream,
336
- });
337
- container.register("SubagentManager", subagentManager);
338
-
339
- const forkedAgentManager = new ForkedAgentManager(container);
340
- container.register("ForkedAgentManager", forkedAgentManager);
341
-
342
- const aiManager = new AIManager(container, {
343
- callbacks: {
344
- ...callbacks,
345
- onUsageAdded: (usage: Usage) => addUsage(usage),
346
- },
347
- workdir,
348
- systemPrompt,
349
- stream,
350
- });
351
- container.register("AIManager", aiManager);
352
-
353
- const slashCommandManager = new SlashCommandManager(container, { workdir });
354
- container.register("SlashCommandManager", slashCommandManager);
355
- slashCommandManager.initialize();
356
-
357
- const pluginManager = new PluginManager(container, { workdir });
358
- container.register("PluginManager", pluginManager);
359
-
360
- const bangManager = new BangManager(container, { workdir });
361
- container.register("BangManager", bangManager);
362
-
363
- const cronManager = new CronManager(container, messageManager.getSessionId());
364
- container.register("CronManager", cronManager);
365
- cronManager.start();
366
-
367
- const workflowManager = new WorkflowManager(container);
368
- container.register("WorkflowManager", workflowManager);
369
-
370
- return container;
371
- }
@@ -1,326 +0,0 @@
1
- import type { Message } from "../types/index.js";
2
- import { convertImageToBase64 } from "./messageOperations.js";
3
- import { taskNotificationToXml } from "./notificationXml.js";
4
- import { ChatCompletionMessageToolCall } from "openai/resources";
5
- import { recoverTruncatedJson, stripAnsiColors } from "./stringUtils.js";
6
- import {
7
- ChatCompletionContentPart,
8
- ChatCompletionMessageParam,
9
- } from "openai/resources.js";
10
- import { logger } from "./globalLogger.js";
11
-
12
- /**
13
- * Safely handle tool call parameters, ensuring a legal JSON string is returned.
14
- * Attempts to recover truncated JSON (e.g., missing closing braces).
15
- * @param args Tool call parameters
16
- * @returns Legal JSON string
17
- */
18
- function safeToolArguments(args: string): string {
19
- if (!args) {
20
- return "{}";
21
- }
22
-
23
- try {
24
- // Try to parse as JSON to validate format
25
- JSON.parse(args);
26
- return args;
27
- } catch {
28
- // Attempt to recover truncated JSON
29
- const recovered = recoverTruncatedJson(args);
30
- try {
31
- JSON.parse(recovered);
32
- return recovered;
33
- } catch {
34
- // Truly malformed JSON — return sanitized fallback
35
- return JSON.stringify({
36
- invalid_arguments: args,
37
- });
38
- }
39
- }
40
- }
41
-
42
- /**
43
- * Options for converting messages to API format.
44
- */
45
- export interface ConvertMessagesOptions {
46
- /** When false, images are stripped and replaced with text placeholders. */
47
- supportsVision?: boolean;
48
- }
49
-
50
- /**
51
- * Convert message format to API call format, stopping when a compacted message is encountered.
52
- * Messages with no meaningful content or tool calls are filtered out.
53
- * @param messages Message list
54
- * @param options Optional conversion options (e.g. supportsVision)
55
- * @returns Converted API message format list
56
- */
57
- export function convertMessagesForAPI(
58
- messages: Message[],
59
- options?: ConvertMessagesOptions,
60
- ): ChatCompletionMessageParam[] {
61
- const supportsVision = options?.supportsVision !== false;
62
- const recentMessages: ChatCompletionMessageParam[] = [];
63
-
64
- const startIndex = messages.length - 1;
65
- for (let i = startIndex; i >= 0; i--) {
66
- const message = messages[i];
67
-
68
- // Check if a compaction block is encountered, if so, stop iteration
69
- if (
70
- message.role === "assistant" &&
71
- message.blocks.some((block) => block.type === "compact")
72
- ) {
73
- // Add the content of the compaction block as an assistant message to the history
74
- const compactBlock = message.blocks.find(
75
- (block) => block.type === "compact",
76
- );
77
- if (compactBlock && compactBlock.type === "compact") {
78
- recentMessages.unshift({
79
- role: "user",
80
- content: compactBlock.content,
81
- });
82
- }
83
- break;
84
- }
85
-
86
- // Skip empty assistant messages (no blocks or all blocks are empty)
87
- if (message.role === "assistant" && message.blocks.length === 0) {
88
- continue;
89
- }
90
-
91
- if (message.role === "assistant") {
92
- // First check if there is a tool block, if so, add the tool message first
93
- // Filter out incomplete tool blocks (no result or still running)
94
- const toolBlocks = message.blocks.filter(
95
- (block) => block.type === "tool",
96
- );
97
- const completedToolIds = new Set<string>(); // Record completed tool IDs
98
-
99
- if (toolBlocks.length > 0) {
100
- // Collect image user messages to place after all tool messages
101
- const imageUserMessages: ChatCompletionMessageParam[] = [];
102
-
103
- toolBlocks.forEach((toolBlock) => {
104
- // Only add completed tool blocks (i.e., stage is 'end')
105
- if (toolBlock.id && toolBlock.stage === "end") {
106
- completedToolIds.add(toolBlock.id);
107
-
108
- // Always add a proper tool message to satisfy tool_call_id pairing
109
- recentMessages.unshift({
110
- tool_call_id: toolBlock.id,
111
- role: "tool",
112
- content: stripAnsiColors(toolBlock.result || ""),
113
- });
114
-
115
- // If there are images, also prepare a user message with image content
116
- if (toolBlock.images && toolBlock.images.length > 0) {
117
- if (supportsVision) {
118
- const contentParts: ChatCompletionContentPart[] = [];
119
-
120
- toolBlock.images.forEach((image) => {
121
- const imageUrl = image.data.startsWith("data:")
122
- ? image.data
123
- : `data:${image.mediaType || "image/png"};base64,${image.data}`;
124
-
125
- contentParts.push({
126
- type: "image_url",
127
- image_url: {
128
- url: imageUrl,
129
- detail: "auto",
130
- },
131
- });
132
- });
133
-
134
- imageUserMessages.push({
135
- role: "user",
136
- content: contentParts,
137
- });
138
- } else {
139
- // Non-vision model: replace images with a text placeholder
140
- imageUserMessages.push({
141
- role: "user",
142
- content:
143
- "[Tool returned an image, but the current model does not support image recognition]",
144
- });
145
- }
146
- }
147
- }
148
- });
149
-
150
- // Insert image user messages after all tool messages but before the
151
- // assistant message (which will be unshifted next). Since tool messages
152
- // were unshifted to the front, we splice images right after them.
153
- if (imageUserMessages.length > 0) {
154
- // Tool messages are at indices 0..N-1, existing messages start at N
155
- const toolMsgCount = completedToolIds.size;
156
- recentMessages.splice(toolMsgCount, 0, ...imageUserMessages);
157
- }
158
- }
159
-
160
- // Construct the content of the assistant message
161
- let content = "";
162
- let tool_calls: ChatCompletionMessageToolCall[] | undefined = undefined;
163
-
164
- // Construct content from text blocks - filter out empty content
165
- const textBlocks = message.blocks.filter(
166
- (block) =>
167
- block.type === "text" &&
168
- block.content &&
169
- block.content.trim().length > 0,
170
- );
171
- if (textBlocks.length > 0) {
172
- content = textBlocks
173
- .map((block) => (block.type === "text" ? block.content : ""))
174
- .join("\n");
175
- }
176
-
177
- // Extract reasoning content from reasoning blocks
178
- const reasoningBlocks = message.blocks.filter(
179
- (block) =>
180
- block.type === "reasoning" &&
181
- block.content &&
182
- block.content.trim().length > 0,
183
- );
184
- let reasoning_content: string | undefined;
185
- if (reasoningBlocks.length > 0) {
186
- reasoning_content = reasoningBlocks
187
- .map((block) => (block.type === "reasoning" ? block.content : ""))
188
- .join("\n");
189
- }
190
-
191
- // Construct tool calls from tool blocks
192
- if (toolBlocks.length > 0) {
193
- tool_calls = toolBlocks
194
- .filter(
195
- (toolBlock) => toolBlock.id && completedToolIds.has(toolBlock.id),
196
- )
197
- .map((toolBlock) => ({
198
- id: toolBlock.id!,
199
- type: "function",
200
- function: {
201
- name: toolBlock.name || "",
202
- arguments: safeToolArguments(
203
- String(toolBlock.parameters || "{}"),
204
- ),
205
- },
206
- }));
207
-
208
- if (tool_calls.length === 0) {
209
- tool_calls = undefined;
210
- }
211
- }
212
-
213
- // Construct assistant message - only add if there is meaningful content or tool calls
214
- const hasContent = content && content.trim().length > 0;
215
- const hasToolCalls = tool_calls && tool_calls.length > 0;
216
-
217
- if (hasContent || hasToolCalls) {
218
- const assistantMessage: ChatCompletionMessageParam = {
219
- role: "assistant",
220
- content: hasContent ? content : undefined,
221
- tool_calls,
222
- ...(reasoning_content ? { reasoning_content } : {}),
223
- ...(message.additionalFields ? { ...message.additionalFields } : {}),
224
- } as ChatCompletionMessageParam;
225
-
226
- recentMessages.unshift(assistantMessage);
227
- }
228
- } else if (message.role === "user") {
229
- // User messages converted to standard format
230
- const contentParts: ChatCompletionContentPart[] = [];
231
-
232
- message.blocks.forEach((block) => {
233
- // Add text content - only if it has meaningful content
234
- if (
235
- block.type === "text" &&
236
- block.content &&
237
- block.content.trim().length > 0
238
- ) {
239
- const textForApi = block.customCommandContent ?? block.content;
240
- contentParts.push({
241
- type: "text",
242
- text: textForApi,
243
- });
244
- }
245
-
246
- // If there is an image, add image content
247
- if (
248
- block.type === "image" &&
249
- block.imageUrls &&
250
- block.imageUrls.length > 0
251
- ) {
252
- if (!supportsVision) {
253
- // Non-vision model: replace images with a text placeholder
254
- contentParts.push({
255
- type: "text",
256
- text: "[User shared an image, but the current model does not support image recognition]",
257
- });
258
- } else {
259
- block.imageUrls.forEach((imageUrl: string) => {
260
- // Check if it's already base64, convert if not
261
- let finalImageUrl = imageUrl;
262
- if (!imageUrl.startsWith("data:image/")) {
263
- // If it's a file path, it needs to be converted to base64
264
- try {
265
- finalImageUrl = convertImageToBase64(imageUrl);
266
- } catch (error) {
267
- logger.error(
268
- "Failed to convert image path to base64:",
269
- imageUrl,
270
- error,
271
- );
272
- // Skip this image, do not add to content
273
- return;
274
- }
275
- }
276
-
277
- contentParts.push({
278
- type: "image_url",
279
- image_url: {
280
- url: finalImageUrl,
281
- detail: "auto",
282
- },
283
- });
284
- });
285
- }
286
- }
287
-
288
- // If there is a tool block in user message, add its result
289
- if (block.type === "tool" && block.stage === "end" && block.result) {
290
- contentParts.push({
291
- type: "text",
292
- text: `<local-command-stdout>\n${stripAnsiColors(block.result)}\n</local-command-stdout>`,
293
- });
294
- }
295
-
296
- // If there is a task notification block, convert it back to XML
297
- if (block.type === "task_notification") {
298
- contentParts.push({
299
- type: "text",
300
- text: taskNotificationToXml(block),
301
- });
302
- }
303
- });
304
-
305
- // Only add user message if there is meaningful content
306
- if (contentParts.length > 0) {
307
- // Filter out empty text parts
308
- const meaningfulParts = contentParts.filter((part) => {
309
- if (part.type === "text") {
310
- return part.text && part.text.trim().length > 0;
311
- }
312
- return true; // Keep image parts
313
- });
314
-
315
- if (meaningfulParts.length > 0) {
316
- recentMessages.unshift({
317
- role: "user",
318
- content: meaningfulParts,
319
- });
320
- }
321
- }
322
- }
323
- }
324
-
325
- return recentMessages;
326
- }