wave-agent-sdk 0.17.0 → 0.17.2

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 (169) hide show
  1. package/builtin/skills/deep-research/SKILL.md +90 -0
  2. package/builtin/skills/settings/ENV.md +6 -3
  3. package/dist/agent.d.ts +28 -1
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +128 -34
  6. package/dist/constants/goalPrompts.d.ts +2 -0
  7. package/dist/constants/goalPrompts.d.ts.map +1 -0
  8. package/dist/constants/goalPrompts.js +10 -0
  9. package/dist/constants/tools.d.ts +1 -0
  10. package/dist/constants/tools.d.ts.map +1 -1
  11. package/dist/constants/tools.js +1 -0
  12. package/dist/managers/aiManager.d.ts +7 -0
  13. package/dist/managers/aiManager.d.ts.map +1 -1
  14. package/dist/managers/aiManager.js +77 -41
  15. package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
  16. package/dist/managers/backgroundTaskManager.js +10 -2
  17. package/dist/managers/goalManager.d.ts +43 -0
  18. package/dist/managers/goalManager.d.ts.map +1 -0
  19. package/dist/managers/goalManager.js +177 -0
  20. package/dist/managers/messageManager.d.ts +2 -2
  21. package/dist/managers/messageManager.d.ts.map +1 -1
  22. package/dist/managers/messageQueue.d.ts +10 -0
  23. package/dist/managers/messageQueue.d.ts.map +1 -1
  24. package/dist/managers/messageQueue.js +53 -1
  25. package/dist/managers/pluginManager.d.ts.map +1 -1
  26. package/dist/managers/pluginManager.js +7 -1
  27. package/dist/managers/skillManager.d.ts +2 -0
  28. package/dist/managers/skillManager.d.ts.map +1 -1
  29. package/dist/managers/skillManager.js +19 -9
  30. package/dist/managers/slashCommandManager.d.ts +6 -0
  31. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  32. package/dist/managers/slashCommandManager.js +105 -0
  33. package/dist/managers/toolManager.d.ts.map +1 -1
  34. package/dist/managers/toolManager.js +5 -0
  35. package/dist/managers/workflowManager.d.ts +65 -0
  36. package/dist/managers/workflowManager.d.ts.map +1 -0
  37. package/dist/managers/workflowManager.js +380 -0
  38. package/dist/prompts/index.d.ts +2 -1
  39. package/dist/prompts/index.d.ts.map +1 -1
  40. package/dist/prompts/index.js +3 -3
  41. package/dist/services/aiService.d.ts +23 -0
  42. package/dist/services/aiService.d.ts.map +1 -1
  43. package/dist/services/aiService.js +102 -9
  44. package/dist/services/configurationService.d.ts +1 -1
  45. package/dist/services/configurationService.d.ts.map +1 -1
  46. package/dist/services/configurationService.js +3 -16
  47. package/dist/services/hook.d.ts.map +1 -1
  48. package/dist/services/hook.js +4 -0
  49. package/dist/services/session.d.ts +9 -1
  50. package/dist/services/session.d.ts.map +1 -1
  51. package/dist/services/session.js +28 -1
  52. package/dist/tools/bashTool.d.ts.map +1 -1
  53. package/dist/tools/bashTool.js +49 -7
  54. package/dist/tools/readTool.d.ts.map +1 -1
  55. package/dist/tools/readTool.js +1 -1
  56. package/dist/tools/taskManagementTools.d.ts.map +1 -1
  57. package/dist/tools/taskManagementTools.js +103 -157
  58. package/dist/tools/types.d.ts +2 -0
  59. package/dist/tools/types.d.ts.map +1 -1
  60. package/dist/tools/webFetchTool.d.ts.map +1 -1
  61. package/dist/tools/webFetchTool.js +0 -9
  62. package/dist/tools/workflowTool.d.ts +11 -0
  63. package/dist/tools/workflowTool.d.ts.map +1 -0
  64. package/dist/tools/workflowTool.js +190 -0
  65. package/dist/types/agent.d.ts +2 -0
  66. package/dist/types/agent.d.ts.map +1 -1
  67. package/dist/types/commands.d.ts +4 -0
  68. package/dist/types/commands.d.ts.map +1 -1
  69. package/dist/types/config.d.ts +2 -2
  70. package/dist/types/config.d.ts.map +1 -1
  71. package/dist/types/core.d.ts +1 -1
  72. package/dist/types/core.d.ts.map +1 -1
  73. package/dist/types/hooks.d.ts +2 -0
  74. package/dist/types/hooks.d.ts.map +1 -1
  75. package/dist/types/index.d.ts +1 -0
  76. package/dist/types/index.d.ts.map +1 -1
  77. package/dist/types/index.js +1 -0
  78. package/dist/types/messaging.d.ts +2 -2
  79. package/dist/types/messaging.d.ts.map +1 -1
  80. package/dist/types/processes.d.ts +6 -2
  81. package/dist/types/processes.d.ts.map +1 -1
  82. package/dist/types/workflow.d.ts +2 -0
  83. package/dist/types/workflow.d.ts.map +1 -0
  84. package/dist/types/workflow.js +1 -0
  85. package/dist/utils/cacheControlUtils.d.ts +13 -8
  86. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  87. package/dist/utils/cacheControlUtils.js +73 -102
  88. package/dist/utils/containerSetup.d.ts.map +1 -1
  89. package/dist/utils/containerSetup.js +7 -0
  90. package/dist/utils/markdownParser.d.ts.map +1 -1
  91. package/dist/utils/markdownParser.js +21 -6
  92. package/dist/utils/messageOperations.d.ts +2 -2
  93. package/dist/utils/messageOperations.d.ts.map +1 -1
  94. package/dist/utils/notificationXml.d.ts.map +1 -1
  95. package/dist/workflow/budgetTracker.d.ts +12 -0
  96. package/dist/workflow/budgetTracker.d.ts.map +1 -0
  97. package/dist/workflow/budgetTracker.js +30 -0
  98. package/dist/workflow/concurrencyLimiter.d.ts +14 -0
  99. package/dist/workflow/concurrencyLimiter.d.ts.map +1 -0
  100. package/dist/workflow/concurrencyLimiter.js +39 -0
  101. package/dist/workflow/journal.d.ts +19 -0
  102. package/dist/workflow/journal.d.ts.map +1 -0
  103. package/dist/workflow/journal.js +74 -0
  104. package/dist/workflow/progressReporter.d.ts +21 -0
  105. package/dist/workflow/progressReporter.d.ts.map +1 -0
  106. package/dist/workflow/progressReporter.js +118 -0
  107. package/dist/workflow/runState.d.ts +16 -0
  108. package/dist/workflow/runState.d.ts.map +1 -0
  109. package/dist/workflow/runState.js +57 -0
  110. package/dist/workflow/scriptRuntime.d.ts +35 -0
  111. package/dist/workflow/scriptRuntime.d.ts.map +1 -0
  112. package/dist/workflow/scriptRuntime.js +196 -0
  113. package/dist/workflow/structuredOutput.d.ts +27 -0
  114. package/dist/workflow/structuredOutput.d.ts.map +1 -0
  115. package/dist/workflow/structuredOutput.js +106 -0
  116. package/dist/workflow/types.d.ts +81 -0
  117. package/dist/workflow/types.d.ts.map +1 -0
  118. package/dist/workflow/types.js +1 -0
  119. package/dist/workflow/workflowApis.d.ts +46 -0
  120. package/dist/workflow/workflowApis.d.ts.map +1 -0
  121. package/dist/workflow/workflowApis.js +280 -0
  122. package/package.json +1 -1
  123. package/src/agent.ts +144 -34
  124. package/src/constants/goalPrompts.ts +10 -0
  125. package/src/constants/tools.ts +1 -0
  126. package/src/managers/aiManager.ts +91 -47
  127. package/src/managers/backgroundTaskManager.ts +16 -4
  128. package/src/managers/goalManager.ts +232 -0
  129. package/src/managers/messageManager.ts +2 -2
  130. package/src/managers/messageQueue.ts +59 -1
  131. package/src/managers/pluginManager.ts +8 -1
  132. package/src/managers/skillManager.ts +20 -9
  133. package/src/managers/slashCommandManager.ts +119 -0
  134. package/src/managers/toolManager.ts +7 -0
  135. package/src/managers/workflowManager.ts +491 -0
  136. package/src/prompts/index.ts +4 -2
  137. package/src/services/aiService.ts +166 -12
  138. package/src/services/configurationService.ts +2 -22
  139. package/src/services/hook.ts +5 -0
  140. package/src/services/session.ts +42 -2
  141. package/src/tools/bashTool.ts +64 -9
  142. package/src/tools/readTool.ts +1 -2
  143. package/src/tools/taskManagementTools.ts +146 -195
  144. package/src/tools/types.ts +2 -0
  145. package/src/tools/webFetchTool.ts +0 -12
  146. package/src/tools/workflowTool.ts +205 -0
  147. package/src/types/agent.ts +6 -0
  148. package/src/types/commands.ts +4 -0
  149. package/src/types/config.ts +2 -2
  150. package/src/types/core.ts +3 -3
  151. package/src/types/hooks.ts +2 -0
  152. package/src/types/index.ts +1 -0
  153. package/src/types/messaging.ts +2 -2
  154. package/src/types/processes.ts +10 -2
  155. package/src/types/workflow.ts +5 -0
  156. package/src/utils/cacheControlUtils.ts +106 -131
  157. package/src/utils/containerSetup.ts +9 -0
  158. package/src/utils/markdownParser.ts +26 -8
  159. package/src/utils/messageOperations.ts +2 -2
  160. package/src/utils/notificationXml.ts +6 -1
  161. package/src/workflow/budgetTracker.ts +34 -0
  162. package/src/workflow/concurrencyLimiter.ts +47 -0
  163. package/src/workflow/journal.ts +95 -0
  164. package/src/workflow/progressReporter.ts +141 -0
  165. package/src/workflow/runState.ts +65 -0
  166. package/src/workflow/scriptRuntime.ts +274 -0
  167. package/src/workflow/structuredOutput.ts +123 -0
  168. package/src/workflow/types.ts +95 -0
  169. package/src/workflow/workflowApis.ts +412 -0
@@ -0,0 +1,380 @@
1
+ import { randomUUID } from "crypto";
2
+ import * as os from "os";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import { taskNotificationToXml } from "../utils/notificationXml.js";
6
+ import { ConcurrencyLimiter } from "../workflow/concurrencyLimiter.js";
7
+ import { BudgetTracker } from "../workflow/budgetTracker.js";
8
+ import { ProgressReporter } from "../workflow/progressReporter.js";
9
+ import { Journal } from "../workflow/journal.js";
10
+ import { createWorkflowApis } from "../workflow/workflowApis.js";
11
+ import { validateScript, parseScript, executeScript, } from "../workflow/scriptRuntime.js";
12
+ import { RunStateStore } from "../workflow/runState.js";
13
+ import { logger } from "../utils/globalLogger.js";
14
+ const DEFAULT_CONCURRENCY = () => Math.max(1, Math.min(16, os.cpus().length - 2));
15
+ export class WorkflowManager {
16
+ constructor(container) {
17
+ this.runs = new Map();
18
+ this.abortControllers = new Map();
19
+ this.agentControllers = new Map();
20
+ this.runStateStore = null;
21
+ this.container = container;
22
+ // Initialize RunStateStore lazily (sessionDir depends on MessageManager)
23
+ }
24
+ get stateStore() {
25
+ if (!this.runStateStore) {
26
+ this.runStateStore = new RunStateStore(path.join(this.sessionDir, "workflows"));
27
+ }
28
+ return this.runStateStore;
29
+ }
30
+ get backgroundTaskManager() {
31
+ return this.container.get("BackgroundTaskManager");
32
+ }
33
+ get notificationQueue() {
34
+ return this.container.get("NotificationQueue");
35
+ }
36
+ get subagentManager() {
37
+ return this.container.get("SubagentManager");
38
+ }
39
+ get workdir() {
40
+ return this.container.get("workdir") || process.cwd();
41
+ }
42
+ get sessionDir() {
43
+ const messageManager = this.container.get("MessageManager");
44
+ return (messageManager?.getSessionDir() ||
45
+ path.join(os.homedir(), ".wave", "sessions"));
46
+ }
47
+ /**
48
+ * Create a new workflow run from a script string or file path.
49
+ * Persists the script to the session directory.
50
+ */
51
+ async createRun(script, args, opts) {
52
+ // Validate script
53
+ const validation = validateScript(script);
54
+ if (!validation.valid) {
55
+ throw new Error(`Script validation failed:\n${validation.errors.join("\n")}`);
56
+ }
57
+ // Parse meta for the run object
58
+ const { meta } = parseScript(script);
59
+ // Validate resumeFromRunId if provided
60
+ if (opts?.resumeFromRunId) {
61
+ const prevRun = this.runs.get(opts.resumeFromRunId);
62
+ if (!prevRun) {
63
+ throw new Error(`Cannot resume: run ${opts.resumeFromRunId} not found`);
64
+ }
65
+ }
66
+ // Generate run ID and persist script
67
+ const runId = `wf_${randomUUID().slice(0, 8)}`;
68
+ const runDir = path.join(this.sessionDir, "workflows", runId);
69
+ await fs.promises.mkdir(path.join(runDir, "agents"), { recursive: true });
70
+ const scriptPath = path.join(runDir, "script.js");
71
+ await fs.promises.writeFile(scriptPath, script, "utf-8");
72
+ const run = {
73
+ runId,
74
+ meta,
75
+ status: "running",
76
+ scriptPath,
77
+ args,
78
+ startTime: Date.now(),
79
+ phases: [],
80
+ totalAgents: 0,
81
+ totalTokens: 0,
82
+ resumeFromRunId: opts?.resumeFromRunId,
83
+ };
84
+ this.runs.set(runId, run);
85
+ // Persist run state
86
+ this.stateStore.save(run).catch((err) => {
87
+ logger.warn(`[Workflow] Failed to persist run state: ${err instanceof Error ? err.message : String(err)}`);
88
+ });
89
+ return run;
90
+ }
91
+ /**
92
+ * Start executing a workflow run in the background.
93
+ * Returns immediately; the workflow runs asynchronously.
94
+ */
95
+ async startRun(runId, opts) {
96
+ const run = this.runs.get(runId);
97
+ if (!run)
98
+ throw new Error(`Workflow run ${runId} not found`);
99
+ if (run.status !== "running")
100
+ throw new Error(`Workflow run ${runId} is not in running state`);
101
+ const abortController = new AbortController();
102
+ this.abortControllers.set(runId, abortController);
103
+ const runAgentControllers = new Map();
104
+ this.agentControllers.set(runId, runAgentControllers);
105
+ // Read script from file
106
+ const script = await fs.promises.readFile(run.scriptPath, "utf-8");
107
+ // Set up infrastructure
108
+ const concurrencyLimiter = new ConcurrencyLimiter(DEFAULT_CONCURRENCY());
109
+ const budgetTracker = new BudgetTracker(run.args &&
110
+ typeof run.args === "object" &&
111
+ "budget" in run.args
112
+ ? run.args.budget
113
+ : null);
114
+ const progressReporter = new ProgressReporter(run.meta, runId);
115
+ // Forward progress events via onProgress callback
116
+ const onProgress = (event) => {
117
+ logger.debug(`[Workflow:${runId}] progress: ${event.type} phase=${event.phaseIndex} agent=${event.agentIndex}`);
118
+ };
119
+ progressReporter.onEvent(onProgress);
120
+ // Journal — load previous journal if resuming
121
+ let journal;
122
+ let initialAgentCount = 0;
123
+ if (run.resumeFromRunId || opts?.retryAgentIndex !== undefined) {
124
+ // Try new-format path first, fall back to old format
125
+ const sourceRunId = run.resumeFromRunId || runId;
126
+ const newJournalPath = path.join(this.sessionDir, "workflows", sourceRunId, "journal.jsonl");
127
+ const oldJournalPath = path.join(this.sessionDir, "workflows", `journal-${sourceRunId}.jsonl`);
128
+ const journalPath = (await fs.promises
129
+ .access(newJournalPath)
130
+ .then(() => true)
131
+ .catch(() => false))
132
+ ? newJournalPath
133
+ : oldJournalPath;
134
+ journal = await Journal.load(journalPath);
135
+ // When retrying a specific agent, remove its failed entry
136
+ if (opts?.retryAgentIndex !== undefined) {
137
+ journal.removeFailedEntry(opts.retryAgentIndex);
138
+ }
139
+ // Count existing agent entries to offset the counter
140
+ initialAgentCount = journal.agentEntryCount;
141
+ // Re-open for appending (load() doesn't open the write stream)
142
+ await journal.init();
143
+ logger.info(`[Workflow] Resuming from ${sourceRunId} with ${initialAgentCount} cached agent results`);
144
+ }
145
+ else {
146
+ const journalPath = path.join(this.sessionDir, "workflows", runId, "journal.jsonl");
147
+ journal = new Journal(journalPath);
148
+ await journal.init();
149
+ }
150
+ // Register as background task
151
+ const taskId = this.backgroundTaskManager.generateId();
152
+ this.backgroundTaskManager.addTask({
153
+ id: taskId,
154
+ type: "workflow",
155
+ status: "running",
156
+ startTime: Date.now(),
157
+ stdout: "",
158
+ stderr: "",
159
+ description: `Workflow: ${run.meta.name}`,
160
+ runId,
161
+ onStop: () => {
162
+ abortController.abort();
163
+ },
164
+ });
165
+ // Create workflow APIs
166
+ const apis = createWorkflowApis({
167
+ subagentManager: this.subagentManager,
168
+ concurrencyLimiter,
169
+ budgetTracker,
170
+ progressReporter,
171
+ journal,
172
+ abortSignal: abortController.signal,
173
+ args: run.args,
174
+ initialAgentCount,
175
+ sessionDir: this.sessionDir,
176
+ runDir: path.join(this.sessionDir, "workflows", runId),
177
+ agentControllers: runAgentControllers,
178
+ onProgress: (event) => {
179
+ logger.debug(`[Workflow:${runId}] progress: ${event.type} phase=${event.phaseIndex} agent=${event.agentIndex}`);
180
+ },
181
+ onLog: (message) => {
182
+ logger.info(`[Workflow:${runId}] ${message}`);
183
+ },
184
+ });
185
+ // Execute in background
186
+ run.completionPromise = (async () => {
187
+ try {
188
+ const { result } = await executeScript(script, apis, abortController.signal);
189
+ // Update run state
190
+ run.status = "completed";
191
+ run.endTime = Date.now();
192
+ run.result = result;
193
+ run.phases = progressReporter.getPhaseStates();
194
+ run.totalAgents = progressReporter.totalAgents;
195
+ run.totalTokens = progressReporter.totalTokens;
196
+ // Persist final state
197
+ this.stateStore.save(run).catch(() => { });
198
+ // Close journal
199
+ await journal.close();
200
+ // Update background task status
201
+ const task = this.backgroundTaskManager.getTask(taskId);
202
+ if (task) {
203
+ task.status = "completed";
204
+ task.endTime = Date.now();
205
+ }
206
+ // Enqueue completion notification
207
+ const journalPath = journal.filePath;
208
+ this.notificationQueue.enqueue(taskNotificationToXml({
209
+ type: "task_notification",
210
+ taskId: runId,
211
+ taskType: "workflow",
212
+ status: "completed",
213
+ summary: `Workflow "${run.meta.name}" completed — ${run.totalAgents} agents, ${(run.totalTokens / 1000).toFixed(1)}k tokens`,
214
+ ...(journalPath && { outputFile: journalPath }),
215
+ }));
216
+ logger.info(`[Workflow] Run ${runId} completed: ${run.totalAgents} agents, ${run.totalTokens} tokens`);
217
+ }
218
+ catch (error) {
219
+ // Only update if stopRun hasn't already set the status
220
+ if (run.status === "running") {
221
+ if (abortController.signal.aborted) {
222
+ run.status = "aborted";
223
+ }
224
+ else {
225
+ run.status = "failed";
226
+ run.error = error instanceof Error ? error.message : String(error);
227
+ }
228
+ run.endTime = Date.now();
229
+ }
230
+ run.phases = progressReporter.getPhaseStates();
231
+ run.totalAgents = progressReporter.totalAgents;
232
+ run.totalTokens = progressReporter.totalTokens;
233
+ // Persist failure/abort state
234
+ this.stateStore.save(run).catch(() => { });
235
+ await journal.close();
236
+ // Update background task status
237
+ const task = this.backgroundTaskManager.getTask(taskId);
238
+ if (task) {
239
+ task.status = abortController.signal.aborted ? "killed" : "failed";
240
+ task.stderr = run.error || "";
241
+ task.endTime = Date.now();
242
+ }
243
+ this.notificationQueue.enqueue(taskNotificationToXml({
244
+ type: "task_notification",
245
+ taskId: runId,
246
+ taskType: "workflow",
247
+ status: run.status === "aborted" ? "aborted" : "failed",
248
+ summary: `Workflow "${run.meta.name}" ${run.status}${run.error ? `: ${run.error}` : ""}`,
249
+ }));
250
+ logger.warn(`[Workflow] Run ${runId} ${run.status}: ${run.error || "aborted"}`);
251
+ }
252
+ finally {
253
+ this.abortControllers.delete(runId);
254
+ this.agentControllers.delete(runId);
255
+ }
256
+ })();
257
+ // Don't await — let it run in background
258
+ run.completionPromise.catch(() => { }); // Prevent unhandled rejection
259
+ }
260
+ /**
261
+ * Resume a workflow run from its journal.
262
+ */
263
+ async resumeRun(runId) {
264
+ const run = this.runs.get(runId);
265
+ if (!run)
266
+ throw new Error(`Workflow run ${runId} not found`);
267
+ run.status = "running";
268
+ await this.startRun(runId);
269
+ }
270
+ /**
271
+ * Stop a running workflow.
272
+ */
273
+ stopRun(runId) {
274
+ const controller = this.abortControllers.get(runId);
275
+ if (controller) {
276
+ controller.abort();
277
+ }
278
+ const run = this.runs.get(runId);
279
+ if (run && run.status === "running") {
280
+ run.status = "aborted";
281
+ run.endTime = Date.now();
282
+ }
283
+ }
284
+ /**
285
+ * Skip a specific agent in a running workflow.
286
+ * Aborts the agent's controller and lets the workflow continue.
287
+ */
288
+ skipAgent(runId, agentIndex) {
289
+ const run = this.runs.get(runId);
290
+ if (!run || run.status !== "running")
291
+ return;
292
+ const agentController = this.agentControllers.get(runId)?.get(agentIndex);
293
+ if (agentController) {
294
+ agentController.abort();
295
+ }
296
+ }
297
+ /**
298
+ * Retry a specific agent by removing its journal entry and resuming.
299
+ */
300
+ async retryAgent(runId, agentIndex) {
301
+ const run = this.runs.get(runId);
302
+ if (!run)
303
+ throw new Error(`Workflow run ${runId} not found`);
304
+ // Stop the current execution
305
+ this.stopRun(runId);
306
+ // The journal will have cached results; when we resume,
307
+ // the agent_failed entry for this index will cause getCachedResult
308
+ // to return undefined, forcing re-execution
309
+ run.status = "running";
310
+ run.error = undefined;
311
+ run.failedAgentIndex = undefined;
312
+ run.failedAgentError = undefined;
313
+ await this.startRun(runId, { retryAgentIndex: agentIndex });
314
+ }
315
+ /**
316
+ * Kill a running workflow — aborts all agent controllers plus the run controller.
317
+ */
318
+ killRun(runId) {
319
+ // Abort all per-agent controllers
320
+ const agentControllers = this.agentControllers.get(runId);
321
+ if (agentControllers) {
322
+ for (const controller of agentControllers.values()) {
323
+ controller.abort();
324
+ }
325
+ }
326
+ // Abort the run controller
327
+ const controller = this.abortControllers.get(runId);
328
+ if (controller) {
329
+ controller.abort();
330
+ }
331
+ const run = this.runs.get(runId);
332
+ if (run && run.status === "running") {
333
+ run.status = "aborted";
334
+ run.endTime = Date.now();
335
+ }
336
+ }
337
+ /**
338
+ * List all workflow runs (includes in-memory and persisted).
339
+ */
340
+ async listRuns() {
341
+ // Load persisted runs that aren't already in memory
342
+ const persistedIds = await this.stateStore.listRuns();
343
+ for (const runId of persistedIds) {
344
+ if (!this.runs.has(runId)) {
345
+ const run = await this.stateStore.load(runId);
346
+ if (run) {
347
+ this.runs.set(runId, run);
348
+ }
349
+ }
350
+ }
351
+ return Array.from(this.runs.values());
352
+ }
353
+ /**
354
+ * Get a specific workflow run.
355
+ */
356
+ getRun(runId) {
357
+ return this.runs.get(runId);
358
+ }
359
+ /**
360
+ * Clean up all running workflows.
361
+ */
362
+ cleanup() {
363
+ for (const controller of this.abortControllers.values()) {
364
+ controller.abort();
365
+ }
366
+ this.abortControllers.clear();
367
+ for (const agentMap of this.agentControllers.values()) {
368
+ for (const controller of agentMap.values()) {
369
+ controller.abort();
370
+ }
371
+ }
372
+ this.agentControllers.clear();
373
+ for (const run of this.runs.values()) {
374
+ if (run.status === "running") {
375
+ run.status = "aborted";
376
+ run.endTime = Date.now();
377
+ }
378
+ }
379
+ }
380
+ }
@@ -16,6 +16,7 @@ export declare const WEB_CONTENT_SYSTEM_PROMPT = "You are a helpful assistant th
16
16
  export declare const BTW_SYSTEM_PROMPT = "You are a helpful assistant. Answer the user's side question based on the conversation history. \nDo NOT say things like \"Let me try...\", \"I'll now...\", \"Let me check...\", or promise to take any action. \nIf you don't know the answer, say so - do not offer to look it up or investigate. \nSimply answer the question with the information you have.";
17
17
  export declare function buildSystemPrompt(basePrompt: string | undefined, tools: ToolPlugin[], options?: {
18
18
  workdir?: string;
19
+ originalWorkdir?: string;
19
20
  memory?: string;
20
21
  language?: string;
21
22
  isSubagent?: boolean;
@@ -25,5 +26,5 @@ export declare function buildSystemPrompt(basePrompt: string | undefined, tools:
25
26
  };
26
27
  permissionMode?: PermissionMode;
27
28
  }): string;
28
- export declare function enhanceSystemPromptWithEnvDetails(existingSystemPrompt: string, workdir: string): string;
29
+ export declare function enhanceSystemPromptWithEnvDetails(existingSystemPrompt: string, workdir: string, originalWorkdir?: string): string;
29
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI/C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAiBzD,eAAO,MAAM,kBAAkB,oKAAoK,CAAC;AAEpM,eAAO,MAAM,kBAAkB,opIAcqQ,CAAC;AAErS,eAAO,MAAM,wBAAwB,25DASqmB,CAAC;AAE3oB,eAAO,MAAM,WAAW,ihDAWqH,CAAC;AAE9I;;GAEG;AACH,eAAO,MAAM,wBAAwB,+uBAWiH,CAAC;AAEvJ,eAAO,MAAM,qBAAqB,6sBAMuL,CAAC;AAE1N,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,OAAO,EACnB,UAAU,GAAE,OAAe,GAC1B,MAAM,CAmFR;AAED,eAAO,MAAM,qBAAqB,oKAAqB,CAAC;AAExD,eAAO,MAAM,8BAA8B,44DA8CI,CAAC;AAEhD,eAAO,MAAM,yBAAyB,wHAAwH,CAAC;AAC/J,eAAO,MAAM,iBAAiB,qWAG4B,CAAC;AAE3D,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;CAC5B,GACL,MAAM,CA4DR;AAED,wBAAgB,iCAAiC,CAC/C,oBAAoB,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,GACd,MAAM,CAkCR"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI/C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAiBzD,eAAO,MAAM,kBAAkB,oKAAoK,CAAC;AAEpM,eAAO,MAAM,kBAAkB,opIAcqQ,CAAC;AAErS,eAAO,MAAM,wBAAwB,25DASqmB,CAAC;AAE3oB,eAAO,MAAM,WAAW,ihDAWqH,CAAC;AAE9I;;GAEG;AACH,eAAO,MAAM,wBAAwB,+uBAWiH,CAAC;AAEvJ,eAAO,MAAM,qBAAqB,6sBAMuL,CAAC;AAE1N,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,OAAO,EACnB,UAAU,GAAE,OAAe,GAC1B,MAAM,CAmFR;AAED,eAAO,MAAM,qBAAqB,oKAAqB,CAAC;AAExD,eAAO,MAAM,8BAA8B,44DA8CI,CAAC;AAEhD,eAAO,MAAM,yBAAyB,wHAAwH,CAAC;AAC/J,eAAO,MAAM,iBAAiB,qWAG4B,CAAC;AAE3D,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;CAC5B,GACL,MAAM,CA4DR;AAED,wBAAgB,iCAAiC,CAC/C,oBAAoB,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACf,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,CAkCR"}
@@ -230,7 +230,7 @@ export function buildSystemPrompt(basePrompt, tools, options = {}) {
230
230
 
231
231
  Here is useful information about the environment you are running in:
232
232
  <env>
233
- Working directory: ${options.workdir}${worktreeSession ? `\nThis is a git worktree — an isolated copy of the repository. Run all commands from this directory. Do NOT \`cd\` to the original repository root at ${worktreeSession.originalCwd}.` : ""}
233
+ Primary working directory: ${options.originalWorkdir ?? options.workdir}${worktreeSession ? `\nThis is a git worktree — an isolated copy of the repository. Run all commands from this directory. Do NOT \`cd\` to the original repository root at ${worktreeSession.originalCwd}.` : ""}
234
234
  Is directory a git repo: ${isGitRepo}
235
235
  Platform: ${platform}
236
236
  Shell: ${shellName}
@@ -250,7 +250,7 @@ Today's date: ${today}
250
250
  }
251
251
  return prompt;
252
252
  }
253
- export function enhanceSystemPromptWithEnvDetails(existingSystemPrompt, workdir) {
253
+ export function enhanceSystemPromptWithEnvDetails(existingSystemPrompt, workdir, originalWorkdir) {
254
254
  const isGitRepo = isGitRepository(workdir);
255
255
  const platform = os.platform();
256
256
  const osVersion = `${os.type()} ${os.release()}`;
@@ -273,7 +273,7 @@ ${notes}
273
273
 
274
274
  Here is useful information about the environment you are running in:
275
275
  <env>
276
- Working directory: ${workdir}${worktreeSession ? `\nThis is a git worktree — an isolated copy of the repository. Run all commands from this directory. Do NOT \`cd\` to the original repository root at ${worktreeSession.originalCwd}.` : ""}
276
+ Primary working directory: ${originalWorkdir ?? workdir}${worktreeSession ? `\nThis is a git worktree — an isolated copy of the repository. Run all commands from this directory. Do NOT \`cd\` to the original repository root at ${worktreeSession.originalCwd}.` : ""}
277
277
  Is directory a git repo: ${isGitRepo}
278
278
  Platform: ${platform}
279
279
  Shell: ${shellName}
@@ -17,6 +17,12 @@ export interface CallAgentOptions {
17
17
  model?: string;
18
18
  systemPrompt?: string;
19
19
  maxTokens?: number;
20
+ toolChoice?: "auto" | "none" | "required" | {
21
+ type: "function";
22
+ function: {
23
+ name: string;
24
+ };
25
+ };
20
26
  onContentUpdate?: (content: string) => void;
21
27
  onToolUpdate?: (toolCall: {
22
28
  id: string;
@@ -88,4 +94,21 @@ export interface BtwResult {
88
94
  };
89
95
  }
90
96
  export declare function btw(options: BtwOptions): Promise<BtwResult>;
97
+ export interface EvaluateGoalOptions {
98
+ gatewayConfig: GatewayConfig;
99
+ modelConfig: ModelConfig;
100
+ model: string;
101
+ goalCondition: string;
102
+ messages: ChatCompletionMessageParam[];
103
+ abortSignal?: AbortSignal;
104
+ }
105
+ export interface EvaluateGoalResult {
106
+ content: string;
107
+ usage?: {
108
+ prompt_tokens: number;
109
+ completion_tokens: number;
110
+ total_tokens: number;
111
+ };
112
+ }
113
+ export declare function evaluateGoal(options: EvaluateGoalOptions): Promise<EvaluateGoalResult>;
91
114
  //# sourceMappingURL=aiService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aiService.d.ts","sourceRoot":"","sources":["../../src/services/aiService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aiService.d.ts","sourceRoot":"","sources":["../../src/services/aiService.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  import { OpenAIClient } from "../utils/openaiClient.js";
2
2
  import { logger } from "../utils/globalLogger.js";
3
3
  import { addOnceAbortListener } from "../utils/abortUtils.js";
4
+ import { ConfigurationError, CONFIG_ERRORS } from "../types/index.js";
4
5
  import { transformMessagesForClaudeCache, addCacheControlToLastTool, supportsPromptCaching, extendUsageWithCacheMetrics, } from "../utils/cacheControlUtils.js";
5
6
  import * as os from "os";
6
7
  import * as fs from "fs";
7
8
  import * as path from "path";
8
9
  import { COMPACT_MESSAGES_SYSTEM_PROMPT, WEB_CONTENT_SYSTEM_PROMPT, BTW_SYSTEM_PROMPT, } from "../prompts/index.js";
10
+ import { GOAL_EVALUATION_SYSTEM_PROMPT } from "../constants/goalPrompts.js";
9
11
  // Global rate limiter state for 1 QPS
10
12
  let nextAllowedTime = 0;
11
13
  const MIN_INTERVAL = 1000; // 1 second for 1 QPS
@@ -63,8 +65,24 @@ function getModelConfig(modelName, baseConfig = {}) {
63
65
  }
64
66
  return config;
65
67
  }
68
+ function validateModelConfig(modelConfig) {
69
+ if (!modelConfig.model) {
70
+ throw new ConfigurationError(CONFIG_ERRORS.MISSING_MODEL, "model", {
71
+ constructor: undefined,
72
+ environment: process.env.WAVE_MODEL,
73
+ });
74
+ }
75
+ if (!modelConfig.fastModel) {
76
+ throw new ConfigurationError(CONFIG_ERRORS.MISSING_FAST_MODEL, "fastModel", {
77
+ constructor: undefined,
78
+ environment: process.env.WAVE_FAST_MODEL,
79
+ });
80
+ }
81
+ }
66
82
  export async function callAgent(options) {
67
83
  const { gatewayConfig, modelConfig, messages, abortSignal, workdir, tools, model, systemPrompt, onContentUpdate, onToolUpdate, onReasoningUpdate, } = options;
84
+ // Validate model config at call time
85
+ validateModelConfig(modelConfig);
68
86
  // Apply global 1 QPS rate limit
69
87
  if (process.env.NODE_ENV !== "test" ||
70
88
  modelConfig.model === "rate-limit-test") {
@@ -127,6 +145,10 @@ export async function callAgent(options) {
127
145
  if (processedTools && processedTools.length > 0) {
128
146
  createParams.tools = processedTools;
129
147
  }
148
+ // Add tool_choice if specified
149
+ if (options.toolChoice) {
150
+ createParams.tool_choice = options.toolChoice;
151
+ }
130
152
  if (isStreaming) {
131
153
  // Handle streaming response
132
154
  const { data: stream, response } = await openai.chat.completions
@@ -139,7 +161,7 @@ export async function callAgent(options) {
139
161
  response.headers.forEach((value, key) => {
140
162
  responseHeaders[key] = value;
141
163
  });
142
- return await processStreamingResponse(stream, onContentUpdate, onToolUpdate, onReasoningUpdate, abortSignal, responseHeaders, currentModel);
164
+ return await processStreamingResponse(stream, onContentUpdate, onToolUpdate, onReasoningUpdate, abortSignal, responseHeaders);
143
165
  }
144
166
  else {
145
167
  // Handle non-streaming response
@@ -162,8 +184,8 @@ export async function callAgent(options) {
162
184
  total_tokens: response.usage.total_tokens,
163
185
  }
164
186
  : undefined;
165
- // Extend usage with cache metrics for Claude models
166
- if (totalUsage && supportsPromptCaching(currentModel) && response.usage) {
187
+ // Extend usage with cache metrics (Claude top-level + OpenAI prompt_tokens_details)
188
+ if (totalUsage && response.usage) {
167
189
  totalUsage = extendUsageWithCacheMetrics(totalUsage, response.usage);
168
190
  }
169
191
  const result = {};
@@ -288,10 +310,9 @@ export async function callAgent(options) {
288
310
  * @param onToolUpdate Callback for tool updates
289
311
  * @param abortSignal Optional abort signal
290
312
  * @param responseHeaders Response headers from the initial request
291
- * @param modelName Model name for cache control processing
292
313
  * @returns Final result with accumulated content and tool calls
293
314
  */
294
- async function processStreamingResponse(stream, onContentUpdate, onToolUpdate, onReasoningUpdate, abortSignal, responseHeaders, modelName) {
315
+ async function processStreamingResponse(stream, onContentUpdate, onToolUpdate, onReasoningUpdate, abortSignal, responseHeaders) {
295
316
  let accumulatedContent = "";
296
317
  let accumulatedReasoningContent = "";
297
318
  let hasReasoningContent = false;
@@ -312,10 +333,8 @@ async function processStreamingResponse(stream, onContentUpdate, onToolUpdate, o
312
333
  completion_tokens: chunk.usage.completion_tokens,
313
334
  total_tokens: chunk.usage.total_tokens,
314
335
  };
315
- // Extend usage with cache metrics for Claude models
316
- if (modelName && supportsPromptCaching(modelName)) {
317
- chunkUsage = extendUsageWithCacheMetrics(chunkUsage, chunk.usage);
318
- }
336
+ // Extend usage with cache metrics (Claude top-level + OpenAI prompt_tokens_details)
337
+ chunkUsage = extendUsageWithCacheMetrics(chunkUsage, chunk.usage);
319
338
  usage = chunkUsage;
320
339
  }
321
340
  // Check for finish_reason in the choice
@@ -454,6 +473,8 @@ async function processStreamingResponse(stream, onContentUpdate, onToolUpdate, o
454
473
  }
455
474
  export async function compactMessages(options) {
456
475
  const { gatewayConfig, modelConfig, messages, abortSignal } = options;
476
+ // Validate model config at call time
477
+ validateModelConfig(modelConfig);
457
478
  // Apply global 1 QPS rate limit
458
479
  if (process.env.NODE_ENV !== "test" ||
459
480
  modelConfig.model === "rate-limit-test") {
@@ -534,6 +555,8 @@ export async function compactMessages(options) {
534
555
  }
535
556
  export async function processWebContent(options) {
536
557
  const { gatewayConfig, modelConfig, content, prompt, abortSignal } = options;
558
+ // Validate model config at call time
559
+ validateModelConfig(modelConfig);
537
560
  // Apply global 1 QPS rate limit
538
561
  if (process.env.NODE_ENV !== "test" ||
539
562
  modelConfig.model === "rate-limit-test") {
@@ -601,6 +624,8 @@ export async function processWebContent(options) {
601
624
  }
602
625
  export async function btw(options) {
603
626
  const { gatewayConfig, modelConfig, messages, question, abortSignal } = options;
627
+ // Validate model config at call time
628
+ validateModelConfig(modelConfig);
604
629
  // Apply global 1 QPS rate limit
605
630
  if (process.env.NODE_ENV !== "test" ||
606
631
  modelConfig.model === "rate-limit-test") {
@@ -667,3 +692,71 @@ export async function btw(options) {
667
692
  throw error;
668
693
  }
669
694
  }
695
+ export async function evaluateGoal(options) {
696
+ const { gatewayConfig, modelConfig, model, goalCondition, messages, abortSignal, } = options;
697
+ // Create OpenAI client with injected configuration (no rate limiter — bypasses 1 QPS)
698
+ const openai = new OpenAIClient({
699
+ apiKey: gatewayConfig.apiKey,
700
+ baseURL: gatewayConfig.baseURL,
701
+ defaultHeaders: gatewayConfig.defaultHeaders,
702
+ fetchOptions: gatewayConfig.fetchOptions,
703
+ fetch: gatewayConfig.fetch,
704
+ });
705
+ const { model: _model, fastModel: _fastModel, maxTokens: _maxTokens, permissionMode: _permissionMode, ...extraParams } = modelConfig;
706
+ void _model;
707
+ void _fastModel;
708
+ void _maxTokens;
709
+ void _permissionMode;
710
+ const openaiModelConfig = getModelConfig(model, {
711
+ temperature: 0,
712
+ max_tokens: 200,
713
+ ...extraParams,
714
+ });
715
+ // Strip images from messages to reduce token usage (same as compact)
716
+ const cleanedMessages = messages.map((msg) => {
717
+ if (Array.isArray(msg.content)) {
718
+ const textParts = msg.content.filter((part) => part.type === "text");
719
+ const text = textParts.map((p) => p.text).join("\n");
720
+ return { ...msg, content: text || "(empty message)" };
721
+ }
722
+ return msg;
723
+ });
724
+ try {
725
+ const response = await openai.chat.completions.create({
726
+ ...openaiModelConfig,
727
+ messages: [
728
+ {
729
+ role: "system",
730
+ content: GOAL_EVALUATION_SYSTEM_PROMPT,
731
+ },
732
+ ...cleanedMessages,
733
+ {
734
+ role: "user",
735
+ content: `Goal condition: ${goalCondition}\n\nHas this goal been achieved based on the conversation above?`,
736
+ },
737
+ ],
738
+ }, {
739
+ signal: abortSignal,
740
+ });
741
+ const result = response.choices[0]?.message?.content?.trim();
742
+ if (!result) {
743
+ throw new Error("Goal evaluation returned empty response");
744
+ }
745
+ const usage = response.usage
746
+ ? {
747
+ prompt_tokens: response.usage.prompt_tokens,
748
+ completion_tokens: response.usage.completion_tokens,
749
+ total_tokens: response.usage.total_tokens,
750
+ }
751
+ : undefined;
752
+ return { content: result, usage };
753
+ }
754
+ catch (error) {
755
+ if (error.name === "AbortError") {
756
+ logger.info("Goal evaluation was aborted");
757
+ throw new Error("Goal evaluation was aborted");
758
+ }
759
+ logger.error("Goal evaluation failed:", error);
760
+ throw error;
761
+ }
762
+ }