wave-agent-sdk 1.0.5 → 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 (193) 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 +13 -6
  7. package/dist/managers/slashCommandManager.js +28 -0
  8. package/dist/managers/subagentManager.d.ts +0 -2
  9. package/dist/telemetry/instrumentation.d.ts +1 -1
  10. package/dist/telemetry/instrumentation.js +57 -28
  11. package/dist/tools/bashTool.js +16 -6
  12. package/dist/tools/grepTool.js +11 -3
  13. package/dist/types/index.d.ts +3 -0
  14. package/dist/types/messaging.d.ts +2 -0
  15. package/dist/utils/containerSetup.js +0 -3
  16. package/dist/utils/messageOperations.d.ts +2 -1
  17. package/dist/utils/messageOperations.js +2 -1
  18. package/dist/utils/notificationXml.js +5 -0
  19. package/dist/utils/ripgrep.d.ts +4 -4
  20. package/dist/utils/ripgrep.js +4 -27
  21. package/package.json +20 -8
  22. package/bin/rg +0 -79
  23. package/dist/managers/forkedAgentManager.d.ts +0 -50
  24. package/dist/managers/forkedAgentManager.js +0 -130
  25. package/scripts/install_ripgrep.js +0 -111
  26. package/scripts/postinstall.js +0 -38
  27. package/src/agent.ts +0 -1271
  28. package/src/constants/subagents.ts +0 -4
  29. package/src/constants/toolLimits.ts +0 -15
  30. package/src/constants/tools.ts +0 -24
  31. package/src/core/plugin.ts +0 -237
  32. package/src/core/session.ts +0 -9
  33. package/src/index.ts +0 -38
  34. package/src/managers/MemoryRuleManager.ts +0 -198
  35. package/src/managers/aiManager.ts +0 -2429
  36. package/src/managers/backgroundTaskManager.ts +0 -480
  37. package/src/managers/bangManager.ts +0 -119
  38. package/src/managers/cronManager.ts +0 -401
  39. package/src/managers/foregroundTaskManager.ts +0 -30
  40. package/src/managers/forkedAgentManager.ts +0 -216
  41. package/src/managers/hookManager.ts +0 -1089
  42. package/src/managers/liveConfigManager.ts +0 -445
  43. package/src/managers/lspManager.ts +0 -470
  44. package/src/managers/mcpManager.ts +0 -1010
  45. package/src/managers/messageManager.ts +0 -1184
  46. package/src/managers/messageQueue.ts +0 -157
  47. package/src/managers/permissionManager.ts +0 -1140
  48. package/src/managers/planManager.ts +0 -112
  49. package/src/managers/pluginManager.ts +0 -330
  50. package/src/managers/pluginScopeManager.ts +0 -124
  51. package/src/managers/reversionManager.ts +0 -178
  52. package/src/managers/skillManager.ts +0 -578
  53. package/src/managers/slashCommandManager.ts +0 -564
  54. package/src/managers/subagentManager.ts +0 -915
  55. package/src/managers/toolManager.ts +0 -489
  56. package/src/managers/workflowManager.ts +0 -493
  57. package/src/prompts/autoMemory.ts +0 -33
  58. package/src/prompts/autoMemoryExtraction.ts +0 -146
  59. package/src/prompts/index.ts +0 -506
  60. package/src/prompts/planModeReminders.ts +0 -126
  61. package/src/services/GitService.ts +0 -131
  62. package/src/services/MarketplaceService.ts +0 -1052
  63. package/src/services/MemoryRuleService.ts +0 -71
  64. package/src/services/aiService.ts +0 -919
  65. package/src/services/authService.ts +0 -565
  66. package/src/services/autoMemoryService.ts +0 -280
  67. package/src/services/configurationService.ts +0 -1520
  68. package/src/services/fileWatcher.ts +0 -319
  69. package/src/services/hook.ts +0 -348
  70. package/src/services/initializationService.ts +0 -357
  71. package/src/services/interactionService.ts +0 -229
  72. package/src/services/jsonlHandler.ts +0 -295
  73. package/src/services/memory.ts +0 -234
  74. package/src/services/pluginLoader.ts +0 -252
  75. package/src/services/remoteSettingsService.ts +0 -366
  76. package/src/services/reversionService.ts +0 -122
  77. package/src/services/session.ts +0 -917
  78. package/src/services/taskManager.ts +0 -321
  79. package/src/telemetry/events.ts +0 -65
  80. package/src/telemetry/instrumentation.ts +0 -499
  81. package/src/telemetry/sessionTracing.ts +0 -348
  82. package/src/tools/agentTool.ts +0 -322
  83. package/src/tools/askUserQuestion.ts +0 -160
  84. package/src/tools/bashTool.ts +0 -621
  85. package/src/tools/buildTool.ts +0 -61
  86. package/src/tools/cronCreateTool.ts +0 -161
  87. package/src/tools/cronDeleteTool.ts +0 -51
  88. package/src/tools/cronListTool.ts +0 -47
  89. package/src/tools/editTool.ts +0 -313
  90. package/src/tools/enterPlanMode.ts +0 -124
  91. package/src/tools/enterWorktreeTool.ts +0 -190
  92. package/src/tools/exitPlanMode.ts +0 -139
  93. package/src/tools/exitWorktreeTool.ts +0 -236
  94. package/src/tools/globTool.ts +0 -167
  95. package/src/tools/grepTool.ts +0 -399
  96. package/src/tools/lspTool.ts +0 -883
  97. package/src/tools/readTool.ts +0 -426
  98. package/src/tools/skillTool.ts +0 -248
  99. package/src/tools/taskManagementTools.ts +0 -574
  100. package/src/tools/taskStopTool.ts +0 -78
  101. package/src/tools/types.ts +0 -137
  102. package/src/tools/webFetchTool.ts +0 -373
  103. package/src/tools/workflowTool.ts +0 -205
  104. package/src/tools/writeTool.ts +0 -267
  105. package/src/types/agent.ts +0 -128
  106. package/src/types/auth.ts +0 -19
  107. package/src/types/commands.ts +0 -30
  108. package/src/types/config.ts +0 -43
  109. package/src/types/configuration.ts +0 -168
  110. package/src/types/core.ts +0 -105
  111. package/src/types/cron.ts +0 -13
  112. package/src/types/environment.ts +0 -106
  113. package/src/types/fileSearch.ts +0 -4
  114. package/src/types/history.ts +0 -7
  115. package/src/types/hooks.ts +0 -256
  116. package/src/types/index.ts +0 -41
  117. package/src/types/lsp.ts +0 -98
  118. package/src/types/marketplace.ts +0 -71
  119. package/src/types/mcp.ts +0 -43
  120. package/src/types/memoryRule.ts +0 -31
  121. package/src/types/messaging.ts +0 -114
  122. package/src/types/permissions.ts +0 -80
  123. package/src/types/plugins.ts +0 -39
  124. package/src/types/processes.ts +0 -99
  125. package/src/types/reversion.ts +0 -29
  126. package/src/types/session.ts +0 -12
  127. package/src/types/skills.ts +0 -108
  128. package/src/types/tasks.ts +0 -13
  129. package/src/types/telemetry.ts +0 -98
  130. package/src/types/tools.ts +0 -44
  131. package/src/types/workflow.ts +0 -6
  132. package/src/utils/abortUtils.ts +0 -118
  133. package/src/utils/atomicWrite.ts +0 -61
  134. package/src/utils/bashParser.ts +0 -899
  135. package/src/utils/cacheControlUtils.ts +0 -426
  136. package/src/utils/commandArgumentParser.ts +0 -161
  137. package/src/utils/commandPathResolver.ts +0 -88
  138. package/src/utils/configPaths.ts +0 -220
  139. package/src/utils/configValidator.ts +0 -100
  140. package/src/utils/constants.ts +0 -39
  141. package/src/utils/container.ts +0 -92
  142. package/src/utils/containerSetup.ts +0 -371
  143. package/src/utils/convertMessagesForAPI.ts +0 -326
  144. package/src/utils/cronTasks.ts +0 -128
  145. package/src/utils/cronTasksLock.ts +0 -182
  146. package/src/utils/cronToHuman.ts +0 -99
  147. package/src/utils/customCommands.ts +0 -95
  148. package/src/utils/editUtils.ts +0 -20
  149. package/src/utils/fileFormat.ts +0 -40
  150. package/src/utils/fileSearch.ts +0 -151
  151. package/src/utils/fileUtils.ts +0 -223
  152. package/src/utils/gitUtils.ts +0 -315
  153. package/src/utils/globalLogger.ts +0 -128
  154. package/src/utils/groupMessagesByApiRound.ts +0 -121
  155. package/src/utils/hookMatcher.ts +0 -176
  156. package/src/utils/markdownParser.ts +0 -317
  157. package/src/utils/mcpUtils.ts +0 -138
  158. package/src/utils/messageOperations.ts +0 -659
  159. package/src/utils/modelCapabilities.ts +0 -30
  160. package/src/utils/nameGenerator.ts +0 -99
  161. package/src/utils/notificationXml.ts +0 -57
  162. package/src/utils/openaiClient.ts +0 -251
  163. package/src/utils/parseCronExpression.ts +0 -78
  164. package/src/utils/path.ts +0 -72
  165. package/src/utils/pathEncoder.ts +0 -400
  166. package/src/utils/pathSafety.ts +0 -40
  167. package/src/utils/promptHistory.ts +0 -168
  168. package/src/utils/ripgrep.ts +0 -35
  169. package/src/utils/shellResolver.ts +0 -190
  170. package/src/utils/skillParser.ts +0 -246
  171. package/src/utils/stringUtils.ts +0 -156
  172. package/src/utils/subagentParser.ts +0 -279
  173. package/src/utils/taskReminder.ts +0 -96
  174. package/src/utils/tokenCalculation.ts +0 -43
  175. package/src/utils/tokenEstimate.ts +0 -34
  176. package/src/utils/toolResultStorage.ts +0 -117
  177. package/src/utils/worktreeSession.ts +0 -26
  178. package/src/utils/worktreeUtils.ts +0 -786
  179. package/src/workflow/budgetTracker.ts +0 -34
  180. package/src/workflow/concurrencyLimiter.ts +0 -47
  181. package/src/workflow/journal.ts +0 -95
  182. package/src/workflow/progressReporter.ts +0 -141
  183. package/src/workflow/runState.ts +0 -65
  184. package/src/workflow/scriptRuntime.ts +0 -274
  185. package/src/workflow/structuredOutput.ts +0 -123
  186. package/src/workflow/types.ts +0 -101
  187. package/src/workflow/workflowApis.ts +0 -410
  188. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  189. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  190. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  191. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  192. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  193. 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
- }