wave-agent-sdk 0.18.6 → 0.18.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +12 -5
- 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 +6 -1
- package/dist/managers/messageManager.d.ts +1 -1
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +10 -4
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +2 -0
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +2 -0
- package/dist/telemetry/sessionTracing.d.ts +17 -16
- package/dist/telemetry/sessionTracing.d.ts.map +1 -1
- package/dist/telemetry/sessionTracing.js +62 -58
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +6 -7
- package/dist/types/telemetry.d.ts +2 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- 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/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/managers/aiManager.ts +14 -5
- package/src/managers/cronManager.ts +2 -0
- package/src/managers/liveConfigManager.ts +0 -12
- package/src/managers/mcpManager.ts +8 -1
- package/src/managers/messageManager.ts +10 -5
- package/src/managers/toolManager.ts +2 -0
- package/src/services/configurationService.ts +2 -0
- package/src/telemetry/sessionTracing.ts +64 -67
- package/src/tools/readTool.ts +6 -12
- package/src/types/telemetry.ts +2 -0
- package/src/utils/fileUtils.ts +0 -46
- package/src/utils/gitUtils.ts +54 -0
- package/src/utils/groupMessagesByApiRound.ts +7 -6
- package/src/utils/tokenEstimate.ts +34 -0
- package/src/utils/worktreeUtils.ts +8 -1
|
@@ -63,6 +63,8 @@ export interface ToolMetadata {
|
|
|
63
63
|
error?: string;
|
|
64
64
|
/** Execution time (ms) */
|
|
65
65
|
durationMs: number;
|
|
66
|
+
/** Tool output content (recorded when logToolContent is true) */
|
|
67
|
+
output?: string;
|
|
66
68
|
}
|
|
67
69
|
/** Event names for OTel structured logging */
|
|
68
70
|
export type OTelEventName = "session_start" | "session_end" | "user_prompt" | "tool_decision" | "compaction" | "error";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/types/telemetry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,CAAC;AAE9C,8BAA8B;AAC9B,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,WAAW,GAAG,MAAM,CAAC;AAElE,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,wBAAwB;IACxB,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oCAAoC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,iCAAiC;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,4CAA4C;IAC5C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qCAAqC;AACrC,MAAM,WAAW,kBAAkB;IACjC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,OAAO,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACvC,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/types/telemetry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,CAAC;AAE9C,8BAA8B;AAC9B,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,WAAW,GAAG,MAAM,CAAC;AAElE,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,wBAAwB;IACxB,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oCAAoC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,iCAAiC;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,4CAA4C;IAC5C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qCAAqC;AACrC,MAAM,WAAW,kBAAkB;IACjC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,OAAO,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACvC,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,8CAA8C;AAC9C,MAAM,MAAM,aAAa,GACrB,eAAe,GACf,aAAa,GACb,aAAa,GACb,eAAe,GACf,YAAY,GACZ,OAAO,CAAC"}
|
|
@@ -26,12 +26,6 @@ export declare function readFirstNLines(filePath: string, maxLines: number): Pro
|
|
|
26
26
|
* @return {Promise<string>} - The last non-empty line of the file, or an empty string if no non-empty lines found.
|
|
27
27
|
*/
|
|
28
28
|
export declare function getLastLine(filePath: string, minLength?: number): Promise<string>;
|
|
29
|
-
/**
|
|
30
|
-
* Ensures that a pattern is present in the global git ignore file.
|
|
31
|
-
*
|
|
32
|
-
* @param {string} pattern - The pattern to add to global git ignore.
|
|
33
|
-
*/
|
|
34
|
-
export declare function ensureGlobalGitIgnore(pattern: string): Promise<void>;
|
|
35
29
|
/**
|
|
36
30
|
* Suggests similar paths if a file is not found.
|
|
37
31
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileUtils.d.ts","sourceRoot":"","sources":["../../src/utils/fileUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fileUtils.d.ts","sourceRoot":"","sources":["../../src/utils/fileUtils.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBrE;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,SAAS,SAAI,GACZ,OAAO,CAAC,MAAM,CAAC,CA8DjB;AA2BD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,EAAE,CAAC,CAwBnB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB"}
|
package/dist/utils/fileUtils.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { execSync } from "node:child_process";
|
|
5
|
-
import { homedir } from "node:os";
|
|
6
4
|
import { glob } from "glob";
|
|
7
5
|
/**
|
|
8
6
|
* Reads the first line of a file efficiently using Node.js readline.
|
|
@@ -138,47 +136,6 @@ export async function getLastLine(filePath, minLength = 1) {
|
|
|
138
136
|
}
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Ensures that a pattern is present in the global git ignore file.
|
|
143
|
-
*
|
|
144
|
-
* @param {string} pattern - The pattern to add to global git ignore.
|
|
145
|
-
*/
|
|
146
|
-
export async function ensureGlobalGitIgnore(pattern) {
|
|
147
|
-
try {
|
|
148
|
-
let globalIgnorePath;
|
|
149
|
-
try {
|
|
150
|
-
globalIgnorePath = execSync("git config --get core.excludesfile", {
|
|
151
|
-
encoding: "utf8",
|
|
152
|
-
}).trim();
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
// If not set, use default paths
|
|
156
|
-
const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(homedir(), ".config");
|
|
157
|
-
globalIgnorePath = path.join(xdgConfigHome, "git", "ignore");
|
|
158
|
-
}
|
|
159
|
-
if (!globalIgnorePath)
|
|
160
|
-
return;
|
|
161
|
-
// Ensure directory exists
|
|
162
|
-
await fs.mkdir(path.dirname(globalIgnorePath), { recursive: true });
|
|
163
|
-
let content = "";
|
|
164
|
-
try {
|
|
165
|
-
content = await fs.readFile(globalIgnorePath, "utf8");
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
// File doesn't exist
|
|
169
|
-
}
|
|
170
|
-
const lines = content.split("\n").map((line) => line.trim());
|
|
171
|
-
if (!lines.includes(pattern)) {
|
|
172
|
-
const newContent = content.endsWith("\n") || content === ""
|
|
173
|
-
? `${content}${pattern}\n`
|
|
174
|
-
: `${content}\n${pattern}\n`;
|
|
175
|
-
await fs.writeFile(globalIgnorePath, newContent, "utf8");
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
// Ignore errors
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
139
|
/**
|
|
183
140
|
* Simple Levenshtein distance implementation
|
|
184
141
|
*/
|
package/dist/utils/gitUtils.d.ts
CHANGED
|
@@ -45,6 +45,17 @@ export declare function resolveGitDir(cwd: string): string | null;
|
|
|
45
45
|
* @returns Default remote branch name
|
|
46
46
|
*/
|
|
47
47
|
export declare function getDefaultRemoteBranch(cwd: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Ensure that Wave runtime files are excluded from git status by writing
|
|
50
|
+
* patterns to `.git/info/exclude` (per-repo, not global).
|
|
51
|
+
*
|
|
52
|
+
* Idempotent: if the marker `# wave-runtime` is already present in the
|
|
53
|
+
* exclude file, it skips writing. A module-level Set provides additional
|
|
54
|
+
* per-process dedup to avoid redundant file reads.
|
|
55
|
+
*
|
|
56
|
+
* @param cwd Working directory to start searching from
|
|
57
|
+
*/
|
|
58
|
+
export declare function ensureWaveRuntimeFilesExcluded(cwd: string): void;
|
|
48
59
|
/**
|
|
49
60
|
* Check if there are uncommitted changes in the working directory
|
|
50
61
|
* @param cwd Working directory
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitUtils.d.ts","sourceRoot":"","sources":["../../src/utils/gitUtils.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAevD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWnD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAetD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkCxD;AAwDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4B1D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW1D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAYvE"}
|
|
1
|
+
{"version":3,"file":"gitUtils.d.ts","sourceRoot":"","sources":["../../src/utils/gitUtils.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAevD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWnD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAetD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkCxD;AAwDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4B1D;AAQD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAoChE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW1D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAYvE"}
|
package/dist/utils/gitUtils.js
CHANGED
|
@@ -216,6 +216,57 @@ export function getDefaultRemoteBranch(cwd) {
|
|
|
216
216
|
// 4. Hardcoded fallback
|
|
217
217
|
return "main";
|
|
218
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Module-level set tracking git dirs already processed in this process,
|
|
221
|
+
* to avoid redundant I/O.
|
|
222
|
+
*/
|
|
223
|
+
const excludedGitDirs = new Set();
|
|
224
|
+
/**
|
|
225
|
+
* Ensure that Wave runtime files are excluded from git status by writing
|
|
226
|
+
* patterns to `.git/info/exclude` (per-repo, not global).
|
|
227
|
+
*
|
|
228
|
+
* Idempotent: if the marker `# wave-runtime` is already present in the
|
|
229
|
+
* exclude file, it skips writing. A module-level Set provides additional
|
|
230
|
+
* per-process dedup to avoid redundant file reads.
|
|
231
|
+
*
|
|
232
|
+
* @param cwd Working directory to start searching from
|
|
233
|
+
*/
|
|
234
|
+
export function ensureWaveRuntimeFilesExcluded(cwd) {
|
|
235
|
+
try {
|
|
236
|
+
const gitDir = resolveGitDir(cwd);
|
|
237
|
+
if (!gitDir)
|
|
238
|
+
return;
|
|
239
|
+
if (excludedGitDirs.has(gitDir))
|
|
240
|
+
return;
|
|
241
|
+
const excludePath = path.join(gitDir, "info", "exclude");
|
|
242
|
+
let content = "";
|
|
243
|
+
try {
|
|
244
|
+
content = fsSync.readFileSync(excludePath, "utf8");
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// File doesn't exist yet
|
|
248
|
+
}
|
|
249
|
+
const marker = "# wave-runtime";
|
|
250
|
+
if (content.includes(marker)) {
|
|
251
|
+
excludedGitDirs.add(gitDir);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const block = [
|
|
255
|
+
marker,
|
|
256
|
+
"**/.wave/scheduled_tasks.lock",
|
|
257
|
+
"**/.wave/scheduled_tasks.json",
|
|
258
|
+
"**/.wave/worktrees/",
|
|
259
|
+
"**/.wave/settings.local.json",
|
|
260
|
+
"",
|
|
261
|
+
].join("\n");
|
|
262
|
+
fsSync.mkdirSync(path.join(gitDir, "info"), { recursive: true });
|
|
263
|
+
fsSync.appendFileSync(excludePath, block);
|
|
264
|
+
excludedGitDirs.add(gitDir);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// Best-effort: ignore all errors
|
|
268
|
+
}
|
|
269
|
+
}
|
|
219
270
|
/**
|
|
220
271
|
* Check if there are uncommitted changes in the working directory
|
|
221
272
|
* @param cwd Working directory
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groupMessagesByApiRound.d.ts","sourceRoot":"","sources":["../../src/utils/groupMessagesByApiRound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"groupMessagesByApiRound.d.ts","sourceRoot":"","sources":["../../src/utils/groupMessagesByApiRound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CA8DvE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,EAAE,CAIX"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { estimateTokens as estimateStrTokens } from "./tokenEstimate.js";
|
|
1
2
|
/**
|
|
2
3
|
* Groups messages into "API rounds" — each round corresponds to one API
|
|
3
4
|
* call-response cycle. This is critical because in agentic sessions with a
|
|
@@ -74,24 +75,24 @@ export function getLastApiRounds(messages, roundCount) {
|
|
|
74
75
|
return lastRounds.flatMap((r) => r.messages);
|
|
75
76
|
}
|
|
76
77
|
/**
|
|
77
|
-
*
|
|
78
|
+
* Estimate token count from message blocks using CJK-aware estimation.
|
|
78
79
|
*/
|
|
79
80
|
function estimateTokens(messages) {
|
|
80
|
-
let
|
|
81
|
+
let combined = "";
|
|
81
82
|
for (const msg of messages) {
|
|
82
83
|
for (const block of msg.blocks) {
|
|
83
84
|
if ("content" in block && typeof block.content === "string") {
|
|
84
|
-
|
|
85
|
+
combined += block.content;
|
|
85
86
|
}
|
|
86
87
|
if (block.type === "tool" &&
|
|
87
88
|
block.parameters &&
|
|
88
89
|
typeof block.parameters === "string") {
|
|
89
|
-
|
|
90
|
+
combined += block.parameters;
|
|
90
91
|
}
|
|
91
92
|
if (block.type === "tool" && block.result) {
|
|
92
|
-
|
|
93
|
+
combined += block.result;
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
|
-
return
|
|
97
|
+
return estimateStrTokens(combined);
|
|
97
98
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CJK-aware token estimation without external dependencies.
|
|
3
|
+
*
|
|
4
|
+
* The naive `length / 4` heuristic under-estimates CJK text by ~4x because
|
|
5
|
+
* each Chinese/Japanese/Korean character is typically 1-2 tokens, not 0.25.
|
|
6
|
+
* This function separates CJK characters from other text and applies
|
|
7
|
+
* different ratios:
|
|
8
|
+
*
|
|
9
|
+
* - CJK characters: 1 char ≈ 1 token
|
|
10
|
+
* - Other characters: 4 chars ≈ 1 token (2 for JSON/JSONL/JSONC)
|
|
11
|
+
*
|
|
12
|
+
* The estimate intentionally leans high for CJK (safe direction for limit
|
|
13
|
+
* checks — better to reject than to let oversized content through).
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Estimate token count for a string, with CJK-awareness.
|
|
17
|
+
*
|
|
18
|
+
* @param content - The text content to estimate
|
|
19
|
+
* @param ext - File extension (without dot), e.g. "ts", "json". JSON/JSONL/JSONC
|
|
20
|
+
* files use a tighter ratio (2 chars/token) for non-CJK text.
|
|
21
|
+
* @returns Estimated token count
|
|
22
|
+
*/
|
|
23
|
+
export declare function estimateTokens(content: string, ext?: string): number;
|
|
24
|
+
//# sourceMappingURL=tokenEstimate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenEstimate.d.ts","sourceRoot":"","sources":["../../src/utils/tokenEstimate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAMpE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CJK-aware token estimation without external dependencies.
|
|
3
|
+
*
|
|
4
|
+
* The naive `length / 4` heuristic under-estimates CJK text by ~4x because
|
|
5
|
+
* each Chinese/Japanese/Korean character is typically 1-2 tokens, not 0.25.
|
|
6
|
+
* This function separates CJK characters from other text and applies
|
|
7
|
+
* different ratios:
|
|
8
|
+
*
|
|
9
|
+
* - CJK characters: 1 char ≈ 1 token
|
|
10
|
+
* - Other characters: 4 chars ≈ 1 token (2 for JSON/JSONL/JSONC)
|
|
11
|
+
*
|
|
12
|
+
* The estimate intentionally leans high for CJK (safe direction for limit
|
|
13
|
+
* checks — better to reject than to let oversized content through).
|
|
14
|
+
*/
|
|
15
|
+
// CJK Unified Ideographs + Extension A + Hiragana + Katakana + Hangul Syllables
|
|
16
|
+
const CJK_REGEX = /[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g;
|
|
17
|
+
/**
|
|
18
|
+
* Estimate token count for a string, with CJK-awareness.
|
|
19
|
+
*
|
|
20
|
+
* @param content - The text content to estimate
|
|
21
|
+
* @param ext - File extension (without dot), e.g. "ts", "json". JSON/JSONL/JSONC
|
|
22
|
+
* files use a tighter ratio (2 chars/token) for non-CJK text.
|
|
23
|
+
* @returns Estimated token count
|
|
24
|
+
*/
|
|
25
|
+
export function estimateTokens(content, ext) {
|
|
26
|
+
const cjkCount = (content.match(CJK_REGEX) || []).length;
|
|
27
|
+
const otherCount = content.length - cjkCount;
|
|
28
|
+
const bytesPerToken = ext === "json" || ext === "jsonl" || ext === "jsonc" ? 2 : 4;
|
|
29
|
+
return Math.ceil(cjkCount + otherCount / bytesPerToken);
|
|
30
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeUtils.d.ts","sourceRoot":"","sources":["../../src/utils/worktreeUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"worktreeUtils.d.ts","sourceRoot":"","sources":["../../src/utils/worktreeUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAmBvD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CA6B7C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAgKtE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAmEvD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,GAAG,SAAS,GACrC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAsClD"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
|
-
import { getGitMainRepoRoot, getDefaultRemoteBranch } from "./gitUtils.js";
|
|
8
|
+
import { getGitMainRepoRoot, getDefaultRemoteBranch, ensureWaveRuntimeFilesExcluded, } from "./gitUtils.js";
|
|
9
9
|
import { logger } from "./globalLogger.js";
|
|
10
10
|
/**
|
|
11
11
|
* Validate a worktree name to prevent path traversal and invalid characters.
|
|
@@ -79,6 +79,8 @@ export function createWorktree(name, cwd) {
|
|
|
79
79
|
const worktreePath = path.join(repoRoot, ".wave", "worktrees", name);
|
|
80
80
|
const branchName = `worktree-${name}`;
|
|
81
81
|
const baseBranch = getDefaultRemoteBranch(cwd);
|
|
82
|
+
// Ensure Wave runtime files are git-excluded in this repo
|
|
83
|
+
ensureWaveRuntimeFilesExcluded(cwd);
|
|
82
84
|
// Ensure parent directory exists
|
|
83
85
|
const parentDir = path.dirname(worktreePath);
|
|
84
86
|
if (!fs.existsSync(parentDir)) {
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import * as aiService from "../services/aiService.js";
|
|
|
3
3
|
import { convertMessagesForAPI } from "../utils/convertMessagesForAPI.js";
|
|
4
4
|
import { parseTaskNotificationXml } from "../utils/notificationXml.js";
|
|
5
5
|
import { calculateComprehensiveTotalTokens } from "../utils/tokenCalculation.js";
|
|
6
|
+
import { estimateTokens } from "../utils/tokenEstimate.js";
|
|
6
7
|
import {
|
|
7
8
|
getTaskReminderTurnCounts,
|
|
8
9
|
maybeInjectTaskReminder,
|
|
@@ -606,7 +607,7 @@ export class AIManager {
|
|
|
606
607
|
);
|
|
607
608
|
let usedTokens = 0;
|
|
608
609
|
for (const file of recentFiles) {
|
|
609
|
-
const fileTokens =
|
|
610
|
+
const fileTokens = estimateTokens(file.content);
|
|
610
611
|
if (usedTokens + fileTokens > POST_COMPACT_MAX_TOKENS_PER_FILE) continue;
|
|
611
612
|
if (fileTokens > 0) usedTokens += fileTokens;
|
|
612
613
|
contextParts.push(`\n\n## ${file.path}\n\`\`\`\n${file.content}\n\`\`\``);
|
|
@@ -639,7 +640,7 @@ export class AIManager {
|
|
|
639
640
|
skillContent.slice(0, maxSkillChars) + "\n\n...[truncated]...";
|
|
640
641
|
}
|
|
641
642
|
|
|
642
|
-
const skillTokens =
|
|
643
|
+
const skillTokens = estimateTokens(skillContent);
|
|
643
644
|
if (skillsUsedTokens + skillTokens > POST_COMPACT_SKILLS_TOKEN_BUDGET)
|
|
644
645
|
break;
|
|
645
646
|
skillsUsedTokens += skillTokens;
|
|
@@ -813,6 +814,7 @@ export class AIManager {
|
|
|
813
814
|
let turnDepth = turnOffset;
|
|
814
815
|
|
|
815
816
|
inner: while (true) {
|
|
817
|
+
let llmSpan: import("@opentelemetry/api").Span | undefined;
|
|
816
818
|
try {
|
|
817
819
|
// Save session in each iteration to ensure message persistence
|
|
818
820
|
await this.messageManager.saveSession();
|
|
@@ -949,15 +951,21 @@ export class AIManager {
|
|
|
949
951
|
};
|
|
950
952
|
}
|
|
951
953
|
|
|
952
|
-
|
|
954
|
+
llmSpan = startLLMRequestSpan(
|
|
955
|
+
model || this.getModelConfig().model || "",
|
|
956
|
+
);
|
|
953
957
|
|
|
954
958
|
const result = await aiService.callAgent(callAgentOptions);
|
|
955
959
|
|
|
956
960
|
// End LLM span with usage data
|
|
957
|
-
endLLMRequestSpan({
|
|
961
|
+
endLLMRequestSpan(llmSpan, {
|
|
958
962
|
model: model || this.getModelConfig().model || "",
|
|
959
963
|
success: true,
|
|
960
964
|
hasToolCall: !!(result.tool_calls && result.tool_calls.length > 0),
|
|
965
|
+
inputTokens: result.usage?.prompt_tokens,
|
|
966
|
+
outputTokens: result.usage?.completion_tokens,
|
|
967
|
+
cacheReadTokens: result.usage?.cache_read_input_tokens,
|
|
968
|
+
cacheCreationTokens: result.usage?.cache_creation_input_tokens,
|
|
961
969
|
});
|
|
962
970
|
|
|
963
971
|
const createdByStreaming = assistantMessageCreated;
|
|
@@ -1238,7 +1246,7 @@ export class AIManager {
|
|
|
1238
1246
|
break inner;
|
|
1239
1247
|
} catch (error) {
|
|
1240
1248
|
// End LLM span with error
|
|
1241
|
-
endLLMRequestSpan({
|
|
1249
|
+
endLLMRequestSpan(llmSpan, {
|
|
1242
1250
|
model: model || this.getModelConfig().model || "",
|
|
1243
1251
|
success: false,
|
|
1244
1252
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -1653,6 +1661,7 @@ export class AIManager {
|
|
|
1653
1661
|
shortResult: toolResult.shortResult,
|
|
1654
1662
|
isManuallyBackgrounded: toolResult.isManuallyBackgrounded,
|
|
1655
1663
|
startLineNumber: toolResult.startLineNumber,
|
|
1664
|
+
images: toolResult.images,
|
|
1656
1665
|
timestamp: Date.now(),
|
|
1657
1666
|
});
|
|
1658
1667
|
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
releaseSchedulerLock,
|
|
16
16
|
registerSchedulerLockCleanup,
|
|
17
17
|
} from "../utils/cronTasksLock.js";
|
|
18
|
+
import { ensureWaveRuntimeFilesExcluded } from "../utils/gitUtils.js";
|
|
18
19
|
|
|
19
20
|
export class CronManager {
|
|
20
21
|
private jobs = new Map<string, CronJob>();
|
|
@@ -100,6 +101,7 @@ export class CronManager {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
private tryLock(workdir: string): void {
|
|
104
|
+
ensureWaveRuntimeFilesExcluded(workdir);
|
|
103
105
|
tryAcquireSchedulerLock({ dir: workdir, sessionId: this.sessionId })
|
|
104
106
|
.then((acquired) => {
|
|
105
107
|
if (acquired) {
|
|
@@ -17,7 +17,6 @@ import type { HookManager } from "./hookManager.js";
|
|
|
17
17
|
import type { PermissionManager } from "./permissionManager.js";
|
|
18
18
|
import { isValidHookEvent } from "../types/hooks.js";
|
|
19
19
|
import { ConfigurationService } from "../services/configurationService.js";
|
|
20
|
-
import { ensureGlobalGitIgnore } from "../utils/fileUtils.js";
|
|
21
20
|
import { Container } from "../utils/container.js";
|
|
22
21
|
|
|
23
22
|
import type {
|
|
@@ -97,9 +96,6 @@ export class LiveConfigManager {
|
|
|
97
96
|
if (projectPaths) {
|
|
98
97
|
for (const projectPath of projectPaths) {
|
|
99
98
|
if (existsSync(projectPath)) {
|
|
100
|
-
if (projectPath.endsWith("settings.local.json")) {
|
|
101
|
-
await ensureGlobalGitIgnore("**/.wave/settings.local.json");
|
|
102
|
-
}
|
|
103
99
|
await this.fileWatcher.watchFile(projectPath, (event) =>
|
|
104
100
|
this.handleFileChange(event, "project"),
|
|
105
101
|
);
|
|
@@ -364,14 +360,6 @@ export class LiveConfigManager {
|
|
|
364
360
|
|
|
365
361
|
// Handle file creation or modification
|
|
366
362
|
if (event.type === "change" || event.type === "create") {
|
|
367
|
-
if (
|
|
368
|
-
source === "project" &&
|
|
369
|
-
event.path.endsWith("settings.local.json") &&
|
|
370
|
-
event.type === "create"
|
|
371
|
-
) {
|
|
372
|
-
await ensureGlobalGitIgnore("**/.wave/settings.local.json");
|
|
373
|
-
}
|
|
374
|
-
|
|
375
363
|
// Add small delay to ensure file write is complete
|
|
376
364
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
377
365
|
|
|
@@ -848,9 +848,16 @@ export class McpManager {
|
|
|
848
848
|
textContent.push(String(result.content));
|
|
849
849
|
}
|
|
850
850
|
|
|
851
|
+
const textContentStr =
|
|
852
|
+
textContent.length > 0
|
|
853
|
+
? textContent.join("\n")
|
|
854
|
+
: images.length > 0
|
|
855
|
+
? `Tool returned ${images.length} image(s).`
|
|
856
|
+
: "No content";
|
|
857
|
+
|
|
851
858
|
return {
|
|
852
859
|
success: true,
|
|
853
|
-
content:
|
|
860
|
+
content: textContentStr,
|
|
854
861
|
images: images.length > 0 ? images : undefined,
|
|
855
862
|
serverName,
|
|
856
863
|
};
|
|
@@ -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";
|
|
@@ -1063,7 +1064,7 @@ export class MessageManager {
|
|
|
1063
1064
|
/**
|
|
1064
1065
|
* Get recent file read contents, sorted by timestamp (newest first).
|
|
1065
1066
|
* @param maxFiles - Maximum number of files to return
|
|
1066
|
-
* @param maxTokensPerFile - Maximum tokens per file (
|
|
1067
|
+
* @param maxTokensPerFile - Maximum tokens per file (CJK-aware estimation)
|
|
1067
1068
|
* @returns Array of { path, content } sorted by recency
|
|
1068
1069
|
*/
|
|
1069
1070
|
public getRecentFileReads(
|
|
@@ -1076,10 +1077,14 @@ export class MessageManager {
|
|
|
1076
1077
|
|
|
1077
1078
|
const result: Array<{ path: string; content: string }> = [];
|
|
1078
1079
|
for (const [path, { content }] of sorted) {
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1080
|
+
const tokenCount = estimateTokens(content);
|
|
1081
|
+
let truncated = content;
|
|
1082
|
+
if (tokenCount > maxTokensPerFile) {
|
|
1083
|
+
// Truncate proportionally to fit within the token budget
|
|
1084
|
+
const ratio = maxTokensPerFile / tokenCount;
|
|
1085
|
+
const cutIndex = Math.floor(content.length * ratio);
|
|
1086
|
+
truncated = content.slice(0, cutIndex);
|
|
1087
|
+
}
|
|
1083
1088
|
result.push({ path, content: truncated });
|
|
1084
1089
|
}
|
|
1085
1090
|
return result;
|
|
@@ -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) {
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
mergeRemoteSettings,
|
|
48
48
|
} from "./remoteSettingsService.js";
|
|
49
49
|
import { createAuthAwareFetch } from "./authService.js";
|
|
50
|
+
import { ensureWaveRuntimeFilesExcluded } from "../utils/gitUtils.js";
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
53
|
* Default ConfigurationService implementation
|
|
@@ -791,6 +792,7 @@ export class ConfigurationService {
|
|
|
791
792
|
|
|
792
793
|
// Ensure .wave directory exists
|
|
793
794
|
const waveDir = path.join(workdir, ".wave");
|
|
795
|
+
ensureWaveRuntimeFilesExcluded(workdir);
|
|
794
796
|
if (!existsSync(waveDir)) {
|
|
795
797
|
await fs.mkdir(waveDir, { recursive: true });
|
|
796
798
|
}
|