wave-agent-sdk 0.18.5 → 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.
Files changed (60) hide show
  1. package/dist/managers/aiManager.d.ts.map +1 -1
  2. package/dist/managers/aiManager.js +92 -87
  3. package/dist/managers/cronManager.d.ts.map +1 -1
  4. package/dist/managers/cronManager.js +2 -0
  5. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  6. package/dist/managers/liveConfigManager.js +0 -9
  7. package/dist/managers/mcpManager.d.ts.map +1 -1
  8. package/dist/managers/mcpManager.js +6 -1
  9. package/dist/managers/messageManager.d.ts +1 -1
  10. package/dist/managers/messageManager.d.ts.map +1 -1
  11. package/dist/managers/messageManager.js +10 -4
  12. package/dist/managers/toolManager.d.ts.map +1 -1
  13. package/dist/managers/toolManager.js +2 -0
  14. package/dist/services/configurationService.d.ts.map +1 -1
  15. package/dist/services/configurationService.js +2 -0
  16. package/dist/telemetry/sessionTracing.d.ts +17 -16
  17. package/dist/telemetry/sessionTracing.d.ts.map +1 -1
  18. package/dist/telemetry/sessionTracing.js +62 -58
  19. package/dist/tools/readTool.d.ts.map +1 -1
  20. package/dist/tools/readTool.js +6 -7
  21. package/dist/types/telemetry.d.ts +2 -0
  22. package/dist/types/telemetry.d.ts.map +1 -1
  23. package/dist/utils/fileUtils.d.ts +0 -6
  24. package/dist/utils/fileUtils.d.ts.map +1 -1
  25. package/dist/utils/fileUtils.js +0 -43
  26. package/dist/utils/gitUtils.d.ts +11 -0
  27. package/dist/utils/gitUtils.d.ts.map +1 -1
  28. package/dist/utils/gitUtils.js +51 -0
  29. package/dist/utils/groupMessagesByApiRound.d.ts.map +1 -1
  30. package/dist/utils/groupMessagesByApiRound.js +7 -6
  31. package/dist/utils/openaiClient.d.ts.map +1 -1
  32. package/dist/utils/openaiClient.js +2 -0
  33. package/dist/utils/tokenEstimate.d.ts +24 -0
  34. package/dist/utils/tokenEstimate.d.ts.map +1 -0
  35. package/dist/utils/tokenEstimate.js +30 -0
  36. package/dist/utils/worktreeUtils.d.ts.map +1 -1
  37. package/dist/utils/worktreeUtils.js +3 -1
  38. package/package.json +1 -1
  39. package/scripts/install_ripgrep.js +1 -21
  40. package/src/managers/aiManager.ts +111 -102
  41. package/src/managers/cronManager.ts +2 -0
  42. package/src/managers/liveConfigManager.ts +0 -12
  43. package/src/managers/mcpManager.ts +8 -1
  44. package/src/managers/messageManager.ts +10 -5
  45. package/src/managers/toolManager.ts +2 -0
  46. package/src/services/configurationService.ts +2 -0
  47. package/src/telemetry/sessionTracing.ts +64 -67
  48. package/src/tools/readTool.ts +6 -12
  49. package/src/types/telemetry.ts +2 -0
  50. package/src/utils/fileUtils.ts +0 -46
  51. package/src/utils/gitUtils.ts +54 -0
  52. package/src/utils/groupMessagesByApiRound.ts +7 -6
  53. package/src/utils/openaiClient.ts +2 -0
  54. package/src/utils/tokenEstimate.ts +34 -0
  55. package/src/utils/worktreeUtils.ts +8 -1
  56. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  57. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  58. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  59. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  60. package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
@@ -5,6 +5,7 @@ import { logger } from "../utils/globalLogger.js";
5
5
  import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
6
6
  import { resolvePath, getDisplayPath } from "../utils/path.js";
7
7
  import { formatLineNumberPrefix } from "../utils/stringUtils.js";
8
+ import { estimateTokens } from "../utils/tokenEstimate.js";
8
9
  import {
9
10
  isBinaryDocument,
10
11
  getBinaryDocumentError,
@@ -267,14 +268,11 @@ Usage:
267
268
  }
268
269
  }
269
270
 
270
- // Resource Limits
271
+ // Resource Limits — align with Claude Code: 256KB default, bypassed only
272
+ // when the caller provides an explicit line limit (not offset alone).
271
273
  const maxSizeBytes =
272
- context.fileReadingLimits?.maxSizeBytes ?? 1024 * 1024; // Default 1MB
273
- if (
274
- stats.size > maxSizeBytes &&
275
- typeof offset !== "number" &&
276
- typeof limit !== "number"
277
- ) {
274
+ context.fileReadingLimits?.maxSizeBytes ?? 0.25 * 1024 * 1024; // Default 256KB
275
+ if (stats.size > maxSizeBytes && typeof limit !== "number") {
278
276
  return {
279
277
  success: false,
280
278
  content: "",
@@ -357,11 +355,7 @@ Usage:
357
355
  // Token-level validation: estimate tokens and reject if over limit
358
356
  const maxTokens = context.fileReadingLimits?.maxTokens ?? 25000; // Default 25000 tokens
359
357
  const ext = extname(actualFilePath).toLowerCase().slice(1);
360
- const bytesPerToken =
361
- ext === "json" || ext === "jsonl" || ext === "jsonc" ? 2 : 4;
362
- const estimatedTokens = Math.ceil(
363
- formattedContent.length / bytesPerToken,
364
- );
358
+ const estimatedTokens = estimateTokens(formattedContent, ext);
365
359
  if (estimatedTokens > maxTokens) {
366
360
  return {
367
361
  success: false,
@@ -68,6 +68,8 @@ export interface ToolMetadata {
68
68
  error?: string;
69
69
  /** Execution time (ms) */
70
70
  durationMs: number;
71
+ /** Tool output content (recorded when logToolContent is true) */
72
+ output?: string;
71
73
  }
72
74
 
73
75
  /** Event names for OTel structured logging */
@@ -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
  /**
@@ -156,50 +154,6 @@ export async function getLastLine(
156
154
  }
157
155
  }
158
156
 
159
- /**
160
- * Ensures that a pattern is present in the global git ignore file.
161
- *
162
- * @param {string} pattern - The pattern to add to global git ignore.
163
- */
164
- export async function ensureGlobalGitIgnore(pattern: string): Promise<void> {
165
- try {
166
- let globalIgnorePath: string;
167
- try {
168
- globalIgnorePath = execSync("git config --get core.excludesfile", {
169
- encoding: "utf8",
170
- }).trim();
171
- } catch {
172
- // If not set, use default paths
173
- const xdgConfigHome =
174
- process.env.XDG_CONFIG_HOME || path.join(homedir(), ".config");
175
- globalIgnorePath = path.join(xdgConfigHome, "git", "ignore");
176
- }
177
-
178
- if (!globalIgnorePath) return;
179
-
180
- // Ensure directory exists
181
- await fs.mkdir(path.dirname(globalIgnorePath), { recursive: true });
182
-
183
- let content = "";
184
- try {
185
- content = await fs.readFile(globalIgnorePath, "utf8");
186
- } catch {
187
- // File doesn't exist
188
- }
189
-
190
- const lines = content.split("\n").map((line) => line.trim());
191
- if (!lines.includes(pattern)) {
192
- const newContent =
193
- content.endsWith("\n") || content === ""
194
- ? `${content}${pattern}\n`
195
- : `${content}\n${pattern}\n`;
196
- await fs.writeFile(globalIgnorePath, newContent, "utf8");
197
- }
198
- } catch {
199
- // Ignore errors
200
- }
201
- }
202
-
203
157
  /**
204
158
  * Simple Levenshtein distance implementation
205
159
  */
@@ -222,6 +222,60 @@ export function getDefaultRemoteBranch(cwd: string): string {
222
222
  return "main";
223
223
  }
224
224
 
225
+ /**
226
+ * Module-level set tracking git dirs already processed in this process,
227
+ * to avoid redundant I/O.
228
+ */
229
+ const excludedGitDirs = new Set<string>();
230
+
231
+ /**
232
+ * Ensure that Wave runtime files are excluded from git status by writing
233
+ * patterns to `.git/info/exclude` (per-repo, not global).
234
+ *
235
+ * Idempotent: if the marker `# wave-runtime` is already present in the
236
+ * exclude file, it skips writing. A module-level Set provides additional
237
+ * per-process dedup to avoid redundant file reads.
238
+ *
239
+ * @param cwd Working directory to start searching from
240
+ */
241
+ export function ensureWaveRuntimeFilesExcluded(cwd: string): void {
242
+ try {
243
+ const gitDir = resolveGitDir(cwd);
244
+ if (!gitDir) return;
245
+
246
+ if (excludedGitDirs.has(gitDir)) return;
247
+
248
+ const excludePath = path.join(gitDir, "info", "exclude");
249
+ let content = "";
250
+ try {
251
+ content = fsSync.readFileSync(excludePath, "utf8");
252
+ } catch {
253
+ // File doesn't exist yet
254
+ }
255
+
256
+ const marker = "# wave-runtime";
257
+ if (content.includes(marker)) {
258
+ excludedGitDirs.add(gitDir);
259
+ return;
260
+ }
261
+
262
+ const block = [
263
+ marker,
264
+ "**/.wave/scheduled_tasks.lock",
265
+ "**/.wave/scheduled_tasks.json",
266
+ "**/.wave/worktrees/",
267
+ "**/.wave/settings.local.json",
268
+ "",
269
+ ].join("\n");
270
+
271
+ fsSync.mkdirSync(path.join(gitDir, "info"), { recursive: true });
272
+ fsSync.appendFileSync(excludePath, block);
273
+ excludedGitDirs.add(gitDir);
274
+ } catch {
275
+ // Best-effort: ignore all errors
276
+ }
277
+ }
278
+
225
279
  /**
226
280
  * Check if there are uncommitted changes in the working directory
227
281
  * @param cwd Working directory
@@ -1,4 +1,5 @@
1
1
  import type { Message } from "../types/index.js";
2
+ import { estimateTokens as estimateStrTokens } from "./tokenEstimate.js";
2
3
 
3
4
  export interface ApiRound {
4
5
  messages: Message[];
@@ -95,26 +96,26 @@ export function getLastApiRounds(
95
96
  }
96
97
 
97
98
  /**
98
- * Roughly estimate token count from character count (~4 chars per token).
99
+ * Estimate token count from message blocks using CJK-aware estimation.
99
100
  */
100
101
  function estimateTokens(messages: Message[]): number {
101
- let chars = 0;
102
+ let combined = "";
102
103
  for (const msg of messages) {
103
104
  for (const block of msg.blocks) {
104
105
  if ("content" in block && typeof block.content === "string") {
105
- chars += block.content.length;
106
+ combined += block.content;
106
107
  }
107
108
  if (
108
109
  block.type === "tool" &&
109
110
  block.parameters &&
110
111
  typeof block.parameters === "string"
111
112
  ) {
112
- chars += block.parameters.length;
113
+ combined += block.parameters;
113
114
  }
114
115
  if (block.type === "tool" && block.result) {
115
- chars += block.result.length;
116
+ combined += block.result;
116
117
  }
117
118
  }
118
119
  }
119
- return Math.ceil(chars / 4);
120
+ return estimateStrTokens(combined);
120
121
  }
@@ -133,6 +133,7 @@ export class OpenAIClient {
133
133
  }
134
134
  if (attempt < MAX_RETRIES) {
135
135
  logger.warn("OpenAI API network error, retrying...", {
136
+ model: params.model,
136
137
  attempt: attempt + 1,
137
138
  error: e,
138
139
  });
@@ -185,6 +186,7 @@ export class OpenAIClient {
185
186
  if (retryableStatus && attempt < MAX_RETRIES) {
186
187
  lastRetryAfter = response.headers.get("retry-after");
187
188
  logger.warn("OpenAI API error, retrying...", {
189
+ model: params.model,
188
190
  attempt: attempt + 1,
189
191
  status: response.status,
190
192
  retryAfter: lastRetryAfter,
@@ -0,0 +1,34 @@
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
+ // CJK Unified Ideographs + Extension A + Hiragana + Katakana + Hangul Syllables
17
+ const CJK_REGEX =
18
+ /[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g;
19
+
20
+ /**
21
+ * Estimate token count for a string, with CJK-awareness.
22
+ *
23
+ * @param content - The text content to estimate
24
+ * @param ext - File extension (without dot), e.g. "ts", "json". JSON/JSONL/JSONC
25
+ * files use a tighter ratio (2 chars/token) for non-CJK text.
26
+ * @returns Estimated token count
27
+ */
28
+ export function estimateTokens(content: string, ext?: string): number {
29
+ const cjkCount = (content.match(CJK_REGEX) || []).length;
30
+ const otherCount = content.length - cjkCount;
31
+ const bytesPerToken =
32
+ ext === "json" || ext === "jsonl" || ext === "jsonc" ? 2 : 4;
33
+ return Math.ceil(cjkCount + otherCount / bytesPerToken);
34
+ }
@@ -6,7 +6,11 @@
6
6
  import { execFileSync } from "node:child_process";
7
7
  import * as path from "node:path";
8
8
  import * as fs from "node:fs";
9
- import { getGitMainRepoRoot, getDefaultRemoteBranch } from "./gitUtils.js";
9
+ import {
10
+ getGitMainRepoRoot,
11
+ getDefaultRemoteBranch,
12
+ ensureWaveRuntimeFilesExcluded,
13
+ } from "./gitUtils.js";
10
14
  import { logger } from "./globalLogger.js";
11
15
 
12
16
  export interface WorktreeInfo {
@@ -105,6 +109,9 @@ export function createWorktree(name: string, cwd: string): WorktreeInfo {
105
109
  const branchName = `worktree-${name}`;
106
110
  const baseBranch = getDefaultRemoteBranch(cwd);
107
111
 
112
+ // Ensure Wave runtime files are git-excluded in this repo
113
+ ensureWaveRuntimeFilesExcluded(cwd);
114
+
108
115
  // Ensure parent directory exists
109
116
  const parentDir = path.dirname(worktreePath);
110
117
  if (!fs.existsSync(parentDir)) {
Binary file
Binary file
Binary file