wave-agent-sdk 1.0.5 → 1.0.7

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