wave-agent-sdk 0.17.1 → 0.17.3
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.
- package/builtin/skills/deep-research/SKILL.md +90 -0
- package/builtin/skills/settings/ENV.md +6 -3
- package/dist/agent.d.ts +28 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +128 -34
- package/dist/constants/goalPrompts.d.ts +2 -0
- package/dist/constants/goalPrompts.d.ts.map +1 -0
- package/dist/constants/goalPrompts.js +10 -0
- package/dist/constants/tools.d.ts +1 -0
- package/dist/constants/tools.d.ts.map +1 -1
- package/dist/constants/tools.js +1 -0
- package/dist/managers/aiManager.d.ts +7 -0
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +77 -41
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
- package/dist/managers/backgroundTaskManager.js +10 -2
- package/dist/managers/goalManager.d.ts +43 -0
- package/dist/managers/goalManager.d.ts.map +1 -0
- package/dist/managers/goalManager.js +177 -0
- package/dist/managers/messageManager.d.ts +2 -2
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageQueue.d.ts +10 -0
- package/dist/managers/messageQueue.d.ts.map +1 -1
- package/dist/managers/messageQueue.js +53 -1
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +7 -1
- package/dist/managers/skillManager.d.ts +2 -0
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +19 -9
- package/dist/managers/slashCommandManager.d.ts +6 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +105 -0
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +5 -0
- package/dist/managers/workflowManager.d.ts +65 -0
- package/dist/managers/workflowManager.d.ts.map +1 -0
- package/dist/managers/workflowManager.js +380 -0
- package/dist/prompts/index.d.ts +2 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +3 -3
- package/dist/services/MarketplaceService.d.ts +2 -2
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +11 -32
- package/dist/services/aiService.d.ts +23 -0
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +102 -9
- package/dist/services/configurationService.d.ts +1 -1
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +3 -16
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +4 -0
- package/dist/services/session.d.ts +9 -1
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +28 -1
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +49 -7
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +1 -1
- package/dist/tools/taskManagementTools.d.ts.map +1 -1
- package/dist/tools/taskManagementTools.js +103 -157
- package/dist/tools/types.d.ts +2 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/webFetchTool.d.ts.map +1 -1
- package/dist/tools/webFetchTool.js +0 -9
- package/dist/tools/workflowTool.d.ts +11 -0
- package/dist/tools/workflowTool.d.ts.map +1 -0
- package/dist/tools/workflowTool.js +190 -0
- package/dist/types/agent.d.ts +2 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/commands.d.ts +4 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/config.d.ts +2 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/core.d.ts +1 -1
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/hooks.d.ts +2 -0
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/messaging.d.ts +2 -2
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/processes.d.ts +6 -2
- package/dist/types/processes.d.ts.map +1 -1
- package/dist/types/workflow.d.ts +2 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +1 -0
- package/dist/utils/cacheControlUtils.d.ts +13 -8
- package/dist/utils/cacheControlUtils.d.ts.map +1 -1
- package/dist/utils/cacheControlUtils.js +73 -102
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +7 -0
- package/dist/utils/markdownParser.d.ts.map +1 -1
- package/dist/utils/markdownParser.js +21 -6
- package/dist/utils/messageOperations.d.ts +2 -2
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/notificationXml.d.ts.map +1 -1
- package/dist/workflow/budgetTracker.d.ts +12 -0
- package/dist/workflow/budgetTracker.d.ts.map +1 -0
- package/dist/workflow/budgetTracker.js +30 -0
- package/dist/workflow/concurrencyLimiter.d.ts +14 -0
- package/dist/workflow/concurrencyLimiter.d.ts.map +1 -0
- package/dist/workflow/concurrencyLimiter.js +39 -0
- package/dist/workflow/journal.d.ts +19 -0
- package/dist/workflow/journal.d.ts.map +1 -0
- package/dist/workflow/journal.js +74 -0
- package/dist/workflow/progressReporter.d.ts +21 -0
- package/dist/workflow/progressReporter.d.ts.map +1 -0
- package/dist/workflow/progressReporter.js +118 -0
- package/dist/workflow/runState.d.ts +16 -0
- package/dist/workflow/runState.d.ts.map +1 -0
- package/dist/workflow/runState.js +57 -0
- package/dist/workflow/scriptRuntime.d.ts +35 -0
- package/dist/workflow/scriptRuntime.d.ts.map +1 -0
- package/dist/workflow/scriptRuntime.js +196 -0
- package/dist/workflow/structuredOutput.d.ts +27 -0
- package/dist/workflow/structuredOutput.d.ts.map +1 -0
- package/dist/workflow/structuredOutput.js +106 -0
- package/dist/workflow/types.d.ts +81 -0
- package/dist/workflow/types.d.ts.map +1 -0
- package/dist/workflow/types.js +1 -0
- package/dist/workflow/workflowApis.d.ts +46 -0
- package/dist/workflow/workflowApis.d.ts.map +1 -0
- package/dist/workflow/workflowApis.js +280 -0
- package/package.json +1 -1
- package/src/agent.ts +144 -34
- package/src/constants/goalPrompts.ts +10 -0
- package/src/constants/tools.ts +1 -0
- package/src/managers/aiManager.ts +91 -47
- package/src/managers/backgroundTaskManager.ts +16 -4
- package/src/managers/goalManager.ts +232 -0
- package/src/managers/messageManager.ts +2 -2
- package/src/managers/messageQueue.ts +59 -1
- package/src/managers/pluginManager.ts +8 -1
- package/src/managers/skillManager.ts +20 -9
- package/src/managers/slashCommandManager.ts +119 -0
- package/src/managers/toolManager.ts +7 -0
- package/src/managers/workflowManager.ts +491 -0
- package/src/prompts/index.ts +4 -2
- package/src/services/MarketplaceService.ts +14 -38
- package/src/services/aiService.ts +166 -12
- package/src/services/configurationService.ts +2 -22
- package/src/services/hook.ts +5 -0
- package/src/services/session.ts +42 -2
- package/src/tools/bashTool.ts +64 -9
- package/src/tools/readTool.ts +1 -2
- package/src/tools/taskManagementTools.ts +146 -195
- package/src/tools/types.ts +2 -0
- package/src/tools/webFetchTool.ts +0 -12
- package/src/tools/workflowTool.ts +205 -0
- package/src/types/agent.ts +6 -0
- package/src/types/commands.ts +4 -0
- package/src/types/config.ts +2 -2
- package/src/types/core.ts +3 -3
- package/src/types/hooks.ts +2 -0
- package/src/types/index.ts +1 -0
- package/src/types/messaging.ts +2 -2
- package/src/types/processes.ts +10 -2
- package/src/types/workflow.ts +5 -0
- package/src/utils/cacheControlUtils.ts +106 -131
- package/src/utils/containerSetup.ts +9 -0
- package/src/utils/markdownParser.ts +26 -8
- package/src/utils/messageOperations.ts +2 -2
- package/src/utils/notificationXml.ts +6 -1
- package/src/workflow/budgetTracker.ts +34 -0
- package/src/workflow/concurrencyLimiter.ts +47 -0
- package/src/workflow/journal.ts +95 -0
- package/src/workflow/progressReporter.ts +141 -0
- package/src/workflow/runState.ts +65 -0
- package/src/workflow/scriptRuntime.ts +274 -0
- package/src/workflow/structuredOutput.ts +123 -0
- package/src/workflow/types.ts +95 -0
- package/src/workflow/workflowApis.ts +412 -0
package/src/prompts/index.ts
CHANGED
|
@@ -236,6 +236,7 @@ export function buildSystemPrompt(
|
|
|
236
236
|
tools: ToolPlugin[],
|
|
237
237
|
options: {
|
|
238
238
|
workdir?: string;
|
|
239
|
+
originalWorkdir?: string;
|
|
239
240
|
memory?: string;
|
|
240
241
|
language?: string;
|
|
241
242
|
isSubagent?: boolean;
|
|
@@ -283,7 +284,7 @@ export function buildSystemPrompt(
|
|
|
283
284
|
|
|
284
285
|
Here is useful information about the environment you are running in:
|
|
285
286
|
<env>
|
|
286
|
-
|
|
287
|
+
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}.` : ""}
|
|
287
288
|
Is directory a git repo: ${isGitRepo}
|
|
288
289
|
Platform: ${platform}
|
|
289
290
|
Shell: ${shellName}
|
|
@@ -310,6 +311,7 @@ Today's date: ${today}
|
|
|
310
311
|
export function enhanceSystemPromptWithEnvDetails(
|
|
311
312
|
existingSystemPrompt: string,
|
|
312
313
|
workdir: string,
|
|
314
|
+
originalWorkdir?: string,
|
|
313
315
|
): string {
|
|
314
316
|
const isGitRepo = isGitRepository(workdir);
|
|
315
317
|
const platform = os.platform();
|
|
@@ -336,7 +338,7 @@ ${notes}
|
|
|
336
338
|
|
|
337
339
|
Here is useful information about the environment you are running in:
|
|
338
340
|
<env>
|
|
339
|
-
|
|
341
|
+
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}.` : ""}
|
|
340
342
|
Is directory a git repo: ${isGitRepo}
|
|
341
343
|
Platform: ${platform}
|
|
342
344
|
Shell: ${shellName}
|
|
@@ -217,33 +217,20 @@ export class MarketplaceService {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
/**
|
|
220
|
-
* Legacy method: loads known marketplaces
|
|
220
|
+
* Legacy method: loads known marketplaces from cache.
|
|
221
221
|
* @deprecated Use listMarketplaces() instead, which combines scoped settings.
|
|
222
222
|
*/
|
|
223
223
|
async getKnownMarketplaces(): Promise<KnownMarketplacesRegistry> {
|
|
224
224
|
const cache = await this.getCacheRegistry();
|
|
225
|
-
// If cache is null (file doesn't exist or has no parseable content), inject builtin
|
|
226
225
|
if (cache === null) {
|
|
227
|
-
return {
|
|
228
|
-
marketplaces: [
|
|
229
|
-
{
|
|
230
|
-
...MarketplaceService.BUILTIN_MARKETPLACE,
|
|
231
|
-
isBuiltin: true,
|
|
232
|
-
declaredScope: "builtin",
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
};
|
|
226
|
+
return { marketplaces: [] };
|
|
236
227
|
}
|
|
237
|
-
// File has valid JSON - respect user's explicit choice even if empty
|
|
238
|
-
const hasBuiltin = cache.marketplaces.some(
|
|
239
|
-
(m) => m.name === MarketplaceService.BUILTIN_MARKETPLACE.name,
|
|
240
|
-
);
|
|
241
228
|
return {
|
|
242
229
|
marketplaces: cache.marketplaces.map((m) => ({
|
|
243
230
|
...m,
|
|
244
231
|
isBuiltin:
|
|
245
232
|
m.isBuiltin || m.name === MarketplaceService.BUILTIN_MARKETPLACE.name,
|
|
246
|
-
declaredScope: m.declaredScope ??
|
|
233
|
+
declaredScope: m.declaredScope ?? "user",
|
|
247
234
|
})),
|
|
248
235
|
};
|
|
249
236
|
}
|
|
@@ -385,8 +372,6 @@ export class MarketplaceService {
|
|
|
385
372
|
* Finds which scope declared a marketplace (user, project, local, or builtin)
|
|
386
373
|
*/
|
|
387
374
|
getMarketplaceDeclaringSource(name: string): Scope | "builtin" | null {
|
|
388
|
-
if (name === MarketplaceService.BUILTIN_MARKETPLACE.name) return "builtin";
|
|
389
|
-
|
|
390
375
|
const scopes: Scope[] = ["local", "project", "user"];
|
|
391
376
|
for (const scope of scopes) {
|
|
392
377
|
const scoped = this.configurationService.getScopedMarketplaces(
|
|
@@ -395,6 +380,7 @@ export class MarketplaceService {
|
|
|
395
380
|
);
|
|
396
381
|
if (scoped[name]) return scope;
|
|
397
382
|
}
|
|
383
|
+
if (name === MarketplaceService.BUILTIN_MARKETPLACE.name) return "builtin";
|
|
398
384
|
return null;
|
|
399
385
|
}
|
|
400
386
|
|
|
@@ -503,7 +489,7 @@ export class MarketplaceService {
|
|
|
503
489
|
}
|
|
504
490
|
|
|
505
491
|
/**
|
|
506
|
-
* Lists all registered marketplaces by combining scoped settings +
|
|
492
|
+
* Lists all registered marketplaces by combining scoped settings + cache
|
|
507
493
|
*/
|
|
508
494
|
async listMarketplaces(): Promise<KnownMarketplace[]> {
|
|
509
495
|
const scopedMarketplaces = this.configurationService.getMergedMarketplaces(
|
|
@@ -516,16 +502,8 @@ export class MarketplaceService {
|
|
|
516
502
|
|
|
517
503
|
const result: KnownMarketplace[] = [];
|
|
518
504
|
|
|
519
|
-
// Add built-in marketplace
|
|
520
|
-
result.push({
|
|
521
|
-
...MarketplaceService.BUILTIN_MARKETPLACE,
|
|
522
|
-
isBuiltin: true,
|
|
523
|
-
declaredScope: "builtin",
|
|
524
|
-
});
|
|
525
|
-
|
|
526
505
|
// Add all scoped marketplaces (local overrides project overrides user)
|
|
527
506
|
for (const [name, config] of Object.entries(scopedMarketplaces)) {
|
|
528
|
-
if (name === MarketplaceService.BUILTIN_MARKETPLACE.name) continue;
|
|
529
507
|
const cache = cacheMap.get(name);
|
|
530
508
|
const declaredScope = this.getMarketplaceDeclaringSource(name) as
|
|
531
509
|
| "user"
|
|
@@ -538,12 +516,12 @@ export class MarketplaceService {
|
|
|
538
516
|
|
|
539
517
|
// Add cache entries not yet in scoped settings (backwards compatibility)
|
|
540
518
|
for (const [name, cache] of cacheMap.entries()) {
|
|
541
|
-
if (name === MarketplaceService.BUILTIN_MARKETPLACE.name) continue;
|
|
542
519
|
if (scopedMarketplaces[name]) continue;
|
|
520
|
+
const isBuiltin = name === MarketplaceService.BUILTIN_MARKETPLACE.name;
|
|
543
521
|
result.push({
|
|
544
522
|
...cache,
|
|
545
|
-
isBuiltin
|
|
546
|
-
declaredScope: cache.declaredScope ?? "user",
|
|
523
|
+
isBuiltin,
|
|
524
|
+
declaredScope: cache.declaredScope ?? (isBuiltin ? "builtin" : "user"),
|
|
547
525
|
});
|
|
548
526
|
}
|
|
549
527
|
|
|
@@ -558,16 +536,14 @@ export class MarketplaceService {
|
|
|
558
536
|
const targetScope =
|
|
559
537
|
scope || this.getMarketplaceDeclaringSource(name) || "user";
|
|
560
538
|
|
|
561
|
-
if (targetScope
|
|
562
|
-
|
|
539
|
+
if (targetScope !== "builtin") {
|
|
540
|
+
await this.configurationService.removeMarketplaceFromScope(
|
|
541
|
+
this.workdir,
|
|
542
|
+
targetScope,
|
|
543
|
+
name,
|
|
544
|
+
);
|
|
563
545
|
}
|
|
564
546
|
|
|
565
|
-
await this.configurationService.removeMarketplaceFromScope(
|
|
566
|
-
this.workdir,
|
|
567
|
-
targetScope,
|
|
568
|
-
name,
|
|
569
|
-
);
|
|
570
|
-
|
|
571
547
|
// Also remove from cache
|
|
572
548
|
await this.removeFromCache(name);
|
|
573
549
|
});
|
|
@@ -10,6 +10,7 @@ import { OpenAIClient } from "../utils/openaiClient.js";
|
|
|
10
10
|
import { logger } from "../utils/globalLogger.js";
|
|
11
11
|
import { addOnceAbortListener } from "../utils/abortUtils.js";
|
|
12
12
|
import type { GatewayConfig, ModelConfig } from "../types/index.js";
|
|
13
|
+
import { ConfigurationError, CONFIG_ERRORS } from "../types/index.js";
|
|
13
14
|
import {
|
|
14
15
|
transformMessagesForClaudeCache,
|
|
15
16
|
addCacheControlToLastTool,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
WEB_CONTENT_SYSTEM_PROMPT,
|
|
28
29
|
BTW_SYSTEM_PROMPT,
|
|
29
30
|
} from "../prompts/index.js";
|
|
31
|
+
import { GOAL_EVALUATION_SYSTEM_PROMPT } from "../constants/goalPrompts.js";
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* Interface for debug data saved during 400 errors
|
|
@@ -160,6 +162,11 @@ export interface CallAgentOptions {
|
|
|
160
162
|
model?: string; // Custom model
|
|
161
163
|
systemPrompt?: string; // Custom system prompt
|
|
162
164
|
maxTokens?: number; // Maximum output tokens
|
|
165
|
+
toolChoice?:
|
|
166
|
+
| "auto"
|
|
167
|
+
| "none"
|
|
168
|
+
| "required"
|
|
169
|
+
| { type: "function"; function: { name: string } }; // Force tool selection
|
|
163
170
|
|
|
164
171
|
// NEW: Streaming callbacks
|
|
165
172
|
onContentUpdate?: (content: string) => void;
|
|
@@ -189,6 +196,27 @@ export interface CallAgentResult {
|
|
|
189
196
|
additionalFields?: Record<string, unknown>;
|
|
190
197
|
}
|
|
191
198
|
|
|
199
|
+
function validateModelConfig(
|
|
200
|
+
modelConfig: ModelConfig,
|
|
201
|
+
): asserts modelConfig is ModelConfig & { model: string; fastModel: string } {
|
|
202
|
+
if (!modelConfig.model) {
|
|
203
|
+
throw new ConfigurationError(CONFIG_ERRORS.MISSING_MODEL, "model", {
|
|
204
|
+
constructor: undefined,
|
|
205
|
+
environment: process.env.WAVE_MODEL,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
if (!modelConfig.fastModel) {
|
|
209
|
+
throw new ConfigurationError(
|
|
210
|
+
CONFIG_ERRORS.MISSING_FAST_MODEL,
|
|
211
|
+
"fastModel",
|
|
212
|
+
{
|
|
213
|
+
constructor: undefined,
|
|
214
|
+
environment: process.env.WAVE_FAST_MODEL,
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
192
220
|
export async function callAgent(
|
|
193
221
|
options: CallAgentOptions,
|
|
194
222
|
): Promise<CallAgentResult> {
|
|
@@ -206,6 +234,9 @@ export async function callAgent(
|
|
|
206
234
|
onReasoningUpdate,
|
|
207
235
|
} = options;
|
|
208
236
|
|
|
237
|
+
// Validate model config at call time
|
|
238
|
+
validateModelConfig(modelConfig);
|
|
239
|
+
|
|
209
240
|
// Apply global 1 QPS rate limit
|
|
210
241
|
if (
|
|
211
242
|
process.env.NODE_ENV !== "test" ||
|
|
@@ -301,6 +332,11 @@ export async function callAgent(
|
|
|
301
332
|
createParams.tools = processedTools;
|
|
302
333
|
}
|
|
303
334
|
|
|
335
|
+
// Add tool_choice if specified
|
|
336
|
+
if (options.toolChoice) {
|
|
337
|
+
createParams.tool_choice = options.toolChoice;
|
|
338
|
+
}
|
|
339
|
+
|
|
304
340
|
if (isStreaming) {
|
|
305
341
|
// Handle streaming response
|
|
306
342
|
const { data: stream, response } = await openai.chat.completions
|
|
@@ -322,7 +358,6 @@ export async function callAgent(
|
|
|
322
358
|
onReasoningUpdate,
|
|
323
359
|
abortSignal,
|
|
324
360
|
responseHeaders,
|
|
325
|
-
currentModel,
|
|
326
361
|
);
|
|
327
362
|
} else {
|
|
328
363
|
// Handle non-streaming response
|
|
@@ -350,8 +385,8 @@ export async function callAgent(
|
|
|
350
385
|
}
|
|
351
386
|
: undefined;
|
|
352
387
|
|
|
353
|
-
// Extend usage with cache metrics
|
|
354
|
-
if (totalUsage &&
|
|
388
|
+
// Extend usage with cache metrics (Claude top-level + OpenAI prompt_tokens_details)
|
|
389
|
+
if (totalUsage && response.usage) {
|
|
355
390
|
totalUsage = extendUsageWithCacheMetrics(
|
|
356
391
|
totalUsage,
|
|
357
392
|
response.usage as Partial<ClaudeUsage>,
|
|
@@ -521,7 +556,6 @@ export async function callAgent(
|
|
|
521
556
|
* @param onToolUpdate Callback for tool updates
|
|
522
557
|
* @param abortSignal Optional abort signal
|
|
523
558
|
* @param responseHeaders Response headers from the initial request
|
|
524
|
-
* @param modelName Model name for cache control processing
|
|
525
559
|
* @returns Final result with accumulated content and tool calls
|
|
526
560
|
*/
|
|
527
561
|
async function processStreamingResponse(
|
|
@@ -537,7 +571,6 @@ async function processStreamingResponse(
|
|
|
537
571
|
onReasoningUpdate?: (content: string) => void,
|
|
538
572
|
abortSignal?: AbortSignal,
|
|
539
573
|
responseHeaders?: Record<string, string>,
|
|
540
|
-
modelName?: string,
|
|
541
574
|
): Promise<CallAgentResult> {
|
|
542
575
|
let accumulatedContent = "";
|
|
543
576
|
let accumulatedReasoningContent = "";
|
|
@@ -569,13 +602,11 @@ async function processStreamingResponse(
|
|
|
569
602
|
total_tokens: chunk.usage.total_tokens,
|
|
570
603
|
};
|
|
571
604
|
|
|
572
|
-
// Extend usage with cache metrics
|
|
573
|
-
|
|
574
|
-
chunkUsage
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
);
|
|
578
|
-
}
|
|
605
|
+
// Extend usage with cache metrics (Claude top-level + OpenAI prompt_tokens_details)
|
|
606
|
+
chunkUsage = extendUsageWithCacheMetrics(
|
|
607
|
+
chunkUsage,
|
|
608
|
+
chunk.usage as Partial<ClaudeUsage>,
|
|
609
|
+
);
|
|
579
610
|
|
|
580
611
|
usage = chunkUsage;
|
|
581
612
|
}
|
|
@@ -775,6 +806,9 @@ export async function compactMessages(
|
|
|
775
806
|
): Promise<CompactMessagesResult> {
|
|
776
807
|
const { gatewayConfig, modelConfig, messages, abortSignal } = options;
|
|
777
808
|
|
|
809
|
+
// Validate model config at call time
|
|
810
|
+
validateModelConfig(modelConfig);
|
|
811
|
+
|
|
778
812
|
// Apply global 1 QPS rate limit
|
|
779
813
|
if (
|
|
780
814
|
process.env.NODE_ENV !== "test" ||
|
|
@@ -901,6 +935,9 @@ export async function processWebContent(
|
|
|
901
935
|
): Promise<ProcessWebContentResult> {
|
|
902
936
|
const { gatewayConfig, modelConfig, content, prompt, abortSignal } = options;
|
|
903
937
|
|
|
938
|
+
// Validate model config at call time
|
|
939
|
+
validateModelConfig(modelConfig);
|
|
940
|
+
|
|
904
941
|
// Apply global 1 QPS rate limit
|
|
905
942
|
if (
|
|
906
943
|
process.env.NODE_ENV !== "test" ||
|
|
@@ -1008,6 +1045,9 @@ export async function btw(options: BtwOptions): Promise<BtwResult> {
|
|
|
1008
1045
|
const { gatewayConfig, modelConfig, messages, question, abortSignal } =
|
|
1009
1046
|
options;
|
|
1010
1047
|
|
|
1048
|
+
// Validate model config at call time
|
|
1049
|
+
validateModelConfig(modelConfig);
|
|
1050
|
+
|
|
1011
1051
|
// Apply global 1 QPS rate limit
|
|
1012
1052
|
if (
|
|
1013
1053
|
process.env.NODE_ENV !== "test" ||
|
|
@@ -1092,3 +1132,117 @@ export async function btw(options: BtwOptions): Promise<BtwResult> {
|
|
|
1092
1132
|
throw error;
|
|
1093
1133
|
}
|
|
1094
1134
|
}
|
|
1135
|
+
|
|
1136
|
+
export interface EvaluateGoalOptions {
|
|
1137
|
+
gatewayConfig: GatewayConfig;
|
|
1138
|
+
modelConfig: ModelConfig;
|
|
1139
|
+
model: string;
|
|
1140
|
+
goalCondition: string;
|
|
1141
|
+
messages: ChatCompletionMessageParam[];
|
|
1142
|
+
abortSignal?: AbortSignal;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
export interface EvaluateGoalResult {
|
|
1146
|
+
content: string;
|
|
1147
|
+
usage?: {
|
|
1148
|
+
prompt_tokens: number;
|
|
1149
|
+
completion_tokens: number;
|
|
1150
|
+
total_tokens: number;
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
export async function evaluateGoal(
|
|
1155
|
+
options: EvaluateGoalOptions,
|
|
1156
|
+
): Promise<EvaluateGoalResult> {
|
|
1157
|
+
const {
|
|
1158
|
+
gatewayConfig,
|
|
1159
|
+
modelConfig,
|
|
1160
|
+
model,
|
|
1161
|
+
goalCondition,
|
|
1162
|
+
messages,
|
|
1163
|
+
abortSignal,
|
|
1164
|
+
} = options;
|
|
1165
|
+
|
|
1166
|
+
// Create OpenAI client with injected configuration (no rate limiter — bypasses 1 QPS)
|
|
1167
|
+
const openai = new OpenAIClient({
|
|
1168
|
+
apiKey: gatewayConfig.apiKey,
|
|
1169
|
+
baseURL: gatewayConfig.baseURL,
|
|
1170
|
+
defaultHeaders: gatewayConfig.defaultHeaders,
|
|
1171
|
+
fetchOptions: gatewayConfig.fetchOptions,
|
|
1172
|
+
fetch: gatewayConfig.fetch,
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
const {
|
|
1176
|
+
model: _model,
|
|
1177
|
+
fastModel: _fastModel,
|
|
1178
|
+
maxTokens: _maxTokens,
|
|
1179
|
+
permissionMode: _permissionMode,
|
|
1180
|
+
...extraParams
|
|
1181
|
+
} = modelConfig;
|
|
1182
|
+
void _model;
|
|
1183
|
+
void _fastModel;
|
|
1184
|
+
void _maxTokens;
|
|
1185
|
+
void _permissionMode;
|
|
1186
|
+
|
|
1187
|
+
const openaiModelConfig = getModelConfig(model, {
|
|
1188
|
+
temperature: 0,
|
|
1189
|
+
max_tokens: 200,
|
|
1190
|
+
...extraParams,
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
// Strip images from messages to reduce token usage (same as compact)
|
|
1194
|
+
const cleanedMessages = messages.map((msg) => {
|
|
1195
|
+
if (Array.isArray(msg.content)) {
|
|
1196
|
+
const textParts = msg.content.filter(
|
|
1197
|
+
(part) => part.type === "text",
|
|
1198
|
+
) as import("openai/resources.js").ChatCompletionContentPartText[];
|
|
1199
|
+
const text = textParts.map((p) => p.text).join("\n");
|
|
1200
|
+
return { ...msg, content: text || "(empty message)" };
|
|
1201
|
+
}
|
|
1202
|
+
return msg;
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
try {
|
|
1206
|
+
const response = await openai.chat.completions.create(
|
|
1207
|
+
{
|
|
1208
|
+
...openaiModelConfig,
|
|
1209
|
+
messages: [
|
|
1210
|
+
{
|
|
1211
|
+
role: "system",
|
|
1212
|
+
content: GOAL_EVALUATION_SYSTEM_PROMPT,
|
|
1213
|
+
},
|
|
1214
|
+
...cleanedMessages,
|
|
1215
|
+
{
|
|
1216
|
+
role: "user",
|
|
1217
|
+
content: `Goal condition: ${goalCondition}\n\nHas this goal been achieved based on the conversation above?`,
|
|
1218
|
+
},
|
|
1219
|
+
],
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
signal: abortSignal,
|
|
1223
|
+
},
|
|
1224
|
+
);
|
|
1225
|
+
|
|
1226
|
+
const result = response.choices[0]?.message?.content?.trim();
|
|
1227
|
+
if (!result) {
|
|
1228
|
+
throw new Error("Goal evaluation returned empty response");
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
const usage = response.usage
|
|
1232
|
+
? {
|
|
1233
|
+
prompt_tokens: response.usage.prompt_tokens,
|
|
1234
|
+
completion_tokens: response.usage.completion_tokens,
|
|
1235
|
+
total_tokens: response.usage.total_tokens,
|
|
1236
|
+
}
|
|
1237
|
+
: undefined;
|
|
1238
|
+
|
|
1239
|
+
return { content: result, usage };
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
if ((error as Error).name === "AbortError") {
|
|
1242
|
+
logger.info("Goal evaluation was aborted");
|
|
1243
|
+
throw new Error("Goal evaluation was aborted");
|
|
1244
|
+
}
|
|
1245
|
+
logger.error("Goal evaluation failed:", error);
|
|
1246
|
+
throw error;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
@@ -33,8 +33,6 @@ import {
|
|
|
33
33
|
import {
|
|
34
34
|
GatewayConfig,
|
|
35
35
|
ModelConfig,
|
|
36
|
-
ConfigurationError,
|
|
37
|
-
CONFIG_ERRORS,
|
|
38
36
|
PermissionMode,
|
|
39
37
|
AgentOptions,
|
|
40
38
|
} from "../types/index.js";
|
|
@@ -410,7 +408,7 @@ export class ConfigurationService {
|
|
|
410
408
|
* @param fetchOptions - Fetch options override (optional)
|
|
411
409
|
* @param fetch - Custom fetch implementation override (optional)
|
|
412
410
|
* @returns Resolved gateway configuration
|
|
413
|
-
* @
|
|
411
|
+
* @returns Resolved model configuration (model/fastModel may be undefined if not yet configured)
|
|
414
412
|
*/
|
|
415
413
|
resolveGatewayConfig(
|
|
416
414
|
apiKey?: string,
|
|
@@ -524,25 +522,6 @@ export class ConfigurationService {
|
|
|
524
522
|
const resolvedFastModel =
|
|
525
523
|
fastModel || this.options.fastModel || process.env.WAVE_FAST_MODEL;
|
|
526
524
|
|
|
527
|
-
// Validate required fields
|
|
528
|
-
if (!resolvedAgentModel) {
|
|
529
|
-
throw new ConfigurationError(CONFIG_ERRORS.MISSING_MODEL, "model", {
|
|
530
|
-
constructor: model,
|
|
531
|
-
environment: process.env.WAVE_MODEL,
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
if (!resolvedFastModel) {
|
|
536
|
-
throw new ConfigurationError(
|
|
537
|
-
CONFIG_ERRORS.MISSING_FAST_MODEL,
|
|
538
|
-
"fastModel",
|
|
539
|
-
{
|
|
540
|
-
constructor: fastModel,
|
|
541
|
-
environment: process.env.WAVE_FAST_MODEL,
|
|
542
|
-
},
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
525
|
// Resolve max output tokens
|
|
547
526
|
const resolvedMaxTokens = this.resolveMaxOutputTokens(maxTokens);
|
|
548
527
|
|
|
@@ -555,6 +534,7 @@ export class ConfigurationService {
|
|
|
555
534
|
|
|
556
535
|
// Merge model-specific settings from configuration
|
|
557
536
|
const modelSpecificConfig =
|
|
537
|
+
resolvedAgentModel &&
|
|
558
538
|
this.currentConfiguration?.models?.[resolvedAgentModel];
|
|
559
539
|
|
|
560
540
|
if (modelSpecificConfig) {
|
package/src/services/hook.ts
CHANGED
|
@@ -72,6 +72,11 @@ async function buildHookJsonInput(
|
|
|
72
72
|
jsonInput.user_prompt = context.userPrompt;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// Add plan_file_path if present
|
|
76
|
+
if (context.planFilePath !== undefined) {
|
|
77
|
+
jsonInput.plan_file_path = context.planFilePath;
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
// Add subagent_type if present
|
|
76
81
|
if (context.subagentType !== undefined) {
|
|
77
82
|
jsonInput.subagent_type = context.subagentType;
|
package/src/services/session.ts
CHANGED
|
@@ -45,6 +45,7 @@ export interface SessionMetadata {
|
|
|
45
45
|
sessionType: "main" | "subagent";
|
|
46
46
|
subagentType?: string;
|
|
47
47
|
workdir: string;
|
|
48
|
+
createdAt: Date;
|
|
48
49
|
lastActiveAt: Date;
|
|
49
50
|
latestTotalTokens: number;
|
|
50
51
|
firstMessage?: string;
|
|
@@ -53,7 +54,10 @@ export interface SessionMetadata {
|
|
|
53
54
|
export interface SessionIndex {
|
|
54
55
|
sessions: Record<
|
|
55
56
|
string,
|
|
56
|
-
Omit<SessionMetadata, "id" | "lastActiveAt"> & {
|
|
57
|
+
Omit<SessionMetadata, "id" | "lastActiveAt" | "createdAt"> & {
|
|
58
|
+
lastActiveAt: string;
|
|
59
|
+
createdAt: string;
|
|
60
|
+
}
|
|
57
61
|
>;
|
|
58
62
|
lastUpdated: string;
|
|
59
63
|
}
|
|
@@ -77,6 +81,27 @@ export function generateSessionId(): string {
|
|
|
77
81
|
return `${ts}-${shortId}`;
|
|
78
82
|
}
|
|
79
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Parse the timestamp prefix from a session ID back into a Date
|
|
86
|
+
* Format: {YYYYMMDDHHmmss}-{8hex} (e.g. 20260527143025-a1b2c3d4)
|
|
87
|
+
* @returns Date from the session ID timestamp prefix
|
|
88
|
+
*/
|
|
89
|
+
export function parseSessionIdTimestamp(sessionId: string): Date {
|
|
90
|
+
const match = sessionId.match(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/);
|
|
91
|
+
if (!match) {
|
|
92
|
+
return new Date(); // Fallback to now for legacy UUID-format session IDs
|
|
93
|
+
}
|
|
94
|
+
const [, year, month, day, hour, minute, second] = match;
|
|
95
|
+
return new Date(
|
|
96
|
+
Number(year),
|
|
97
|
+
Number(month) - 1,
|
|
98
|
+
Number(day),
|
|
99
|
+
Number(hour),
|
|
100
|
+
Number(minute),
|
|
101
|
+
Number(second),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
80
105
|
/**
|
|
81
106
|
* Generate filename for subagent sessions
|
|
82
107
|
* @param sessionId - UUID session identifier
|
|
@@ -114,6 +139,7 @@ async function updateSessionIndex(
|
|
|
114
139
|
const { id, ...rest } = metadata;
|
|
115
140
|
index.sessions[id] = {
|
|
116
141
|
...rest,
|
|
142
|
+
createdAt: metadata.createdAt.toISOString(),
|
|
117
143
|
lastActiveAt: metadata.lastActiveAt.toISOString(),
|
|
118
144
|
firstMessage: metadata.firstMessage || index.sessions[id]?.firstMessage,
|
|
119
145
|
parentSessionId: metadata.parentSessionId,
|
|
@@ -247,8 +273,9 @@ export async function appendMessages(
|
|
|
247
273
|
const projectDir = await encoder.getProjectDirectory(workdir, SESSION_DIR);
|
|
248
274
|
const lastMessage = newMessages[newMessages.length - 1];
|
|
249
275
|
|
|
250
|
-
// Get first message content
|
|
276
|
+
// Get first message content and createdAt from existing index
|
|
251
277
|
let firstMessage: string | undefined;
|
|
278
|
+
let createdAt: Date | undefined;
|
|
252
279
|
try {
|
|
253
280
|
const indexPath = join(projectDir.encodedPath, SESSION_INDEX_FILENAME);
|
|
254
281
|
const content = await fs.readFile(indexPath, "utf8");
|
|
@@ -257,18 +284,27 @@ export async function appendMessages(
|
|
|
257
284
|
firstMessage =
|
|
258
285
|
(await getFirstMessageContent(sessionId, workdir)) || undefined;
|
|
259
286
|
}
|
|
287
|
+
if (index.sessions[sessionId]?.createdAt) {
|
|
288
|
+
createdAt = new Date(index.sessions[sessionId].createdAt);
|
|
289
|
+
}
|
|
260
290
|
} catch {
|
|
261
291
|
// If index doesn't exist, this might be the first message
|
|
262
292
|
firstMessage =
|
|
263
293
|
(await getFirstMessageContent(sessionId, workdir)) || undefined;
|
|
264
294
|
}
|
|
265
295
|
|
|
296
|
+
// Derive createdAt from session ID timestamp if not in index
|
|
297
|
+
if (!createdAt) {
|
|
298
|
+
createdAt = new Date(parseSessionIdTimestamp(sessionId));
|
|
299
|
+
}
|
|
300
|
+
|
|
266
301
|
await updateSessionIndex(projectDir.encodedPath, {
|
|
267
302
|
id: sessionId,
|
|
268
303
|
rootSessionId,
|
|
269
304
|
parentSessionId,
|
|
270
305
|
sessionType,
|
|
271
306
|
workdir,
|
|
307
|
+
createdAt,
|
|
272
308
|
lastActiveAt: new Date(lastMessage.timestamp),
|
|
273
309
|
latestTotalTokens: lastMessage.usage
|
|
274
310
|
? extractLatestTotalTokens([lastMessage])
|
|
@@ -447,6 +483,7 @@ export async function listSessionsFromJsonl(
|
|
|
447
483
|
.map(([id, meta]) => ({
|
|
448
484
|
id,
|
|
449
485
|
...meta,
|
|
486
|
+
createdAt: new Date(meta.createdAt),
|
|
450
487
|
lastActiveAt: new Date(meta.lastActiveAt),
|
|
451
488
|
}));
|
|
452
489
|
|
|
@@ -520,6 +557,7 @@ export async function listSessionsFromJsonl(
|
|
|
520
557
|
sessionType: "main",
|
|
521
558
|
subagentType: undefined, // No longer stored in metadata
|
|
522
559
|
workdir: projectDir.originalPath,
|
|
560
|
+
createdAt: new Date(parseSessionIdTimestamp(sessionId)),
|
|
523
561
|
lastActiveAt,
|
|
524
562
|
latestTotalTokens: lastMessage?.usage
|
|
525
563
|
? extractLatestTotalTokens([lastMessage])
|
|
@@ -558,6 +596,7 @@ export async function listSessionsFromJsonl(
|
|
|
558
596
|
const { id, ...rest } = session;
|
|
559
597
|
index.sessions[id] = {
|
|
560
598
|
...rest,
|
|
599
|
+
createdAt: session.createdAt.toISOString(),
|
|
561
600
|
lastActiveAt: session.lastActiveAt.toISOString(),
|
|
562
601
|
};
|
|
563
602
|
}
|
|
@@ -613,6 +652,7 @@ export async function listAllSessions(): Promise<SessionMetadata[]> {
|
|
|
613
652
|
allSessions.push({
|
|
614
653
|
id,
|
|
615
654
|
...meta,
|
|
655
|
+
createdAt: new Date(meta.createdAt),
|
|
616
656
|
lastActiveAt,
|
|
617
657
|
});
|
|
618
658
|
}
|