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
@@ -2,15 +2,18 @@
2
2
  * Session Tracing -- OpenTelemetry Span Management
3
3
  *
4
4
  * Provides span creation/ending APIs for interactions, LLM requests, and tool
5
- * executions with AsyncLocalStorage context propagation and stale span cleanup.
5
+ * executions. Uses two independent AsyncLocalStorage contexts:
6
+ * - interactionContext: holds the interaction span for the entire turn
7
+ * - toolContext: holds the current tool span (cleared on end)
8
+ *
9
+ * LLM request spans do not enter any ALS; they are passed explicitly to
10
+ * endLLMRequestSpan, aligning with Claude Code's approach.
6
11
  */
7
12
  import { AsyncLocalStorage } from "node:async_hooks";
8
13
  import { getOTELApi, isInitialized, getCurrentConfig, } from "./instrumentation.js";
9
14
  // -- AsyncLocalStorage for context propagation --
10
- const spanContext = new AsyncLocalStorage();
11
- // -- LIFO stacks for nested span tracking --
12
- const llmSpans = [];
13
- const toolSpans = [];
15
+ const interactionContext = new AsyncLocalStorage();
16
+ const toolContext = new AsyncLocalStorage();
14
17
  // -- Tracer accessor --
15
18
  function getTracer() {
16
19
  if (!isInitialized())
@@ -20,29 +23,15 @@ function getTracer() {
20
23
  return undefined;
21
24
  return otelApi.trace.getTracer("wave");
22
25
  }
23
- // -- Helper: create child span with parent context --
24
- function startChildSpan(name, attributes) {
25
- const tracer = getTracer();
26
- if (!tracer)
27
- return undefined;
28
- const parent = spanContext.getStore();
29
- let span;
30
- if (parent) {
31
- const otelApi = getOTELApi();
32
- const ctx = otelApi.trace.setSpan(otelApi.context.active(), parent);
33
- span = tracer.startSpan(name, { attributes }, ctx);
34
- }
35
- else {
36
- span = tracer.startSpan(name, { attributes });
37
- }
38
- spanContext.enterWith(span);
39
- return span;
40
- }
41
26
  // -- Public API --
42
27
  /**
43
28
  * Creates an interaction span for a user turn.
29
+ * The span is stored in interactionContext for the duration of the turn.
44
30
  */
45
31
  export function startInteractionSpan(userPrompt, sequence) {
32
+ const tracer = getTracer();
33
+ if (!tracer)
34
+ return undefined;
46
35
  const config = getCurrentConfig();
47
36
  const attributes = {
48
37
  "span.type": "interaction",
@@ -52,21 +41,28 @@ export function startInteractionSpan(userPrompt, sequence) {
52
41
  if (config?.logUserPrompts) {
53
42
  attributes.user_prompt = userPrompt;
54
43
  }
55
- return startChildSpan("interaction", attributes);
44
+ const span = tracer.startSpan("interaction", { attributes });
45
+ interactionContext.enterWith(span);
46
+ return span;
56
47
  }
57
48
  /**
58
- * Ends the current active interaction span.
49
+ * Ends the current interaction span and clears the context.
59
50
  */
60
51
  export function endInteractionSpan() {
61
- const span = spanContext.getStore();
52
+ const span = interactionContext.getStore();
62
53
  if (!span)
63
54
  return;
64
55
  span.end();
56
+ interactionContext.enterWith(undefined);
65
57
  }
66
58
  /**
67
- * Creates an LLM request span as a child of the current active span.
59
+ * Creates an LLM request span as a child of the interaction span.
60
+ * Does NOT enter any ALS — the span must be passed explicitly to endLLMRequestSpan.
68
61
  */
69
62
  export function startLLMRequestSpan(model, options) {
63
+ const tracer = getTracer();
64
+ if (!tracer)
65
+ return undefined;
70
66
  const attributes = {
71
67
  "span.type": "llm_request",
72
68
  model,
@@ -74,17 +70,23 @@ export function startLLMRequestSpan(model, options) {
74
70
  if (options?.context) {
75
71
  attributes["llm_request.context"] = options.context;
76
72
  }
77
- const span = startChildSpan("llm.request", attributes);
78
- if (span) {
79
- llmSpans.push(span);
73
+ const parent = interactionContext.getStore();
74
+ let span;
75
+ if (parent) {
76
+ const otelApi = getOTELApi();
77
+ const ctx = otelApi.trace.setSpan(otelApi.context.active(), parent);
78
+ span = tracer.startSpan("llm.request", { attributes }, ctx);
79
+ }
80
+ else {
81
+ span = tracer.startSpan("llm.request", { attributes });
80
82
  }
81
83
  return span;
82
84
  }
83
85
  /**
84
- * Ends the most recent LLM request span with response metadata.
86
+ * Ends an LLM request span with response metadata.
87
+ * The span is passed explicitly — no ALS is read or modified.
85
88
  */
86
- export function endLLMRequestSpan(metadata) {
87
- const span = llmSpans.pop();
89
+ export function endLLMRequestSpan(span, metadata) {
88
90
  if (!span)
89
91
  return;
90
92
  if (metadata.inputTokens != null)
@@ -105,14 +107,15 @@ export function endLLMRequestSpan(metadata) {
105
107
  if (metadata.hasToolCall != null)
106
108
  span.setAttribute("has_tool_call", metadata.hasToolCall);
107
109
  span.end();
108
- if (llmSpans.length > 0) {
109
- spanContext.enterWith(llmSpans[llmSpans.length - 1]);
110
- }
111
110
  }
112
111
  /**
113
- * Creates a tool execution span as a child of the current active span.
112
+ * Creates a tool execution span as a child of the interaction span.
113
+ * Enters toolContext with the new span.
114
114
  */
115
115
  export function startToolSpan(toolName, input) {
116
+ const tracer = getTracer();
117
+ if (!tracer)
118
+ return undefined;
116
119
  const config = getCurrentConfig();
117
120
  const attributes = {
118
121
  "span.type": "tool",
@@ -125,38 +128,39 @@ export function startToolSpan(toolName, input) {
125
128
  }
126
129
  attributes.tool_input = inputStr;
127
130
  }
128
- const span = startChildSpan(`tool.${toolName}`, attributes);
129
- if (span) {
130
- toolSpans.push(span);
131
+ const parent = interactionContext.getStore();
132
+ let span;
133
+ if (parent) {
134
+ const otelApi = getOTELApi();
135
+ const ctx = otelApi.trace.setSpan(otelApi.context.active(), parent);
136
+ span = tracer.startSpan(`tool.${toolName}`, { attributes }, ctx);
131
137
  }
138
+ else {
139
+ span = tracer.startSpan(`tool.${toolName}`, { attributes });
140
+ }
141
+ toolContext.enterWith(span);
132
142
  return span;
133
143
  }
134
144
  /**
135
- * Ends a tool span with execution metadata.
145
+ * Ends the current tool span with execution metadata.
146
+ * Reads the span from toolContext, then clears it.
136
147
  */
137
148
  export function endToolSpan(metadata) {
138
- const span = toolSpans.pop();
149
+ const span = toolContext.getStore();
139
150
  if (!span)
140
151
  return;
141
152
  span.setAttribute("success", metadata.success);
142
153
  if (metadata.error)
143
154
  span.setAttribute("error", metadata.error);
144
155
  span.setAttribute("duration_ms", metadata.durationMs);
145
- span.end();
146
- if (toolSpans.length > 0) {
147
- spanContext.enterWith(toolSpans[toolSpans.length - 1]);
156
+ const config = getCurrentConfig();
157
+ if (config?.logToolContent && metadata.output) {
158
+ let outputStr = metadata.output;
159
+ if (outputStr.length > 1000) {
160
+ outputStr = outputStr.substring(0, 1000);
161
+ }
162
+ span.setAttribute("tool_output", outputStr);
148
163
  }
149
- }
150
- /**
151
- * Returns the current active span from ALS context.
152
- */
153
- export function getActiveInteractionSpan() {
154
- return spanContext.getStore();
155
- }
156
- /**
157
- * Executes `fn` with `span` as the active context via ALS.
158
- * Useful for parallel tool calls that each need their own span context.
159
- */
160
- export function withSpanContext(span, fn) {
161
- return spanContext.run(span, fn);
164
+ span.end();
165
+ toolContext.enterWith(undefined);
162
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"readTool.d.ts","sourceRoot":"","sources":["../../src/tools/readTool.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAkItE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAsStB,CAAC"}
1
+ {"version":3,"file":"readTool.d.ts","sourceRoot":"","sources":["../../src/tools/readTool.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAmItE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA+RtB,CAAC"}
@@ -4,6 +4,7 @@ import { createHash } from "crypto";
4
4
  import { logger } from "../utils/globalLogger.js";
5
5
  import { resolvePath, getDisplayPath } from "../utils/path.js";
6
6
  import { formatLineNumberPrefix } from "../utils/stringUtils.js";
7
+ import { estimateTokens } from "../utils/tokenEstimate.js";
7
8
  import { isBinaryDocument, getBinaryDocumentError, } from "../utils/fileFormat.js";
8
9
  import { convertImageToBase64 } from "../utils/messageOperations.js";
9
10
  import { READ_TOOL_NAME } from "../constants/tools.js";
@@ -226,11 +227,10 @@ Usage:
226
227
  }
227
228
  }
228
229
  }
229
- // Resource Limits
230
- const maxSizeBytes = context.fileReadingLimits?.maxSizeBytes ?? 1024 * 1024; // Default 1MB
231
- if (stats.size > maxSizeBytes &&
232
- typeof offset !== "number" &&
233
- typeof limit !== "number") {
230
+ // Resource Limits — align with Claude Code: 256KB default, bypassed only
231
+ // when the caller provides an explicit line limit (not offset alone).
232
+ const maxSizeBytes = context.fileReadingLimits?.maxSizeBytes ?? 0.25 * 1024 * 1024; // Default 256KB
233
+ if (stats.size > maxSizeBytes && typeof limit !== "number") {
234
234
  return {
235
235
  success: false,
236
236
  content: "",
@@ -299,8 +299,7 @@ Usage:
299
299
  // Token-level validation: estimate tokens and reject if over limit
300
300
  const maxTokens = context.fileReadingLimits?.maxTokens ?? 25000; // Default 25000 tokens
301
301
  const ext = extname(actualFilePath).toLowerCase().slice(1);
302
- const bytesPerToken = ext === "json" || ext === "jsonl" || ext === "jsonc" ? 2 : 4;
303
- const estimatedTokens = Math.ceil(formattedContent.length / bytesPerToken);
302
+ const estimatedTokens = estimateTokens(formattedContent, ext);
304
303
  if (estimatedTokens > maxTokens) {
305
304
  return {
306
305
  success: false,
@@ -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;CACpB;AAED,8CAA8C;AAC9C,MAAM,MAAM,aAAa,GACrB,eAAe,GACf,aAAa,GACb,aAAa,GACb,eAAe,GACf,YAAY,GACZ,OAAO,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":"AAOA;;;;;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;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC1E;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"}
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"}
@@ -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
  */
@@ -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"}
@@ -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;AAEjD,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
+ {"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
- * Roughly estimate token count from character count (~4 chars per token).
78
+ * Estimate token count from message blocks using CJK-aware estimation.
78
79
  */
79
80
  function estimateTokens(messages) {
80
- let chars = 0;
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
- chars += block.content.length;
85
+ combined += block.content;
85
86
  }
86
87
  if (block.type === "tool" &&
87
88
  block.parameters &&
88
89
  typeof block.parameters === "string") {
89
- chars += block.parameters.length;
90
+ combined += block.parameters;
90
91
  }
91
92
  if (block.type === "tool" && block.result) {
92
- chars += block.result.length;
93
+ combined += block.result;
93
94
  }
94
95
  }
95
96
  }
96
- return Math.ceil(chars / 4);
97
+ return estimateStrTokens(combined);
97
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openaiClient.d.ts","sourceRoot":"","sources":["../../src/utils/openaiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sCAAsC,EACtC,mCAAmC,EACnC,mBAAmB,EACnB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAyBnD,KAAK,YAAY,GACb,sCAAsC,GACtC,mCAAmC,CAAC;AAExC,UAAU,WAAW,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,UAAU,UAAU,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,CAAC,CAAC;IACxC,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACzC;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEzC,IAAI,IAAI;;qBAGO,CAAC,SAAS,YAAY,UACrB,CAAC,YACC;gBAAE,MAAM,CAAC,EAAE,WAAW,CAAA;aAAE,KACjC,UAAU,CACX,CAAC,SAAS,mCAAmC,GACzC,aAAa,CAAC,mBAAmB,CAAC,GAClC,cAAc,CACnB;;MA2BN;YAEa,OAAO;YAuHN,oBAAoB;CAyCpC"}
1
+ {"version":3,"file":"openaiClient.d.ts","sourceRoot":"","sources":["../../src/utils/openaiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sCAAsC,EACtC,mCAAmC,EACnC,mBAAmB,EACnB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAyBnD,KAAK,YAAY,GACb,sCAAsC,GACtC,mCAAmC,CAAC;AAExC,UAAU,WAAW,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,UAAU,UAAU,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,CAAC,CAAC;IACxC,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACzC;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEzC,IAAI,IAAI;;qBAGO,CAAC,SAAS,YAAY,UACrB,CAAC,YACC;gBAAE,MAAM,CAAC,EAAE,WAAW,CAAA;aAAE,KACjC,UAAU,CACX,CAAC,SAAS,mCAAmC,GACzC,aAAa,CAAC,mBAAmB,CAAC,GAClC,cAAc,CACnB;;MA2BN;YAEa,OAAO;YAyHN,oBAAoB;CAyCpC"}
@@ -67,6 +67,7 @@ export class OpenAIClient {
67
67
  }
68
68
  if (attempt < MAX_RETRIES) {
69
69
  logger.warn("OpenAI API network error, retrying...", {
70
+ model: params.model,
70
71
  attempt: attempt + 1,
71
72
  error: e,
72
73
  });
@@ -106,6 +107,7 @@ export class OpenAIClient {
106
107
  if (retryableStatus && attempt < MAX_RETRIES) {
107
108
  lastRetryAfter = response.headers.get("retry-after");
108
109
  logger.warn("OpenAI API error, retrying...", {
110
+ model: params.model,
109
111
  attempt: attempt + 1,
110
112
  status: response.status,
111
113
  retryAfter: lastRetryAfter,
@@ -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;AAQH,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,CA6JtE;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"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.18.5",
3
+ "version": "0.18.7",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
@@ -9,18 +9,6 @@ const __dirname = path.dirname(__filename);
9
9
  const MANIFEST_PATH = path.resolve(__dirname, "../bin/rg");
10
10
  const VENDOR_DIR = path.resolve(__dirname, "../vendor/ripgrep");
11
11
 
12
- function getCurrentPlatform() {
13
- const platform = process.platform;
14
- const arch = process.arch;
15
- if (platform === "darwin")
16
- return `macos-${arch === "arm64" ? "aarch64" : "x86_64"}`;
17
- if (platform === "linux")
18
- return `linux-${arch === "arm64" ? "aarch64" : "x86_64"}`;
19
- if (platform === "win32")
20
- return `windows-${arch === "arm64" ? "aarch64" : "x86_64"}`;
21
- return null;
22
- }
23
-
24
12
  async function main() {
25
13
  if (!fs.existsSync(MANIFEST_PATH)) {
26
14
  console.error(`Manifest not found: ${MANIFEST_PATH}`);
@@ -30,15 +18,7 @@ async function main() {
30
18
  const manifestContent = fs.readFileSync(MANIFEST_PATH, "utf-8");
31
19
  const jsonContent = manifestContent.replace(/^#!.*\n/, "");
32
20
  const manifest = JSON.parse(jsonContent);
33
- const allPlatforms = manifest.platforms;
34
-
35
- // In CI, only download for the current platform
36
- const isCI = process.env.CI === "true";
37
- const currentPlatform = isCI ? getCurrentPlatform() : null;
38
- const platforms =
39
- currentPlatform && currentPlatform in allPlatforms
40
- ? { [currentPlatform]: allPlatforms[currentPlatform] }
41
- : allPlatforms;
21
+ const platforms = manifest.platforms;
42
22
 
43
23
  if (!fs.existsSync(VENDOR_DIR)) {
44
24
  fs.mkdirSync(VENDOR_DIR, { recursive: true });