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.
Files changed (129) hide show
  1. package/builtin/skills/settings/ENV.md +0 -1
  2. package/builtin/skills/settings/HOOKS.md +10 -0
  3. package/builtin/skills/settings/MODELS.md +37 -0
  4. package/builtin/skills/settings/SKILLS.md +6 -4
  5. package/dist/agent.d.ts.map +1 -1
  6. package/dist/agent.js +12 -5
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/managers/aiManager.d.ts.map +1 -1
  11. package/dist/managers/aiManager.js +31 -8
  12. package/dist/managers/cronManager.d.ts.map +1 -1
  13. package/dist/managers/cronManager.js +2 -0
  14. package/dist/managers/liveConfigManager.d.ts.map +1 -1
  15. package/dist/managers/liveConfigManager.js +0 -9
  16. package/dist/managers/mcpManager.d.ts.map +1 -1
  17. package/dist/managers/mcpManager.js +13 -3
  18. package/dist/managers/messageManager.d.ts +4 -6
  19. package/dist/managers/messageManager.d.ts.map +1 -1
  20. package/dist/managers/messageManager.js +27 -81
  21. package/dist/managers/skillManager.d.ts +1 -0
  22. package/dist/managers/skillManager.d.ts.map +1 -1
  23. package/dist/managers/skillManager.js +11 -2
  24. package/dist/managers/toolManager.d.ts.map +1 -1
  25. package/dist/managers/toolManager.js +2 -0
  26. package/dist/prompts/index.d.ts +3 -2
  27. package/dist/prompts/index.d.ts.map +1 -1
  28. package/dist/prompts/index.js +5 -1
  29. package/dist/services/aiService.d.ts.map +1 -1
  30. package/dist/services/aiService.js +13 -9
  31. package/dist/services/configurationService.d.ts.map +1 -1
  32. package/dist/services/configurationService.js +24 -9
  33. package/dist/services/hook.d.ts.map +1 -1
  34. package/dist/services/hook.js +6 -0
  35. package/dist/services/initializationService.d.ts.map +1 -1
  36. package/dist/services/initializationService.js +1 -1
  37. package/dist/services/interactionService.js +1 -1
  38. package/dist/services/reversionService.d.ts +5 -1
  39. package/dist/services/reversionService.d.ts.map +1 -1
  40. package/dist/services/reversionService.js +50 -22
  41. package/dist/services/session.d.ts +4 -13
  42. package/dist/services/session.d.ts.map +1 -1
  43. package/dist/services/session.js +59 -215
  44. package/dist/telemetry/sessionTracing.d.ts +31 -20
  45. package/dist/telemetry/sessionTracing.d.ts.map +1 -1
  46. package/dist/telemetry/sessionTracing.js +192 -62
  47. package/dist/tools/bashTool.d.ts.map +1 -1
  48. package/dist/tools/bashTool.js +16 -1
  49. package/dist/tools/readTool.d.ts.map +1 -1
  50. package/dist/tools/readTool.js +6 -7
  51. package/dist/types/config.d.ts +7 -0
  52. package/dist/types/config.d.ts.map +1 -1
  53. package/dist/types/hooks.d.ts +1 -0
  54. package/dist/types/hooks.d.ts.map +1 -1
  55. package/dist/types/messaging.d.ts +0 -1
  56. package/dist/types/messaging.d.ts.map +1 -1
  57. package/dist/types/skills.d.ts +1 -0
  58. package/dist/types/skills.d.ts.map +1 -1
  59. package/dist/types/telemetry.d.ts +16 -1
  60. package/dist/types/telemetry.d.ts.map +1 -1
  61. package/dist/utils/atomicWrite.d.ts +15 -0
  62. package/dist/utils/atomicWrite.d.ts.map +1 -0
  63. package/dist/utils/atomicWrite.js +59 -0
  64. package/dist/utils/cacheControlUtils.d.ts +3 -15
  65. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  66. package/dist/utils/cacheControlUtils.js +4 -35
  67. package/dist/utils/containerSetup.d.ts.map +1 -1
  68. package/dist/utils/containerSetup.js +5 -1
  69. package/dist/utils/convertMessagesForAPI.d.ts +9 -1
  70. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  71. package/dist/utils/convertMessagesForAPI.js +74 -55
  72. package/dist/utils/fileUtils.d.ts +0 -6
  73. package/dist/utils/fileUtils.d.ts.map +1 -1
  74. package/dist/utils/fileUtils.js +0 -43
  75. package/dist/utils/gitUtils.d.ts +11 -0
  76. package/dist/utils/gitUtils.d.ts.map +1 -1
  77. package/dist/utils/gitUtils.js +51 -0
  78. package/dist/utils/groupMessagesByApiRound.d.ts.map +1 -1
  79. package/dist/utils/groupMessagesByApiRound.js +7 -6
  80. package/dist/utils/mcpUtils.d.ts.map +1 -1
  81. package/dist/utils/mcpUtils.js +2 -1
  82. package/dist/utils/modelCapabilities.d.ts +22 -0
  83. package/dist/utils/modelCapabilities.d.ts.map +1 -0
  84. package/dist/utils/modelCapabilities.js +24 -0
  85. package/dist/utils/skillParser.d.ts.map +1 -1
  86. package/dist/utils/skillParser.js +3 -1
  87. package/dist/utils/tokenEstimate.d.ts +24 -0
  88. package/dist/utils/tokenEstimate.d.ts.map +1 -0
  89. package/dist/utils/tokenEstimate.js +30 -0
  90. package/dist/utils/worktreeUtils.d.ts.map +1 -1
  91. package/dist/utils/worktreeUtils.js +3 -1
  92. package/package.json +1 -1
  93. package/src/agent.ts +11 -5
  94. package/src/index.ts +1 -0
  95. package/src/managers/aiManager.ts +36 -7
  96. package/src/managers/cronManager.ts +2 -0
  97. package/src/managers/liveConfigManager.ts +0 -12
  98. package/src/managers/mcpManager.ts +16 -3
  99. package/src/managers/messageManager.ts +32 -109
  100. package/src/managers/skillManager.ts +16 -2
  101. package/src/managers/toolManager.ts +2 -0
  102. package/src/prompts/index.ts +6 -1
  103. package/src/services/aiService.ts +12 -5
  104. package/src/services/configurationService.ts +26 -24
  105. package/src/services/hook.ts +7 -0
  106. package/src/services/initializationService.ts +1 -3
  107. package/src/services/interactionService.ts +1 -1
  108. package/src/services/reversionService.ts +50 -27
  109. package/src/services/session.ts +63 -262
  110. package/src/telemetry/sessionTracing.ts +225 -73
  111. package/src/tools/bashTool.ts +23 -1
  112. package/src/tools/readTool.ts +6 -12
  113. package/src/types/config.ts +8 -0
  114. package/src/types/hooks.ts +1 -0
  115. package/src/types/messaging.ts +0 -1
  116. package/src/types/skills.ts +1 -0
  117. package/src/types/telemetry.ts +19 -1
  118. package/src/utils/atomicWrite.ts +61 -0
  119. package/src/utils/cacheControlUtils.ts +5 -38
  120. package/src/utils/containerSetup.ts +5 -1
  121. package/src/utils/convertMessagesForAPI.ts +87 -57
  122. package/src/utils/fileUtils.ts +0 -46
  123. package/src/utils/gitUtils.ts +54 -0
  124. package/src/utils/groupMessagesByApiRound.ts +7 -6
  125. package/src/utils/mcpUtils.ts +2 -1
  126. package/src/utils/modelCapabilities.ts +30 -0
  127. package/src/utils/skillParser.ts +3 -1
  128. package/src/utils/tokenEstimate.ts +34 -0
  129. package/src/utils/worktreeUtils.ts +8 -1
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Model Capability Detection
3
+ *
4
+ * Pure declarative capability accessors. Capabilities are configured per-model
5
+ * via the `capabilities` field in `ModelConfig` (settings.json), not via regex
6
+ * or environment variables.
7
+ */
8
+ import type { ModelCapabilities } from "../types/config.js";
9
+ /**
10
+ * Determines if a model supports prompt caching.
11
+ * @param capabilities - Declarative model capabilities
12
+ * @returns True if promptCaching is explicitly enabled (default: false)
13
+ */
14
+ export declare function supportsPromptCaching(capabilities?: ModelCapabilities): boolean;
15
+ /**
16
+ * Determines if a model supports image/vision recognition.
17
+ * Opt-out semantics: by default all models are assumed to support vision.
18
+ * @param capabilities - Declarative model capabilities
19
+ * @returns True unless vision is explicitly disabled (default: true)
20
+ */
21
+ export declare function supportsVision(capabilities?: ModelCapabilities): boolean;
22
+ //# sourceMappingURL=modelCapabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelCapabilities.d.ts","sourceRoot":"","sources":["../../src/utils/modelCapabilities.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,CAAC,EAAE,iBAAiB,GAC/B,OAAO,CAET;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,YAAY,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAExE"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Model Capability Detection
3
+ *
4
+ * Pure declarative capability accessors. Capabilities are configured per-model
5
+ * via the `capabilities` field in `ModelConfig` (settings.json), not via regex
6
+ * or environment variables.
7
+ */
8
+ /**
9
+ * Determines if a model supports prompt caching.
10
+ * @param capabilities - Declarative model capabilities
11
+ * @returns True if promptCaching is explicitly enabled (default: false)
12
+ */
13
+ export function supportsPromptCaching(capabilities) {
14
+ return capabilities?.promptCaching ?? false;
15
+ }
16
+ /**
17
+ * Determines if a model supports image/vision recognition.
18
+ * Opt-out semantics: by default all models are assumed to support vision.
19
+ * @param capabilities - Declarative model capabilities
20
+ * @returns True unless vision is explicitly disabled (default: true)
21
+ */
22
+ export function supportsVision(capabilities) {
23
+ return capabilities?.vision ?? true;
24
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"skillParser.d.ts","sourceRoot":"","sources":["../../src/utils/skillParser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAEjB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iBAAsB,GAC9B,eAAe,CAkGjB;AAyDD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAwCvE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAa5E"}
1
+ {"version":3,"file":"skillParser.d.ts","sourceRoot":"","sources":["../../src/utils/skillParser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAEjB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iBAAsB,GAC9B,eAAe,CAoGjB;AAyDD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAwCvE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAa5E"}
@@ -38,7 +38,9 @@ export function parseSkillFile(filePath, options = {}) {
38
38
  const skillType = skillPath.includes("/.wave/skills") ||
39
39
  skillPath.includes("\\.wave\\skills") ||
40
40
  skillPath.includes("/.claude/skills") ||
41
- skillPath.includes("\\.claude\\skills")
41
+ skillPath.includes("\\.claude\\skills") ||
42
+ skillPath.includes("/.agents/skills") ||
43
+ skillPath.includes("\\.agents\\skills")
42
44
  ? "project"
43
45
  : "personal";
44
46
  // Extract allowed tools
@@ -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.6",
3
+ "version": "0.19.0",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
package/src/agent.ts CHANGED
@@ -41,6 +41,7 @@ import { SkillManager } from "./managers/skillManager.js";
41
41
  import { TaskManager } from "./services/taskManager.js";
42
42
  import { btw } from "./services/aiService.js";
43
43
  import { convertMessagesForAPI } from "./utils/convertMessagesForAPI.js";
44
+ import { supportsVision } from "./utils/modelCapabilities.js";
44
45
  import { parseTaskNotificationXml } from "./utils/notificationXml.js";
45
46
  import { InitializationService } from "./services/initializationService.js";
46
47
  import { InteractionService } from "./services/interactionService.js";
@@ -598,17 +599,20 @@ export class Agent {
598
599
  options,
599
600
  );
600
601
 
601
- // Initialize OpenTelemetry (non-blocking, graceful degradation)
602
+ // Initialize OpenTelemetry (awaited to ensure ALS context is ready before
603
+ // startInteractionSpan is called, preventing trace context fragmentation)
602
604
  const telemetryConfig = this.configurationService.resolveTelemetryConfig();
603
- initializeTelemetry(telemetryConfig).catch((error) => {
605
+ try {
606
+ await initializeTelemetry(telemetryConfig);
607
+ } catch (error) {
604
608
  this.logger?.warn("Telemetry initialization failed:", error);
605
- });
609
+ }
606
610
 
607
611
  // Log session_start event
608
612
  try {
609
613
  const modelConfig = this.getModelConfig();
610
614
  if (modelConfig.model) {
611
- logOTelEvent("session_start", {
615
+ await logOTelEvent("session_start", {
612
616
  sessionId: this.messageManager.getSessionId(),
613
617
  model: modelConfig.model,
614
618
  workdir: this.workdir,
@@ -992,7 +996,9 @@ export class Agent {
992
996
  * @returns Promise that resolves to the AI's answer
993
997
  */
994
998
  public async askBtw(question: string): Promise<string> {
995
- const messages = convertMessagesForAPI(this.messageManager.getMessages());
999
+ const messages = convertMessagesForAPI(this.messageManager.getMessages(), {
1000
+ supportsVision: supportsVision(this.getModelConfig().capabilities),
1001
+ });
996
1002
  const result = await btw({
997
1003
  gatewayConfig: this.getGatewayConfig(),
998
1004
  modelConfig: this.getModelConfig(),
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ export * from "./managers/messageQueue.js";
14
14
  // Export all utilities
15
15
  export * from "./utils/bashParser.js";
16
16
  export * from "./utils/convertMessagesForAPI.js";
17
+ export * from "./utils/modelCapabilities.js";
17
18
  export * from "./utils/fileSearch.js";
18
19
  export * from "./utils/globalLogger.js";
19
20
  export * from "./utils/mcpUtils.js";
@@ -1,8 +1,10 @@
1
1
  import { type CallAgentOptions } from "../services/aiService.js";
2
2
  import * as aiService from "../services/aiService.js";
3
3
  import { convertMessagesForAPI } from "../utils/convertMessagesForAPI.js";
4
+ import { supportsVision } from "../utils/modelCapabilities.js";
4
5
  import { parseTaskNotificationXml } from "../utils/notificationXml.js";
5
6
  import { calculateComprehensiveTotalTokens } from "../utils/tokenCalculation.js";
7
+ import { estimateTokens } from "../utils/tokenEstimate.js";
6
8
  import {
7
9
  getTaskReminderTurnCounts,
8
10
  maybeInjectTaskReminder,
@@ -44,6 +46,7 @@ import {
44
46
  endInteractionSpan,
45
47
  startLLMRequestSpan,
46
48
  endLLMRequestSpan,
49
+ resetTracingState,
47
50
  } from "../telemetry/sessionTracing.js";
48
51
  import { logOTelEvent } from "../telemetry/events.js";
49
52
 
@@ -491,7 +494,7 @@ export class AIManager {
491
494
  );
492
495
 
493
496
  // 7. Execute message reconstruction
494
- this.messageManager.compactMessagesAndUpdateSession(
497
+ await this.messageManager.compactMessagesAndUpdateSession(
495
498
  enhancedSummary,
496
499
  compactUsage,
497
500
  );
@@ -522,6 +525,9 @@ export class AIManager {
522
525
 
523
526
  this.consecutiveCompactionFailures = 0;
524
527
 
528
+ // Reset incremental tracing state after compaction
529
+ resetTracingState();
530
+
525
531
  // 9. Log OTEL event
526
532
  logOTelEvent("compaction", {
527
533
  beforeTokens: String(messagesToCompact.length),
@@ -606,7 +612,7 @@ export class AIManager {
606
612
  );
607
613
  let usedTokens = 0;
608
614
  for (const file of recentFiles) {
609
- const fileTokens = Math.ceil(file.content.length / 4);
615
+ const fileTokens = estimateTokens(file.content);
610
616
  if (usedTokens + fileTokens > POST_COMPACT_MAX_TOKENS_PER_FILE) continue;
611
617
  if (fileTokens > 0) usedTokens += fileTokens;
612
618
  contextParts.push(`\n\n## ${file.path}\n\`\`\`\n${file.content}\n\`\`\``);
@@ -639,7 +645,7 @@ export class AIManager {
639
645
  skillContent.slice(0, maxSkillChars) + "\n\n...[truncated]...";
640
646
  }
641
647
 
642
- const skillTokens = Math.ceil(skillContent.length / 4);
648
+ const skillTokens = estimateTokens(skillContent);
643
649
  if (skillsUsedTokens + skillTokens > POST_COMPACT_SKILLS_TOKEN_BUDGET)
644
650
  break;
645
651
  skillsUsedTokens += skillTokens;
@@ -813,6 +819,7 @@ export class AIManager {
813
819
  let turnDepth = turnOffset;
814
820
 
815
821
  inner: while (true) {
822
+ let llmSpan: import("@opentelemetry/api").Span | undefined;
816
823
  try {
817
824
  // Save session in each iteration to ensure message persistence
818
825
  await this.messageManager.saveSession();
@@ -836,7 +843,10 @@ export class AIManager {
836
843
 
837
844
  // Get recent message history
838
845
  const rawMessages = this.messageManager.getMessages();
839
- const recentMessages = convertMessagesForAPI(rawMessages);
846
+ const currentModelConfig = this.getModelConfig();
847
+ const recentMessages = convertMessagesForAPI(rawMessages, {
848
+ supportsVision: supportsVision(currentModelConfig.capabilities),
849
+ });
840
850
 
841
851
  // Track if assistant message has been created
842
852
  let assistantMessageCreated = false;
@@ -949,15 +959,33 @@ export class AIManager {
949
959
  };
950
960
  }
951
961
 
952
- startLLMRequestSpan(model || this.getModelConfig().model || "");
962
+ llmSpan = startLLMRequestSpan(
963
+ model || this.getModelConfig().model || "",
964
+ {
965
+ context: "interaction",
966
+ systemPrompt:
967
+ typeof callAgentOptions.systemPrompt === "string"
968
+ ? callAgentOptions.systemPrompt
969
+ : undefined,
970
+ inputMessages: callAgentOptions.messages,
971
+ toolsSchema: callAgentOptions.tools
972
+ ? JSON.stringify(callAgentOptions.tools)
973
+ : undefined,
974
+ },
975
+ );
953
976
 
954
977
  const result = await aiService.callAgent(callAgentOptions);
955
978
 
956
979
  // End LLM span with usage data
957
- endLLMRequestSpan({
980
+ endLLMRequestSpan(llmSpan, {
958
981
  model: model || this.getModelConfig().model || "",
959
982
  success: true,
960
983
  hasToolCall: !!(result.tool_calls && result.tool_calls.length > 0),
984
+ inputTokens: result.usage?.prompt_tokens,
985
+ outputTokens: result.usage?.completion_tokens,
986
+ cacheReadTokens: result.usage?.cache_read_input_tokens,
987
+ cacheCreationTokens: result.usage?.cache_creation_input_tokens,
988
+ modelOutput: result.content,
961
989
  });
962
990
 
963
991
  const createdByStreaming = assistantMessageCreated;
@@ -1238,7 +1266,7 @@ export class AIManager {
1238
1266
  break inner;
1239
1267
  } catch (error) {
1240
1268
  // End LLM span with error
1241
- endLLMRequestSpan({
1269
+ endLLMRequestSpan(llmSpan, {
1242
1270
  model: model || this.getModelConfig().model || "",
1243
1271
  success: false,
1244
1272
  error: error instanceof Error ? error.message : String(error),
@@ -1653,6 +1681,7 @@ export class AIManager {
1653
1681
  shortResult: toolResult.shortResult,
1654
1682
  isManuallyBackgrounded: toolResult.isManuallyBackgrounded,
1655
1683
  startLineNumber: toolResult.startLineNumber,
1684
+ images: toolResult.images,
1656
1685
  timestamp: Date.now(),
1657
1686
  });
1658
1687
 
@@ -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
 
@@ -725,11 +725,17 @@ export class McpManager {
725
725
  }
726
726
  }
727
727
 
728
- // Get all tools from connected servers
728
+ // Get all tools from connected servers.
729
+ // "reconnecting" servers retain their last-known tool snapshot so that
730
+ // transient SSE disconnects don't churn the tool list (which would bust
731
+ // the prompt cache). Tools are only dropped once the server truly fails
732
+ // (status "error"/"disconnected").
729
733
  getAllConnectedTools(): McpTool[] {
730
734
  const allTools: McpTool[] = [];
731
735
  for (const server of this.servers.values()) {
732
- if (server.status === "connected" && server.tools) {
736
+ const usable =
737
+ server.status === "connected" || server.status === "reconnecting";
738
+ if (usable && server.tools) {
733
739
  allTools.push(...server.tools);
734
740
  }
735
741
  }
@@ -848,9 +854,16 @@ export class McpManager {
848
854
  textContent.push(String(result.content));
849
855
  }
850
856
 
857
+ const textContentStr =
858
+ textContent.length > 0
859
+ ? textContent.join("\n")
860
+ : images.length > 0
861
+ ? `Tool returned ${images.length} image(s).`
862
+ : "No content";
863
+
851
864
  return {
852
865
  success: true,
853
- content: textContent.length > 0 ? textContent.join("\n") : "No content",
866
+ content: textContentStr,
854
867
  images: images.length > 0 ? images : undefined,
855
868
  serverName,
856
869
  };