wave-agent-sdk 0.18.6 → 0.19.0
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/settings/ENV.md +0 -1
- package/builtin/skills/settings/HOOKS.md +10 -0
- package/builtin/skills/settings/MODELS.md +37 -0
- package/builtin/skills/settings/SKILLS.md +6 -4
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +12 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +31 -8
- package/dist/managers/cronManager.d.ts.map +1 -1
- package/dist/managers/cronManager.js +2 -0
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/liveConfigManager.js +0 -9
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/mcpManager.js +13 -3
- package/dist/managers/messageManager.d.ts +4 -6
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +27 -81
- package/dist/managers/skillManager.d.ts +1 -0
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +11 -2
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +2 -0
- package/dist/prompts/index.d.ts +3 -2
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +5 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +13 -9
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +24 -9
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +6 -0
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +1 -1
- package/dist/services/interactionService.js +1 -1
- package/dist/services/reversionService.d.ts +5 -1
- package/dist/services/reversionService.d.ts.map +1 -1
- package/dist/services/reversionService.js +50 -22
- package/dist/services/session.d.ts +4 -13
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +59 -215
- package/dist/telemetry/sessionTracing.d.ts +31 -20
- package/dist/telemetry/sessionTracing.d.ts.map +1 -1
- package/dist/telemetry/sessionTracing.js +192 -62
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +16 -1
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +6 -7
- package/dist/types/config.d.ts +7 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/hooks.d.ts +1 -0
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +0 -1
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/skills.d.ts +1 -0
- package/dist/types/skills.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +16 -1
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/utils/atomicWrite.d.ts +15 -0
- package/dist/utils/atomicWrite.d.ts.map +1 -0
- package/dist/utils/atomicWrite.js +59 -0
- package/dist/utils/cacheControlUtils.d.ts +3 -15
- package/dist/utils/cacheControlUtils.d.ts.map +1 -1
- package/dist/utils/cacheControlUtils.js +4 -35
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +5 -1
- package/dist/utils/convertMessagesForAPI.d.ts +9 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +74 -55
- package/dist/utils/fileUtils.d.ts +0 -6
- package/dist/utils/fileUtils.d.ts.map +1 -1
- package/dist/utils/fileUtils.js +0 -43
- package/dist/utils/gitUtils.d.ts +11 -0
- package/dist/utils/gitUtils.d.ts.map +1 -1
- package/dist/utils/gitUtils.js +51 -0
- package/dist/utils/groupMessagesByApiRound.d.ts.map +1 -1
- package/dist/utils/groupMessagesByApiRound.js +7 -6
- package/dist/utils/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +2 -1
- package/dist/utils/modelCapabilities.d.ts +22 -0
- package/dist/utils/modelCapabilities.d.ts.map +1 -0
- package/dist/utils/modelCapabilities.js +24 -0
- package/dist/utils/skillParser.d.ts.map +1 -1
- package/dist/utils/skillParser.js +3 -1
- package/dist/utils/tokenEstimate.d.ts +24 -0
- package/dist/utils/tokenEstimate.d.ts.map +1 -0
- package/dist/utils/tokenEstimate.js +30 -0
- package/dist/utils/worktreeUtils.d.ts.map +1 -1
- package/dist/utils/worktreeUtils.js +3 -1
- package/package.json +1 -1
- package/src/agent.ts +11 -5
- package/src/index.ts +1 -0
- package/src/managers/aiManager.ts +36 -7
- package/src/managers/cronManager.ts +2 -0
- package/src/managers/liveConfigManager.ts +0 -12
- package/src/managers/mcpManager.ts +16 -3
- package/src/managers/messageManager.ts +32 -109
- package/src/managers/skillManager.ts +16 -2
- package/src/managers/toolManager.ts +2 -0
- package/src/prompts/index.ts +6 -1
- package/src/services/aiService.ts +12 -5
- package/src/services/configurationService.ts +26 -24
- package/src/services/hook.ts +7 -0
- package/src/services/initializationService.ts +1 -3
- package/src/services/interactionService.ts +1 -1
- package/src/services/reversionService.ts +50 -27
- package/src/services/session.ts +63 -262
- package/src/telemetry/sessionTracing.ts +225 -73
- package/src/tools/bashTool.ts +23 -1
- package/src/tools/readTool.ts +6 -12
- package/src/types/config.ts +8 -0
- package/src/types/hooks.ts +1 -0
- package/src/types/messaging.ts +0 -1
- package/src/types/skills.ts +1 -0
- package/src/types/telemetry.ts +19 -1
- package/src/utils/atomicWrite.ts +61 -0
- package/src/utils/cacheControlUtils.ts +5 -38
- package/src/utils/containerSetup.ts +5 -1
- package/src/utils/convertMessagesForAPI.ts +87 -57
- package/src/utils/fileUtils.ts +0 -46
- package/src/utils/gitUtils.ts +54 -0
- package/src/utils/groupMessagesByApiRound.ts +7 -6
- package/src/utils/mcpUtils.ts +2 -1
- package/src/utils/modelCapabilities.ts +30 -0
- package/src/utils/skillParser.ts +3 -1
- package/src/utils/tokenEstimate.ts +34 -0
- package/src/utils/worktreeUtils.ts +8 -1
|
@@ -31,6 +31,7 @@ import type { MemoryRuleManager } from "./MemoryRuleManager.js";
|
|
|
31
31
|
import type { MemoryRule } from "../types/memoryRule.js";
|
|
32
32
|
import type { MemoryService } from "../services/memory.js";
|
|
33
33
|
import { pathEncoder } from "../utils/pathEncoder.js";
|
|
34
|
+
import { estimateTokens } from "../utils/tokenEstimate.js";
|
|
34
35
|
import { READ_TOOL_NAME } from "../constants/tools.js";
|
|
35
36
|
|
|
36
37
|
import { Container } from "../utils/container.js";
|
|
@@ -93,8 +94,6 @@ export interface MessageManagerOptions {
|
|
|
93
94
|
export class MessageManager {
|
|
94
95
|
// Private state properties
|
|
95
96
|
private sessionId: string;
|
|
96
|
-
private rootSessionId: string;
|
|
97
|
-
private parentSessionId?: string;
|
|
98
97
|
private messages: Message[];
|
|
99
98
|
private latestTotalTokens: number;
|
|
100
99
|
private workdir: string;
|
|
@@ -117,7 +116,6 @@ export class MessageManager {
|
|
|
117
116
|
options: MessageManagerOptions,
|
|
118
117
|
) {
|
|
119
118
|
this.sessionId = generateSessionId();
|
|
120
|
-
this.rootSessionId = this.sessionId;
|
|
121
119
|
this.messages = [];
|
|
122
120
|
this.latestTotalTokens = 0;
|
|
123
121
|
this.workdir = options.workdir;
|
|
@@ -151,11 +149,7 @@ export class MessageManager {
|
|
|
151
149
|
}
|
|
152
150
|
|
|
153
151
|
public getRootSessionId(): string {
|
|
154
|
-
return this.
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
public getParentSessionId(): string | undefined {
|
|
158
|
-
return this.parentSessionId;
|
|
152
|
+
return this.sessionId;
|
|
159
153
|
}
|
|
160
154
|
|
|
161
155
|
public getMessages(): Message[] {
|
|
@@ -357,8 +351,6 @@ export class MessageManager {
|
|
|
357
351
|
unsavedMessages,
|
|
358
352
|
this.workdir,
|
|
359
353
|
this.sessionType,
|
|
360
|
-
this.rootSessionId,
|
|
361
|
-
this.parentSessionId,
|
|
362
354
|
);
|
|
363
355
|
|
|
364
356
|
// Update the saved message count
|
|
@@ -382,7 +374,6 @@ export class MessageManager {
|
|
|
382
374
|
public clearMessages(): void {
|
|
383
375
|
this.setMessages([]);
|
|
384
376
|
const newSessionId = generateSessionId();
|
|
385
|
-
this.rootSessionId = newSessionId;
|
|
386
377
|
this.setSessionId(newSessionId);
|
|
387
378
|
this.setlatestTotalTokens(0);
|
|
388
379
|
this.savedMessageCount = 0; // Reset saved message count
|
|
@@ -398,8 +389,6 @@ export class MessageManager {
|
|
|
398
389
|
// Initialize state from session data
|
|
399
390
|
public initializeFromSession(sessionData: SessionData): void {
|
|
400
391
|
this.setSessionId(sessionData.id);
|
|
401
|
-
this.rootSessionId = sessionData.rootSessionId || sessionData.id;
|
|
402
|
-
this.parentSessionId = sessionData.parentSessionId;
|
|
403
392
|
this.setMessages([...sessionData.messages]);
|
|
404
393
|
this.setlatestTotalTokens(sessionData.metadata.latestTotalTokens);
|
|
405
394
|
|
|
@@ -562,12 +551,13 @@ export class MessageManager {
|
|
|
562
551
|
}
|
|
563
552
|
|
|
564
553
|
/**
|
|
565
|
-
* Compact messages and update session
|
|
554
|
+
* Compact messages and update session — append compact message to same file (append-only).
|
|
555
|
+
* After compaction, in-memory messages are [compactMessage, ...lastThreeMessages].
|
|
566
556
|
*/
|
|
567
|
-
public compactMessagesAndUpdateSession(
|
|
557
|
+
public async compactMessagesAndUpdateSession(
|
|
568
558
|
compactedContent: string,
|
|
569
559
|
usage?: Usage,
|
|
570
|
-
): void {
|
|
560
|
+
): Promise<void> {
|
|
571
561
|
// Get last 2 API rounds to preserve (structurally safe boundary)
|
|
572
562
|
const lastThreeMessages = getLastApiRounds(this.messages, 2);
|
|
573
563
|
|
|
@@ -579,23 +569,27 @@ export class MessageManager {
|
|
|
579
569
|
{
|
|
580
570
|
type: "compact",
|
|
581
571
|
content: compactedContent,
|
|
582
|
-
sessionId: this.sessionId,
|
|
583
572
|
},
|
|
584
573
|
],
|
|
585
574
|
timestamp: new Date().toISOString(),
|
|
586
575
|
...(usage && { usage }),
|
|
587
576
|
};
|
|
588
577
|
|
|
589
|
-
// Build new message array: keep the compacted message and last
|
|
578
|
+
// Build new message array: keep the compacted message and last messages
|
|
590
579
|
const newMessages: Message[] = [compactMessage, ...lastThreeMessages];
|
|
591
580
|
|
|
592
|
-
//
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
581
|
+
// APPEND to the same session file (append-only, like Claude Code)
|
|
582
|
+
const { appendMessages } = await import("../services/session.js");
|
|
583
|
+
await appendMessages(
|
|
584
|
+
this.sessionId,
|
|
585
|
+
newMessages,
|
|
586
|
+
this.workdir,
|
|
587
|
+
this.sessionType,
|
|
588
|
+
);
|
|
596
589
|
|
|
597
|
-
//
|
|
590
|
+
// Update in-memory state
|
|
598
591
|
this.setMessages(newMessages);
|
|
592
|
+
this.savedMessageCount = newMessages.length;
|
|
599
593
|
|
|
600
594
|
// Clear and rebuild loaded rule IDs from remaining meta messages
|
|
601
595
|
this.clearLoadedRuleIds();
|
|
@@ -910,101 +904,26 @@ export class MessageManager {
|
|
|
910
904
|
index: number,
|
|
911
905
|
reversionManager?: import("./reversionManager.js").ReversionManager,
|
|
912
906
|
): Promise<void> {
|
|
913
|
-
const { messages
|
|
907
|
+
const { messages } = await this.getFullMessageThread();
|
|
914
908
|
|
|
915
909
|
if (index < 0 || index >= messages.length) {
|
|
916
910
|
throw new Error(`Invalid message index: ${index}`);
|
|
917
911
|
}
|
|
918
912
|
|
|
919
|
-
|
|
920
|
-
let targetSessionId = this.sessionId;
|
|
921
|
-
let targetIndexInSession = index;
|
|
922
|
-
|
|
923
|
-
// We need to be careful here because loadFullMessageThread might have removed "compact" blocks
|
|
924
|
-
// Let's re-calculate based on the actual messages returned.
|
|
925
|
-
// Actually, it's easier to just load sessions one by one again or keep track of counts.
|
|
926
|
-
|
|
927
|
-
// For simplicity, let's assume we want to truncate the WHOLE thread.
|
|
928
|
-
// If the index is in a previous session, we need to:
|
|
929
|
-
// 1. Load that session.
|
|
930
|
-
// 2. Truncate it.
|
|
931
|
-
// 3. Make it the current session.
|
|
932
|
-
// 4. Delete/Invalidate subsequent sessions.
|
|
933
|
-
|
|
934
|
-
// To correctly map 'index' to a session, we need to know the message count of each session
|
|
935
|
-
// as they appear in the concatenated 'messages' array.
|
|
936
|
-
|
|
937
|
-
let remainingIndex = index;
|
|
938
|
-
const { loadSessionFromJsonl } = await import("../services/session.js");
|
|
939
|
-
|
|
940
|
-
for (const sid of sessionIds) {
|
|
941
|
-
const sessionData = await loadSessionFromJsonl(sid, this.workdir);
|
|
942
|
-
if (!sessionData) continue;
|
|
943
|
-
|
|
944
|
-
const sessionMessages = sessionData.messages;
|
|
945
|
-
// If this is not the first session in the thread, it might have a compact block at the start
|
|
946
|
-
// that was removed in getFullMessageThread.
|
|
947
|
-
const hasCompactBlock = sessionMessages[0]?.blocks.some(
|
|
948
|
-
(b) => b.type === "compact",
|
|
949
|
-
);
|
|
950
|
-
const effectiveMessages =
|
|
951
|
-
hasCompactBlock && sid !== sessionIds[0]
|
|
952
|
-
? sessionMessages.slice(1)
|
|
953
|
-
: sessionMessages;
|
|
954
|
-
|
|
955
|
-
if (remainingIndex < effectiveMessages.length) {
|
|
956
|
-
targetSessionId = sid;
|
|
957
|
-
targetIndexInSession = hasCompactBlock
|
|
958
|
-
? remainingIndex + 1
|
|
959
|
-
: remainingIndex;
|
|
960
|
-
break;
|
|
961
|
-
}
|
|
962
|
-
remainingIndex -= effectiveMessages.length;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
// Load the target session to perform truncation
|
|
966
|
-
const targetSessionData = await loadSessionFromJsonl(
|
|
967
|
-
targetSessionId,
|
|
968
|
-
this.workdir,
|
|
969
|
-
);
|
|
970
|
-
if (!targetSessionData)
|
|
971
|
-
throw new Error(`Target session ${targetSessionId} not found`);
|
|
972
|
-
|
|
973
|
-
// Identify messages to be removed (from the whole thread)
|
|
913
|
+
const newMessages = messages.slice(0, index);
|
|
974
914
|
const messagesToRemove = messages.slice(index);
|
|
975
915
|
const messageIdsToRemove = messagesToRemove
|
|
976
916
|
.map((m) => m.id as string)
|
|
977
917
|
.filter((id) => !!id);
|
|
978
918
|
|
|
979
|
-
// Revert file changes if manager is provided
|
|
980
919
|
if (reversionManager && messageIdsToRemove.length > 0) {
|
|
981
920
|
await reversionManager.revertTo(messageIdsToRemove, messages);
|
|
982
921
|
}
|
|
983
922
|
|
|
984
|
-
//
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
);
|
|
989
|
-
|
|
990
|
-
// Update target session file
|
|
991
|
-
this.sessionId = targetSessionId;
|
|
992
|
-
this.rootSessionId = targetSessionData.rootSessionId || targetSessionId;
|
|
993
|
-
this.parentSessionId = targetSessionData.parentSessionId;
|
|
994
|
-
this.transcriptPath = this.computeTranscriptPath();
|
|
995
|
-
|
|
996
|
-
await this.rewriteSessionFile(newMessagesInSession);
|
|
997
|
-
|
|
998
|
-
// Update in-memory messages to the truncated session messages
|
|
999
|
-
// We do NOT include ancestor messages here to avoid exceeding context limits.
|
|
1000
|
-
// The 'compact' block at the start of the session (if any) already summarizes them.
|
|
1001
|
-
this.setMessages(newMessagesInSession);
|
|
1002
|
-
|
|
1003
|
-
// Update saved message count
|
|
1004
|
-
this.savedMessageCount = newMessagesInSession.length;
|
|
1005
|
-
|
|
1006
|
-
// Notify session ID change if it changed
|
|
1007
|
-
this.callbacks.onSessionIdChange?.(this.sessionId);
|
|
923
|
+
// Rewrite file with truncated messages
|
|
924
|
+
await this.rewriteSessionFile(newMessages);
|
|
925
|
+
this.setMessages(newMessages);
|
|
926
|
+
this.savedMessageCount = newMessages.length;
|
|
1008
927
|
}
|
|
1009
928
|
|
|
1010
929
|
/**
|
|
@@ -1063,7 +982,7 @@ export class MessageManager {
|
|
|
1063
982
|
/**
|
|
1064
983
|
* Get recent file read contents, sorted by timestamp (newest first).
|
|
1065
984
|
* @param maxFiles - Maximum number of files to return
|
|
1066
|
-
* @param maxTokensPerFile - Maximum tokens per file (
|
|
985
|
+
* @param maxTokensPerFile - Maximum tokens per file (CJK-aware estimation)
|
|
1067
986
|
* @returns Array of { path, content } sorted by recency
|
|
1068
987
|
*/
|
|
1069
988
|
public getRecentFileReads(
|
|
@@ -1076,10 +995,14 @@ export class MessageManager {
|
|
|
1076
995
|
|
|
1077
996
|
const result: Array<{ path: string; content: string }> = [];
|
|
1078
997
|
for (const [path, { content }] of sorted) {
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
998
|
+
const tokenCount = estimateTokens(content);
|
|
999
|
+
let truncated = content;
|
|
1000
|
+
if (tokenCount > maxTokensPerFile) {
|
|
1001
|
+
// Truncate proportionally to fit within the token budget
|
|
1002
|
+
const ratio = maxTokensPerFile / tokenCount;
|
|
1003
|
+
const cutIndex = Math.floor(content.length * ratio);
|
|
1004
|
+
truncated = content.slice(0, cutIndex);
|
|
1005
|
+
}
|
|
1083
1006
|
result.push({ path, content: truncated });
|
|
1084
1007
|
}
|
|
1085
1008
|
return result;
|
|
@@ -30,6 +30,7 @@ import { logger } from "../utils/globalLogger.js";
|
|
|
30
30
|
export class SkillManager extends EventEmitter {
|
|
31
31
|
private personalSkillsPath: string;
|
|
32
32
|
private personalClaudeSkillsPath: string;
|
|
33
|
+
private personalAgentsSkillsPath: string;
|
|
33
34
|
private scanTimeout: number;
|
|
34
35
|
private workdir: string;
|
|
35
36
|
|
|
@@ -50,6 +51,8 @@ export class SkillManager extends EventEmitter {
|
|
|
50
51
|
options.personalSkillsPath || join(homedir(), ".wave", "skills");
|
|
51
52
|
this.personalClaudeSkillsPath =
|
|
52
53
|
options.personalClaudeSkillsPath || join(homedir(), ".claude", "skills");
|
|
54
|
+
this.personalAgentsSkillsPath =
|
|
55
|
+
options.personalAgentsSkillsPath || join(homedir(), ".agents", "skills");
|
|
53
56
|
this.scanTimeout = options.scanTimeout || 5000;
|
|
54
57
|
this.workdir = options.workdir || process.cwd();
|
|
55
58
|
this.watchEnabled = options.watch ?? false;
|
|
@@ -131,8 +134,10 @@ export class SkillManager extends EventEmitter {
|
|
|
131
134
|
const pathsToWatch = [
|
|
132
135
|
this.personalSkillsPath,
|
|
133
136
|
this.personalClaudeSkillsPath,
|
|
137
|
+
this.personalAgentsSkillsPath,
|
|
134
138
|
join(this.workdir, ".wave", "skills"),
|
|
135
139
|
join(this.workdir, ".claude", "skills"),
|
|
140
|
+
join(this.workdir, ".agents", "skills"),
|
|
136
141
|
];
|
|
137
142
|
|
|
138
143
|
logger?.debug(`Setting up skill watcher for: ${pathsToWatch.join(", ")}`);
|
|
@@ -225,6 +230,11 @@ export class SkillManager extends EventEmitter {
|
|
|
225
230
|
"builtin",
|
|
226
231
|
);
|
|
227
232
|
|
|
233
|
+
const personalAgentsCollection = await this.discoverSkillCollection(
|
|
234
|
+
this.personalAgentsSkillsPath,
|
|
235
|
+
"personal",
|
|
236
|
+
);
|
|
237
|
+
|
|
228
238
|
const personalClaudeCollection = await this.discoverSkillCollection(
|
|
229
239
|
this.personalClaudeSkillsPath,
|
|
230
240
|
"personal",
|
|
@@ -243,12 +253,14 @@ export class SkillManager extends EventEmitter {
|
|
|
243
253
|
return {
|
|
244
254
|
builtinSkills: builtinCollection.skills,
|
|
245
255
|
personalSkills: new Map([
|
|
246
|
-
...
|
|
256
|
+
...personalAgentsCollection.skills, // lowest priority
|
|
257
|
+
...personalClaudeCollection.skills, // overrides .agents
|
|
247
258
|
...personalCollection.skills, // .wave overrides .claude
|
|
248
259
|
]),
|
|
249
260
|
projectSkills: projectCollection.skills,
|
|
250
261
|
errors: [
|
|
251
262
|
...builtinCollection.errors,
|
|
263
|
+
...personalAgentsCollection.errors,
|
|
252
264
|
...personalClaudeCollection.errors,
|
|
253
265
|
...personalCollection.errors,
|
|
254
266
|
...projectCollection.errors,
|
|
@@ -271,9 +283,11 @@ export class SkillManager extends EventEmitter {
|
|
|
271
283
|
};
|
|
272
284
|
|
|
273
285
|
if (type === "project") {
|
|
274
|
-
// Scan .
|
|
286
|
+
// Scan .agents/skills first, then .claude/skills, then .wave/skills (wave overrides)
|
|
287
|
+
const agentsPath = join(basePath, ".agents", "skills");
|
|
275
288
|
const claudePath = join(basePath, ".claude", "skills");
|
|
276
289
|
const wavePath = join(basePath, ".wave", "skills");
|
|
290
|
+
await this.scanSkillPath(agentsPath, collection);
|
|
277
291
|
await this.scanSkillPath(claudePath, collection);
|
|
278
292
|
await this.scanSkillPath(wavePath, collection);
|
|
279
293
|
} else {
|
|
@@ -279,6 +279,7 @@ class ToolManager {
|
|
|
279
279
|
endToolSpan({
|
|
280
280
|
success: result.success,
|
|
281
281
|
durationMs: Date.now() - toolStartTime,
|
|
282
|
+
output: result.content,
|
|
282
283
|
});
|
|
283
284
|
return result;
|
|
284
285
|
} catch (error) {
|
|
@@ -303,6 +304,7 @@ class ToolManager {
|
|
|
303
304
|
endToolSpan({
|
|
304
305
|
success: result.success,
|
|
305
306
|
durationMs: Date.now() - toolStartTime,
|
|
307
|
+
output: result.content,
|
|
306
308
|
});
|
|
307
309
|
return result;
|
|
308
310
|
} catch (error) {
|
package/src/prompts/index.ts
CHANGED
|
@@ -19,7 +19,11 @@ import {
|
|
|
19
19
|
GREP_TOOL_NAME,
|
|
20
20
|
} from "../constants/tools.js";
|
|
21
21
|
|
|
22
|
-
export const BASE_SYSTEM_PROMPT = `You are
|
|
22
|
+
export const BASE_SYSTEM_PROMPT = `You are a coding agent developed by NetEase that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.`;
|
|
23
|
+
|
|
24
|
+
export const MODEL_IDENTITY_PROMPT = `# Model identity
|
|
25
|
+
|
|
26
|
+
If asked about your model, model version, underlying architecture, or product name, identify yourself simply as a coding agent developed by NetEase. Do not reveal the specific model ID, model family, model provider, or product name. Do not mention or hint at any of these details.`;
|
|
23
27
|
|
|
24
28
|
export const DOING_TASKS_PROMPT = `# Doing tasks
|
|
25
29
|
- The user will primarily request you to perform software engineering tasks. These may include solving bugs, adding new functionality, refactoring code, explaining code, and more. When given an unclear or generic instruction, consider it in the context of these software engineering tasks and the current working directory. For example, if the user asks you to change "methodName" to snake case, do not reply with just "method_name", instead find the method in the code and modify the code.
|
|
@@ -265,6 +269,7 @@ export function buildSystemPrompt(
|
|
|
265
269
|
|
|
266
270
|
staticText += `\n\n${OUTPUT_EFFICIENCY_PROMPT}`;
|
|
267
271
|
staticText += `\n\n${TONE_AND_STYLE_PROMPT}`;
|
|
272
|
+
staticText += `\n\n${MODEL_IDENTITY_PROMPT}`;
|
|
268
273
|
|
|
269
274
|
const blocks: SystemPromptBlock[] = [{ text: staticText, cacheable: true }];
|
|
270
275
|
|
|
@@ -13,11 +13,11 @@ import type { GatewayConfig, ModelConfig } from "../types/index.js";
|
|
|
13
13
|
import { ConfigurationError, CONFIG_ERRORS } from "../types/index.js";
|
|
14
14
|
import {
|
|
15
15
|
transformMessagesForExplicitCache,
|
|
16
|
-
supportsPromptCaching,
|
|
17
16
|
extendUsageWithCacheMetrics,
|
|
18
17
|
type ClaudeUsage,
|
|
19
18
|
type ClaudeChatCompletionContentPartText,
|
|
20
19
|
} from "../utils/cacheControlUtils.js";
|
|
20
|
+
import { supportsPromptCaching } from "../utils/modelCapabilities.js";
|
|
21
21
|
|
|
22
22
|
import * as os from "os";
|
|
23
23
|
import * as fs from "fs";
|
|
@@ -265,13 +265,12 @@ export async function callAgent(
|
|
|
265
265
|
});
|
|
266
266
|
|
|
267
267
|
// Determine model early (needed for system prompt construction)
|
|
268
|
-
const currentModel = model || modelConfig.model;
|
|
269
268
|
const resolvedMaxTokens = options.maxTokens ?? modelConfig.maxTokens;
|
|
270
269
|
|
|
271
270
|
// Build system message content
|
|
272
271
|
let systemMessage: ChatCompletionMessageParam;
|
|
273
272
|
if (Array.isArray(systemPrompt)) {
|
|
274
|
-
if (supportsPromptCaching(
|
|
273
|
+
if (supportsPromptCaching(modelConfig.capabilities)) {
|
|
275
274
|
// For Claude models, map blocks to content parts with cache_control on cacheable blocks
|
|
276
275
|
const contentParts: ClaudeChatCompletionContentPartText[] =
|
|
277
276
|
systemPrompt.map((block) => {
|
|
@@ -307,10 +306,10 @@ export async function callAgent(
|
|
|
307
306
|
|
|
308
307
|
processedTools = tools;
|
|
309
308
|
|
|
310
|
-
if (supportsPromptCaching(
|
|
309
|
+
if (supportsPromptCaching(modelConfig.capabilities)) {
|
|
311
310
|
openaiMessages = transformMessagesForExplicitCache(
|
|
312
311
|
openaiMessages,
|
|
313
|
-
|
|
312
|
+
modelConfig.capabilities,
|
|
314
313
|
);
|
|
315
314
|
}
|
|
316
315
|
|
|
@@ -320,12 +319,14 @@ export async function callAgent(
|
|
|
320
319
|
fastModel: _fastModel,
|
|
321
320
|
maxTokens: _maxTokens,
|
|
322
321
|
permissionMode: _permissionMode,
|
|
322
|
+
capabilities: _capabilities,
|
|
323
323
|
...extraParams
|
|
324
324
|
} = modelConfig;
|
|
325
325
|
void _model;
|
|
326
326
|
void _fastModel;
|
|
327
327
|
void _maxTokens;
|
|
328
328
|
void _permissionMode;
|
|
329
|
+
void _capabilities;
|
|
329
330
|
|
|
330
331
|
const openaiModelConfig = getModelConfig(model || modelConfig.model, {
|
|
331
332
|
max_tokens: resolvedMaxTokens,
|
|
@@ -866,6 +867,7 @@ export async function compactMessages(
|
|
|
866
867
|
fastModel: _fastModel,
|
|
867
868
|
maxTokens: _maxTokens,
|
|
868
869
|
permissionMode: _permissionMode,
|
|
870
|
+
capabilities: _capabilities,
|
|
869
871
|
fastModelConfig: _fastModelConfig,
|
|
870
872
|
...extraParams
|
|
871
873
|
} = modelConfig;
|
|
@@ -873,6 +875,7 @@ export async function compactMessages(
|
|
|
873
875
|
void _fastModel;
|
|
874
876
|
void _maxTokens;
|
|
875
877
|
void _permissionMode;
|
|
878
|
+
void _capabilities;
|
|
876
879
|
|
|
877
880
|
// When a fast model override is provided, use the fast model's hyperparams
|
|
878
881
|
// (if configured); otherwise fall back to the agent model's extraParams.
|
|
@@ -989,6 +992,7 @@ export async function processWebContent(
|
|
|
989
992
|
fastModel: _fastModel,
|
|
990
993
|
maxTokens: _maxTokens,
|
|
991
994
|
permissionMode: _permissionMode,
|
|
995
|
+
capabilities: _capabilities,
|
|
992
996
|
fastModelConfig: _fastModelConfig,
|
|
993
997
|
...extraParams
|
|
994
998
|
} = modelConfig;
|
|
@@ -996,6 +1000,7 @@ export async function processWebContent(
|
|
|
996
1000
|
void _fastModel;
|
|
997
1001
|
void _maxTokens;
|
|
998
1002
|
void _permissionMode;
|
|
1003
|
+
void _capabilities;
|
|
999
1004
|
|
|
1000
1005
|
// When a fast model override is provided, use the fast model's hyperparams
|
|
1001
1006
|
// (if configured); otherwise fall back to the agent model's extraParams.
|
|
@@ -1106,12 +1111,14 @@ export async function btw(options: BtwOptions): Promise<BtwResult> {
|
|
|
1106
1111
|
fastModel: _fastModel,
|
|
1107
1112
|
maxTokens: _maxTokens,
|
|
1108
1113
|
permissionMode: _permissionMode,
|
|
1114
|
+
capabilities: _capabilities,
|
|
1109
1115
|
...extraParams
|
|
1110
1116
|
} = modelConfig;
|
|
1111
1117
|
void _model;
|
|
1112
1118
|
void _fastModel;
|
|
1113
1119
|
void _maxTokens;
|
|
1114
1120
|
void _permissionMode;
|
|
1121
|
+
void _capabilities;
|
|
1115
1122
|
|
|
1116
1123
|
const openaiModelConfig = getModelConfig(options.model || modelConfig.model, {
|
|
1117
1124
|
temperature: 0.1,
|
|
@@ -47,6 +47,8 @@ import {
|
|
|
47
47
|
mergeRemoteSettings,
|
|
48
48
|
} from "./remoteSettingsService.js";
|
|
49
49
|
import { createAuthAwareFetch } from "./authService.js";
|
|
50
|
+
import { ensureWaveRuntimeFilesExcluded } from "../utils/gitUtils.js";
|
|
51
|
+
import { atomicWriteFile } from "../utils/atomicWrite.js";
|
|
50
52
|
|
|
51
53
|
/**
|
|
52
54
|
* Default ConfigurationService implementation
|
|
@@ -544,12 +546,14 @@ export class ConfigurationService {
|
|
|
544
546
|
fastModel: _ffm,
|
|
545
547
|
maxTokens: _fmt,
|
|
546
548
|
permissionMode: _fpm,
|
|
549
|
+
capabilities: _fcap,
|
|
547
550
|
...fastHyperparams
|
|
548
551
|
} = fastModelConfigSource;
|
|
549
552
|
void _fm;
|
|
550
553
|
void _ffm;
|
|
551
554
|
void _fmt;
|
|
552
555
|
void _fpm;
|
|
556
|
+
void _fcap;
|
|
553
557
|
baseConfig.fastModelConfig = fastHyperparams;
|
|
554
558
|
}
|
|
555
559
|
|
|
@@ -728,7 +732,7 @@ export class ConfigurationService {
|
|
|
728
732
|
}
|
|
729
733
|
|
|
730
734
|
config.model = model;
|
|
731
|
-
await
|
|
735
|
+
await atomicWriteFile(configPath, JSON.stringify(config, null, 2));
|
|
732
736
|
}
|
|
733
737
|
|
|
734
738
|
/**
|
|
@@ -791,6 +795,7 @@ export class ConfigurationService {
|
|
|
791
795
|
|
|
792
796
|
// Ensure .wave directory exists
|
|
793
797
|
const waveDir = path.join(workdir, ".wave");
|
|
798
|
+
ensureWaveRuntimeFilesExcluded(workdir);
|
|
794
799
|
if (!existsSync(waveDir)) {
|
|
795
800
|
await fs.mkdir(waveDir, { recursive: true });
|
|
796
801
|
}
|
|
@@ -814,11 +819,7 @@ export class ConfigurationService {
|
|
|
814
819
|
|
|
815
820
|
if (!config.permissions.allow.includes(rule)) {
|
|
816
821
|
config.permissions.allow.push(rule);
|
|
817
|
-
await
|
|
818
|
-
localConfigPath,
|
|
819
|
-
JSON.stringify(config, null, 2),
|
|
820
|
-
"utf-8",
|
|
821
|
-
);
|
|
822
|
+
await atomicWriteFile(localConfigPath, JSON.stringify(config, null, 2));
|
|
822
823
|
}
|
|
823
824
|
}
|
|
824
825
|
|
|
@@ -866,7 +867,7 @@ export class ConfigurationService {
|
|
|
866
867
|
|
|
867
868
|
config.enabledPlugins[pluginId] = enabled;
|
|
868
869
|
|
|
869
|
-
await
|
|
870
|
+
await atomicWriteFile(configPath, JSON.stringify(config, null, 2));
|
|
870
871
|
}
|
|
871
872
|
|
|
872
873
|
/**
|
|
@@ -938,11 +939,7 @@ export class ConfigurationService {
|
|
|
938
939
|
}
|
|
939
940
|
fileConfig.marketplaces[name] = config;
|
|
940
941
|
|
|
941
|
-
await
|
|
942
|
-
configPath,
|
|
943
|
-
JSON.stringify(fileConfig, null, 2),
|
|
944
|
-
"utf-8",
|
|
945
|
-
);
|
|
942
|
+
await atomicWriteFile(configPath, JSON.stringify(fileConfig, null, 2));
|
|
946
943
|
}
|
|
947
944
|
|
|
948
945
|
/**
|
|
@@ -976,11 +973,7 @@ export class ConfigurationService {
|
|
|
976
973
|
|
|
977
974
|
if (fileConfig.marketplaces && name in fileConfig.marketplaces) {
|
|
978
975
|
delete fileConfig.marketplaces[name];
|
|
979
|
-
await
|
|
980
|
-
configPath,
|
|
981
|
-
JSON.stringify(fileConfig, null, 2),
|
|
982
|
-
"utf-8",
|
|
983
|
-
);
|
|
976
|
+
await atomicWriteFile(configPath, JSON.stringify(fileConfig, null, 2));
|
|
984
977
|
}
|
|
985
978
|
} catch {
|
|
986
979
|
// Ignore errors for corrupted or non-existent files
|
|
@@ -1018,11 +1011,7 @@ export class ConfigurationService {
|
|
|
1018
1011
|
|
|
1019
1012
|
if (config.enabledPlugins && pluginId in config.enabledPlugins) {
|
|
1020
1013
|
delete config.enabledPlugins[pluginId];
|
|
1021
|
-
await
|
|
1022
|
-
configPath,
|
|
1023
|
-
JSON.stringify(config, null, 2),
|
|
1024
|
-
"utf-8",
|
|
1025
|
-
);
|
|
1014
|
+
await atomicWriteFile(configPath, JSON.stringify(config, null, 2));
|
|
1026
1015
|
}
|
|
1027
1016
|
} catch {
|
|
1028
1017
|
// Ignore errors for corrupted or non-existent files
|
|
@@ -1169,6 +1158,15 @@ export function loadWaveConfigFromFile(
|
|
|
1169
1158
|
|
|
1170
1159
|
try {
|
|
1171
1160
|
const content = readFileSync(filePath, "utf-8");
|
|
1161
|
+
|
|
1162
|
+
// Tolerate empty/whitespace-only files: a concurrent writer may have the
|
|
1163
|
+
// file truncated mid-write. Returning null lets callers skip this source
|
|
1164
|
+
// instead of crashing (consistent with the "file not found" path).
|
|
1165
|
+
if (content.trim() === "") {
|
|
1166
|
+
logger.warn(`Empty configuration file (likely mid-write): ${filePath}`);
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1172
1170
|
const config = JSON.parse(content) as WaveConfiguration;
|
|
1173
1171
|
|
|
1174
1172
|
return {
|
|
@@ -1187,10 +1185,14 @@ export function loadWaveConfigFromFile(
|
|
|
1187
1185
|
};
|
|
1188
1186
|
} catch (error) {
|
|
1189
1187
|
if (error instanceof SyntaxError) {
|
|
1190
|
-
|
|
1188
|
+
// Tolerate corrupt JSON: a concurrent writer may leave the file in a
|
|
1189
|
+
// partially-written state. Warn and return null so callers skip this
|
|
1190
|
+
// source instead of crashing.
|
|
1191
|
+
logger.warn(`Invalid JSON syntax in ${filePath}: ${error.message}`);
|
|
1192
|
+
return null;
|
|
1191
1193
|
}
|
|
1192
1194
|
|
|
1193
|
-
// Re-throw
|
|
1195
|
+
// Re-throw other errors as-is
|
|
1194
1196
|
throw error;
|
|
1195
1197
|
}
|
|
1196
1198
|
}
|
package/src/services/hook.ts
CHANGED
|
@@ -89,6 +89,13 @@ async function buildHookJsonInput(
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// Add worktree_path field for WorktreeRemove events
|
|
93
|
+
if (context.event === "WorktreeRemove") {
|
|
94
|
+
if (context.worktreePath !== undefined) {
|
|
95
|
+
jsonInput.worktree_path = context.worktreePath;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
92
99
|
// Add SessionStart-specific fields
|
|
93
100
|
if (context.event === "SessionStart") {
|
|
94
101
|
if (context.source !== undefined) {
|
|
@@ -326,9 +326,7 @@ export class InitializationService {
|
|
|
326
326
|
messageManager.initializeFromSession(sessionToRestore);
|
|
327
327
|
|
|
328
328
|
// Update task manager with the root session ID to ensure continuity across compactions
|
|
329
|
-
taskManager.setTaskListId(
|
|
330
|
-
sessionToRestore.rootSessionId || sessionToRestore.id,
|
|
331
|
-
);
|
|
329
|
+
taskManager.setTaskListId(sessionToRestore.id);
|
|
332
330
|
|
|
333
331
|
// After session is initialized, load tasks for the session
|
|
334
332
|
const tasks = await taskManager.listTasks();
|
|
@@ -166,7 +166,7 @@ export class InteractionService {
|
|
|
166
166
|
messageManager.initializeFromSession(sessionData);
|
|
167
167
|
|
|
168
168
|
// Update task manager with the root session ID to ensure continuity across compactions
|
|
169
|
-
taskManager.setTaskListId(sessionData.
|
|
169
|
+
taskManager.setTaskListId(sessionData.id);
|
|
170
170
|
|
|
171
171
|
// 7. Load tasks for the restored session
|
|
172
172
|
const tasks = await taskManager.listTasks();
|