wave-agent-sdk 0.15.0 → 0.15.2

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 (144) hide show
  1. package/builtin/skills/loop/SKILL.md +29 -3
  2. package/dist/agent.d.ts +11 -2
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +44 -11
  5. package/dist/constants/tools.d.ts +3 -0
  6. package/dist/constants/tools.d.ts.map +1 -1
  7. package/dist/constants/tools.js +3 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/managers/aiManager.d.ts +13 -1
  12. package/dist/managers/aiManager.d.ts.map +1 -1
  13. package/dist/managers/aiManager.js +69 -17
  14. package/dist/managers/hookManager.d.ts.map +1 -1
  15. package/dist/managers/hookManager.js +9 -0
  16. package/dist/managers/mcpManager.d.ts +4 -1
  17. package/dist/managers/mcpManager.d.ts.map +1 -1
  18. package/dist/managers/mcpManager.js +25 -5
  19. package/dist/managers/messageManager.d.ts.map +1 -1
  20. package/dist/managers/messageManager.js +7 -6
  21. package/dist/managers/permissionManager.d.ts +0 -2
  22. package/dist/managers/permissionManager.d.ts.map +1 -1
  23. package/dist/managers/permissionManager.js +0 -30
  24. package/dist/managers/slashCommandManager.d.ts +1 -0
  25. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  26. package/dist/managers/slashCommandManager.js +20 -4
  27. package/dist/managers/subagentManager.d.ts +6 -1
  28. package/dist/managers/subagentManager.d.ts.map +1 -1
  29. package/dist/managers/subagentManager.js +17 -18
  30. package/dist/managers/toolManager.d.ts +6 -0
  31. package/dist/managers/toolManager.d.ts.map +1 -1
  32. package/dist/managers/toolManager.js +41 -1
  33. package/dist/prompts/index.d.ts +1 -2
  34. package/dist/prompts/index.d.ts.map +1 -1
  35. package/dist/prompts/index.js +14 -6
  36. package/dist/services/initializationService.d.ts +0 -2
  37. package/dist/services/initializationService.d.ts.map +1 -1
  38. package/dist/services/initializationService.js +3 -35
  39. package/dist/services/jsonlHandler.d.ts +4 -4
  40. package/dist/services/jsonlHandler.d.ts.map +1 -1
  41. package/dist/services/jsonlHandler.js +4 -13
  42. package/dist/services/memory.d.ts +6 -0
  43. package/dist/services/memory.d.ts.map +1 -1
  44. package/dist/services/memory.js +27 -14
  45. package/dist/services/session.d.ts.map +1 -1
  46. package/dist/services/session.js +3 -12
  47. package/dist/tools/agentTool.d.ts.map +1 -1
  48. package/dist/tools/agentTool.js +16 -4
  49. package/dist/tools/bashTool.d.ts.map +1 -1
  50. package/dist/tools/bashTool.js +2 -5
  51. package/dist/tools/cronCreateTool.d.ts.map +1 -1
  52. package/dist/tools/cronCreateTool.js +71 -6
  53. package/dist/tools/cronDeleteTool.d.ts.map +1 -1
  54. package/dist/tools/cronDeleteTool.js +5 -1
  55. package/dist/tools/cronListTool.d.ts.map +1 -1
  56. package/dist/tools/cronListTool.js +5 -1
  57. package/dist/tools/enterWorktreeTool.d.ts +8 -0
  58. package/dist/tools/enterWorktreeTool.d.ts.map +1 -0
  59. package/dist/tools/enterWorktreeTool.js +144 -0
  60. package/dist/tools/exitWorktreeTool.d.ts +8 -0
  61. package/dist/tools/exitWorktreeTool.d.ts.map +1 -0
  62. package/dist/tools/exitWorktreeTool.js +184 -0
  63. package/dist/tools/skillTool.d.ts.map +1 -1
  64. package/dist/tools/skillTool.js +16 -4
  65. package/dist/tools/taskManagementTools.d.ts.map +1 -1
  66. package/dist/tools/taskManagementTools.js +4 -0
  67. package/dist/tools/toolSearchTool.d.ts +15 -0
  68. package/dist/tools/toolSearchTool.d.ts.map +1 -0
  69. package/dist/tools/toolSearchTool.js +185 -0
  70. package/dist/tools/types.d.ts +19 -0
  71. package/dist/tools/types.d.ts.map +1 -1
  72. package/dist/tools/webFetchTool.d.ts.map +1 -1
  73. package/dist/tools/webFetchTool.js +1 -0
  74. package/dist/types/agent.d.ts +6 -1
  75. package/dist/types/agent.d.ts.map +1 -1
  76. package/dist/types/hooks.d.ts +3 -1
  77. package/dist/types/hooks.d.ts.map +1 -1
  78. package/dist/types/hooks.js +1 -0
  79. package/dist/types/messaging.d.ts +1 -0
  80. package/dist/types/messaging.d.ts.map +1 -1
  81. package/dist/types/session.d.ts +0 -4
  82. package/dist/types/session.d.ts.map +1 -1
  83. package/dist/utils/containerSetup.d.ts.map +1 -1
  84. package/dist/utils/containerSetup.js +4 -6
  85. package/dist/utils/cronToHuman.d.ts +6 -0
  86. package/dist/utils/cronToHuman.d.ts.map +1 -0
  87. package/dist/utils/cronToHuman.js +79 -0
  88. package/dist/utils/isDeferredTool.d.ts +19 -0
  89. package/dist/utils/isDeferredTool.d.ts.map +1 -0
  90. package/dist/utils/isDeferredTool.js +31 -0
  91. package/dist/utils/mcpUtils.d.ts.map +1 -1
  92. package/dist/utils/mcpUtils.js +1 -0
  93. package/dist/utils/messageOperations.d.ts.map +1 -1
  94. package/dist/utils/messageOperations.js +5 -0
  95. package/dist/utils/parseCronExpression.d.ts +6 -0
  96. package/dist/utils/parseCronExpression.d.ts.map +1 -0
  97. package/dist/utils/parseCronExpression.js +74 -0
  98. package/dist/utils/worktreeSession.d.ts +26 -0
  99. package/dist/utils/worktreeSession.d.ts.map +1 -0
  100. package/dist/utils/worktreeSession.js +14 -0
  101. package/dist/utils/worktreeUtils.d.ts +42 -0
  102. package/dist/utils/worktreeUtils.d.ts.map +1 -0
  103. package/dist/utils/worktreeUtils.js +236 -0
  104. package/package.json +1 -1
  105. package/src/agent.ts +61 -12
  106. package/src/constants/tools.ts +3 -0
  107. package/src/index.ts +1 -0
  108. package/src/managers/aiManager.ts +73 -18
  109. package/src/managers/hookManager.ts +10 -0
  110. package/src/managers/mcpManager.ts +32 -6
  111. package/src/managers/messageManager.ts +7 -8
  112. package/src/managers/permissionManager.ts +0 -42
  113. package/src/managers/slashCommandManager.ts +30 -5
  114. package/src/managers/subagentManager.ts +28 -23
  115. package/src/managers/toolManager.ts +47 -1
  116. package/src/prompts/index.ts +17 -6
  117. package/src/services/initializationService.ts +2 -41
  118. package/src/services/jsonlHandler.ts +12 -24
  119. package/src/services/memory.ts +30 -17
  120. package/src/services/session.ts +3 -14
  121. package/src/tools/agentTool.ts +24 -5
  122. package/src/tools/bashTool.ts +2 -5
  123. package/src/tools/cronCreateTool.ts +81 -8
  124. package/src/tools/cronDeleteTool.ts +7 -2
  125. package/src/tools/cronListTool.ts +7 -2
  126. package/src/tools/enterWorktreeTool.ts +183 -0
  127. package/src/tools/exitWorktreeTool.ts +242 -0
  128. package/src/tools/skillTool.ts +24 -4
  129. package/src/tools/taskManagementTools.ts +4 -0
  130. package/src/tools/toolSearchTool.ts +228 -0
  131. package/src/tools/types.ts +19 -0
  132. package/src/tools/webFetchTool.ts +1 -0
  133. package/src/types/agent.ts +6 -0
  134. package/src/types/hooks.ts +4 -0
  135. package/src/types/messaging.ts +1 -0
  136. package/src/types/session.ts +0 -8
  137. package/src/utils/containerSetup.ts +7 -8
  138. package/src/utils/cronToHuman.ts +99 -0
  139. package/src/utils/isDeferredTool.ts +36 -0
  140. package/src/utils/mcpUtils.ts +1 -0
  141. package/src/utils/messageOperations.ts +5 -0
  142. package/src/utils/parseCronExpression.ts +78 -0
  143. package/src/utils/worktreeSession.ts +36 -0
  144. package/src/utils/worktreeUtils.ts +288 -0
@@ -4,7 +4,7 @@ import { LspManager } from "../managers/lspManager.js";
4
4
  import { USER_MEMORY_FILE } from "../utils/constants.js";
5
5
  export class InitializationService {
6
6
  static async initialize(context, options) {
7
- const { skillManager, subagentManager, container, toolManager, pluginManager, options: agentOptions, slashCommandManager, logger, mcpManager, workdir, lspManager, configurationService, hookManager, messageManager, memoryRuleManager, liveConfigManager, taskManager, setProjectMemory, setUserMemory, resolveAndValidateConfig, } = context;
7
+ const { skillManager, subagentManager, container, toolManager, pluginManager, options: agentOptions, slashCommandManager, logger, mcpManager, workdir, lspManager, configurationService, hookManager, messageManager, memoryRuleManager, liveConfigManager, taskManager, resolveAndValidateConfig, } = context;
8
8
  const startTime = performance.now();
9
9
  // Initialize managers first
10
10
  try {
@@ -162,40 +162,8 @@ export class InitializationService {
162
162
  logger?.error("Failed to initialize live configuration reload:", error);
163
163
  // Don't throw error to prevent app startup failure - continue without live reload
164
164
  }
165
- // Load memory files during initialization
166
- try {
167
- const phaseStart = performance.now();
168
- const memoryService = container.get("MemoryService");
169
- if (!memoryService) {
170
- throw new Error("MemoryService not found in container");
171
- }
172
- // Load project memory from AGENTS.md
173
- try {
174
- const projectMemoryContent = await memoryService.readMemoryFile(workdir);
175
- setProjectMemory(projectMemoryContent);
176
- }
177
- catch (error) {
178
- logger?.warn("Failed to load project memory file:", error);
179
- setProjectMemory("");
180
- }
181
- // Load user memory
182
- try {
183
- const userMemoryContent = await memoryService.getUserMemoryContent();
184
- setUserMemory(userMemoryContent);
185
- }
186
- catch (error) {
187
- logger?.warn("Failed to load user memory file:", error);
188
- setUserMemory("");
189
- }
190
- logger?.debug(`Initialization Phase [Memory Files Loading] took ${(performance.now() - phaseStart).toFixed(2)}ms`);
191
- }
192
- catch (error) {
193
- // Ensure memory is always initialized even if loading fails
194
- setProjectMemory("");
195
- setUserMemory("");
196
- logger?.error("Failed to load memory files:", error);
197
- // Don't throw error to prevent app startup failure
198
- }
165
+ // Memory is lazy-cached on first getCombinedMemoryContent call
166
+ // No explicit loading needed during initialization
199
167
  // Handle session restoration or set provided messages
200
168
  const sessionPhaseStart = performance.now();
201
169
  if (options?.messages) {
@@ -3,7 +3,7 @@
3
3
  * Handles reading and writing JSONL (JSON Lines) session files for improved performance
4
4
  */
5
5
  import type { Message } from "../types/index.js";
6
- import type { SessionMessage, SessionFilename } from "../types/session.js";
6
+ import type { SessionFilename } from "../types/session.js";
7
7
  /**
8
8
  * JSONL write options
9
9
  */
@@ -31,15 +31,15 @@ export declare class JsonlHandler {
31
31
  /**
32
32
  * Append messages to JSONL file
33
33
  */
34
- append(filePath: string, messages: SessionMessage[], options?: JsonlWriteOptions): Promise<void>;
34
+ append(filePath: string, messages: Message[], options?: JsonlWriteOptions): Promise<void>;
35
35
  /**
36
36
  * Read all messages from JSONL file (simplified - no metadata handling)
37
37
  */
38
- read(filePath: string): Promise<SessionMessage[]>;
38
+ read(filePath: string): Promise<Message[]>;
39
39
  /**
40
40
  * Get the last message from JSONL file using efficient file reading (simplified)
41
41
  */
42
- getLastMessage(filePath: string): Promise<SessionMessage | null>;
42
+ getLastMessage(filePath: string): Promise<Message | null>;
43
43
  /**
44
44
  * Validate messages before writing
45
45
  */
@@ -1 +1 @@
1
- {"version":3,"file":"jsonlHandler.d.ts","sourceRoot":"","sources":["../../src/services/jsonlHandler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAEhC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA8B;;IAQlE;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpD;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAc1E;;OAEG;IACG,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC;IAyChB;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAoCvD;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAgCtE;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA4BxB;;OAEG;YACW,eAAe;IAW7B;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IA6BvD;;;;OAIG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAUjD;;;;;OAKG;IACH,uBAAuB,CACrB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,UAAU,GAC/B,MAAM;CAcV"}
1
+ {"version":3,"file":"jsonlHandler.d.ts","sourceRoot":"","sources":["../../src/services/jsonlHandler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAEhC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA8B;;IAQlE;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpD;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1E;;OAEG;IACG,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC;IAmChB;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAoChD;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAgC/D;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA4BxB;;OAEG;YACW,eAAe;IAW7B;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IA6BvD;;;;OAIG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAUjD;;;;;OAKG;IACH,uBAAuB,CACrB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,UAAU,GAC/B,MAAM;CAcV"}
@@ -36,12 +36,7 @@ export class JsonlHandler {
36
36
  if (messages.length === 0) {
37
37
  return;
38
38
  }
39
- // Convert to SessionMessage format with timestamps
40
- const sessionMessages = messages.map((message) => ({
41
- ...message,
42
- timestamp: new Date().toISOString(),
43
- }));
44
- return this.append(filePath, sessionMessages);
39
+ return this.append(filePath, messages);
45
40
  }
46
41
  /**
47
42
  * Append messages to JSONL file
@@ -55,14 +50,10 @@ export class JsonlHandler {
55
50
  this.validateMessages(messages);
56
51
  // Ensure directory exists
57
52
  await this.ensureDirectory(dirname(filePath));
58
- // Convert messages to JSONL lines (always compact JSON)
53
+ // Convert messages to JSONL lines (compact JSON, timestamp first)
59
54
  const lines = messages.map((message) => {
60
- const { timestamp: existingTimestamp, ...messageWithoutTimestamp } = message;
61
- const messageWithTimestamp = {
62
- timestamp: existingTimestamp || new Date().toISOString(),
63
- ...messageWithoutTimestamp,
64
- };
65
- return JSON.stringify(messageWithTimestamp);
55
+ const { timestamp, ...rest } = message;
56
+ return JSON.stringify({ timestamp, ...rest });
66
57
  });
67
58
  const content = lines.join("\n") + "\n";
68
59
  if (opts.atomic) {
@@ -1,7 +1,13 @@
1
1
  import { Container } from "../utils/container.js";
2
2
  export declare class MemoryService {
3
3
  private container;
4
+ private _cachedProjectMemory;
5
+ private _cachedUserMemory;
6
+ private _cachedCombinedMemory;
4
7
  constructor(container: Container);
8
+ get cachedProjectMemory(): string;
9
+ get cachedUserMemory(): string;
10
+ clearCache(): void;
5
11
  /**
6
12
  * Get the project-specific auto-memory directory.
7
13
  * Uses the git common directory to ensure worktrees share the same memory.
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/services/memory.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIlD,qBAAa,aAAa;IACZ,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAExC;;;OAGG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAU/C;;OAEG;IACG,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B/D;;OAEG;IACG,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBtD,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BrC,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAevC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBhD,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAqBjE"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/services/memory.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIlD,qBAAa,aAAa;IAKZ,OAAO,CAAC,SAAS;IAJ7B,OAAO,CAAC,oBAAoB,CAAc;IAC1C,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,qBAAqB,CAAuB;gBAEhC,SAAS,EAAE,SAAS;IAExC,IAAW,mBAAmB,IAAI,MAAM,CAEvC;IAED,IAAW,gBAAgB,IAAI,MAAM,CAEpC;IAEM,UAAU,IAAI,IAAI;IAMzB;;;OAGG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAU/C;;OAEG;IACG,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B/D;;OAEG;IACG,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBtD,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BrC,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAevC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBhD,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAgBjE"}
@@ -8,6 +8,20 @@ import { pathEncoder } from "../utils/pathEncoder.js";
8
8
  export class MemoryService {
9
9
  constructor(container) {
10
10
  this.container = container;
11
+ this._cachedProjectMemory = "";
12
+ this._cachedUserMemory = "";
13
+ this._cachedCombinedMemory = null;
14
+ }
15
+ get cachedProjectMemory() {
16
+ return this._cachedProjectMemory;
17
+ }
18
+ get cachedUserMemory() {
19
+ return this._cachedUserMemory;
20
+ }
21
+ clearCache() {
22
+ this._cachedProjectMemory = "";
23
+ this._cachedUserMemory = "";
24
+ this._cachedCombinedMemory = null;
11
25
  }
12
26
  /**
13
27
  * Get the project-specific auto-memory directory.
@@ -133,21 +147,20 @@ export class MemoryService {
133
147
  }
134
148
  }
135
149
  async getCombinedMemoryContent(workdir) {
136
- // Read memory file content
137
- const memoryContent = await this.readMemoryFile(workdir);
138
- // Read user-level memory content
139
- const userMemoryContent = await this.getUserMemoryContent();
140
- // Merge project memory and user memory
141
- let combinedMemory = "";
142
- if (memoryContent.trim()) {
143
- combinedMemory += memoryContent;
150
+ if (this._cachedCombinedMemory !== null) {
151
+ return this._cachedCombinedMemory;
144
152
  }
145
- if (userMemoryContent.trim()) {
146
- if (combinedMemory) {
147
- combinedMemory += "\n\n";
148
- }
149
- combinedMemory += userMemoryContent;
153
+ this._cachedProjectMemory = await this.readMemoryFile(workdir);
154
+ this._cachedUserMemory = await this.getUserMemoryContent();
155
+ let combined = "";
156
+ if (this._cachedProjectMemory.trim())
157
+ combined += this._cachedProjectMemory;
158
+ if (this._cachedUserMemory.trim()) {
159
+ if (combined)
160
+ combined += "\n\n";
161
+ combined += this._cachedUserMemory;
150
162
  }
151
- return combinedMemory;
163
+ this._cachedCombinedMemory = combined;
164
+ return combined;
152
165
  }
153
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/services/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/services/session.ts"],"names":[],"mappings":"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"}
@@ -147,17 +147,13 @@ export async function appendMessages(sessionId, newMessages, workdir, sessionTyp
147
147
  catch {
148
148
  throw new Error(`Session file not found: ${sessionId}. Use createSession() to create a new session first.`);
149
149
  }
150
- const messagesWithTimestamp = newMessages.map((msg) => ({
151
- timestamp: new Date().toISOString(),
152
- ...msg,
153
- }));
154
- await jsonlHandler.append(filePath, messagesWithTimestamp, {
150
+ await jsonlHandler.append(filePath, newMessages, {
155
151
  atomic: false,
156
152
  });
157
153
  // Update index
158
154
  const encoder = new PathEncoder();
159
155
  const projectDir = await encoder.getProjectDirectory(workdir, SESSION_DIR);
160
- const lastMessage = messagesWithTimestamp[messagesWithTimestamp.length - 1];
156
+ const lastMessage = newMessages[newMessages.length - 1];
161
157
  // Get first message content if it's a new session or we don't have it
162
158
  let firstMessage;
163
159
  try {
@@ -232,12 +228,7 @@ export async function loadSessionFromJsonl(sessionId, workdir, sessionType = "ma
232
228
  id: sessionId,
233
229
  rootSessionId: rootSessionId || sessionId,
234
230
  parentSessionId,
235
- messages: messages.map((msg) => {
236
- // Remove timestamp property for backward compatibility
237
- const { timestamp: _ignored, ...messageWithoutTimestamp } = msg;
238
- void _ignored; // Use the variable to avoid eslint error
239
- return messageWithoutTimestamp;
240
- }),
231
+ messages,
241
232
  metadata: {
242
233
  workdir,
243
234
  lastActiveAt: lastMessage
@@ -1 +1 @@
1
- {"version":3,"file":"agentTool.d.ts","sourceRoot":"","sources":["../../src/tools/agentTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAStE;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,UA4QvB,CAAC"}
1
+ {"version":3,"file":"agentTool.d.ts","sourceRoot":"","sources":["../../src/tools/agentTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAStE;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,UA+RvB,CAAC"}
@@ -132,17 +132,29 @@ When using the Agent tool, you must specify a subagent_type parameter to select
132
132
  return;
133
133
  const messages = instance.messageManager.getMessages();
134
134
  const tokens = instance.messageManager.getLatestTotalTokens();
135
- const lastTools = instance.lastTools;
135
+ const usedTools = instance.usedTools;
136
136
  const toolCount = countToolBlocks(messages);
137
137
  const summary = formatToolTokenSummary(toolCount, tokens);
138
+ const getDisplayParam = (t) => {
139
+ if ((t.stage === "end" || t.stage === "running") &&
140
+ t.compactParams) {
141
+ return t.compactParams;
142
+ }
143
+ const flat = t.parameters.replace(/\n/g, "\\n");
144
+ return flat.length > 30 ? `…${flat.slice(-30)}` : flat;
145
+ };
138
146
  let shortResult = "";
139
147
  if (toolCount > 2) {
140
148
  shortResult += "... ";
141
149
  }
142
- if (lastTools.length > 0) {
143
- shortResult += `${lastTools.join(", ")} `;
144
- }
145
150
  shortResult += summary;
151
+ if (usedTools.length > 0) {
152
+ shortResult +=
153
+ "\n" +
154
+ usedTools
155
+ .map((t) => `${t.name} ${getDisplayParam(t)}`)
156
+ .join("\n");
157
+ }
146
158
  context.onShortResultUpdate?.(shortResult);
147
159
  });
148
160
  return new Promise((resolve) => {
@@ -1 +1 @@
1
- {"version":3,"file":"bashTool.d.ts","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAwCtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAobtB,CAAC"}
1
+ {"version":3,"file":"bashTool.d.ts","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAwCtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAibtB,CAAC"}
@@ -63,7 +63,7 @@ export const bashTool = {
63
63
  },
64
64
  },
65
65
  prompt: () => `
66
- Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
66
+ Executes a given bash command with optional timeout, ensuring proper handling and security measures. Each invocation runs in a fresh shell process starting from the project root.
67
67
 
68
68
  IMPORTANT: This tool is for terminal operations like git, npm, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead.
69
69
 
@@ -122,10 +122,7 @@ Use the gh command via the Bash tool for GitHub-related tasks including working
122
122
  - Do not retry failing commands in a sleep loop — diagnose the root cause.
123
123
  - If waiting for a background task you started with \`run_in_background\`, you will be notified when it completes — do not poll.
124
124
  - If you must poll an external process, use a check command (e.g. \`gh run view\`) rather than sleeping first.
125
- - If you must sleep, keep the duration short (1-5 seconds) to avoid blocking the user.
126
-
127
- # CWD management
128
- Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \`cd\`. You may use \`cd\` if the User explicitly requests it. When you use \`cd\`, the shell working directory will be reset to the original working directory after the command completes.`,
125
+ - If you must sleep, keep the duration short (1-5 seconds) to avoid blocking the user.`,
129
126
  execute: async (args, context) => {
130
127
  const command = args.command;
131
128
  const runInBackground = args.run_in_background;
@@ -1 +1 @@
1
- {"version":3,"file":"cronCreateTool.d.ts","sourceRoot":"","sources":["../../src/tools/cronCreateTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAGjE,eAAO,MAAM,cAAc,EAAE,UAqE5B,CAAC"}
1
+ {"version":3,"file":"cronCreateTool.d.ts","sourceRoot":"","sources":["../../src/tools/cronCreateTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AA+CjE,eAAO,MAAM,cAAc,EAAE,UAkG5B,CAAC"}
@@ -1,25 +1,67 @@
1
1
  import { CRON_CREATE_TOOL_NAME } from "../constants/tools.js";
2
+ import { cronToHuman } from "../utils/cronToHuman.js";
3
+ import { parseCronExpression } from "../utils/parseCronExpression.js";
4
+ const DEFAULT_MAX_AGE_DAYS = 7;
5
+ const MAX_JOBS = 50;
6
+ const CRON_CREATE_DESCRIPTION = `Schedule a prompt to run at a future time within this Wave session — either recurring on a cron schedule, or once at a specific time.`;
7
+ const CRON_CREATE_PROMPT = `Schedule a prompt to be enqueued at a future time. Use for both recurring schedules and one-shot reminders.
8
+
9
+ Uses standard 5-field cron in the user's local timezone: minute hour day-of-month month day-of-week. "0 9 * * *" means 9am local — no timezone conversion needed.
10
+
11
+ ## One-shot tasks (recurring: false)
12
+
13
+ For "remind me at X" or "at <time>, do Y" requests — fire once then auto-delete.
14
+ Pin minute/hour/day-of-month/month to specific values:
15
+ "remind me at 2:30pm today to check the deploy" → cron: "30 14 <today_dom> <today_month> *", recurring: false
16
+ "tomorrow morning, run the smoke test" → cron: "57 8 <tomorrow_dom> <tomorrow_month> *", recurring: false
17
+
18
+ ## Recurring jobs (recurring: true, the default)
19
+
20
+ For "every N minutes" / "every hour" / "weekdays at 9am" requests:
21
+ "*/5 * * * *" (every 5 min), "0 * * * *" (hourly), "0 9 * * 1-5" (weekdays at 9am local)
22
+
23
+ ## Avoid the :00 and :30 minute marks when the task allows it
24
+
25
+ Every user who asks for "9am" gets \`0 9\`, and every user who asks for "hourly" gets \`0 *\` — which means requests from across the planet land on the API at the same instant. When the user's request is approximate, pick a minute that is NOT 0 or 30:
26
+ "every morning around 9" → "57 8 * * *" or "3 9 * * *" (not "0 9 * * *")
27
+ "hourly" → "7 * * * *" (not "0 * * * *")
28
+ "in an hour or so, remind me to..." → pick whatever minute you land on, don't round
29
+
30
+ Only use minute 0 or 30 when the user names that exact time and clearly means it ("at 9:00 sharp", "at half past", coordinating with a meeting). When in doubt, nudge a few minutes early or late — the user will not notice, and the fleet will.
31
+
32
+ ## Session-only
33
+
34
+ Jobs live only in this Wave session — nothing is written to disk, and the job is gone when Wave exits.
35
+
36
+ ## Runtime behavior
37
+
38
+ Jobs only fire while the REPL is idle (not mid-query). The scheduler adds a small deterministic jitter on top of whatever you pick: recurring tasks fire up to 10% of their period late (max 15 min); one-shot tasks landing on :00 or :30 fire up to 90s early. Picking an off-minute is still the bigger lever.
39
+
40
+ Recurring tasks auto-expire after ${DEFAULT_MAX_AGE_DAYS} days — they fire one final time, then are deleted. This bounds session lifetime. Tell the user about the ${DEFAULT_MAX_AGE_DAYS}-day limit when scheduling recurring jobs.
41
+
42
+ Returns a job ID you can pass to CronDelete.`;
2
43
  export const cronCreateTool = {
3
44
  name: CRON_CREATE_TOOL_NAME,
45
+ shouldDefer: true,
4
46
  config: {
5
47
  type: "function",
6
48
  function: {
7
49
  name: CRON_CREATE_TOOL_NAME,
8
- description: "Schedule a prompt to be enqueued at a future time. Use for both recurring schedules and one-shot reminders.",
50
+ description: CRON_CREATE_DESCRIPTION,
9
51
  parameters: {
10
52
  type: "object",
11
53
  properties: {
12
54
  cron: {
13
55
  type: "string",
14
- description: 'Standard 5-field cron expression in local time: "M H DoM Mon DoW"',
56
+ description: 'Standard 5-field cron expression in local time: "M H DoM Mon DoW" (e.g. "*/5 * * * *" = every 5 minutes, "30 14 28 2 *" = Feb 28 at 2:30pm local once).',
15
57
  },
16
58
  prompt: {
17
59
  type: "string",
18
- description: "The prompt to enqueue at each fire time",
60
+ description: "The prompt to enqueue at each fire time.",
19
61
  },
20
62
  recurring: {
21
63
  type: "boolean",
22
- description: "Default: true. true = fire on every cron match until deleted or auto-expired after 7 days. false = fire once at the next match, then auto-delete",
64
+ description: `true (default) = fire on every cron match until deleted or auto-expired after ${DEFAULT_MAX_AGE_DAYS} days. false = fire once at the next match, then auto-delete. Use false for "remind me at X" one-shot requests with pinned minute/hour/dom/month.`,
23
65
  default: true,
24
66
  },
25
67
  },
@@ -27,6 +69,7 @@ export const cronCreateTool = {
27
69
  },
28
70
  },
29
71
  },
72
+ prompt: () => CRON_CREATE_PROMPT,
30
73
  execute: async (args, context) => {
31
74
  const { cron, prompt, recurring = true, } = args;
32
75
  if (!context.cronManager) {
@@ -36,16 +79,38 @@ export const cronCreateTool = {
36
79
  error: "CronManager not available",
37
80
  };
38
81
  }
82
+ // Validate cron expression
83
+ if (!parseCronExpression(cron)) {
84
+ return {
85
+ success: false,
86
+ content: "",
87
+ error: `Invalid cron expression '${cron}'. Expected 5 fields: M H DoM Mon DoW.`,
88
+ };
89
+ }
90
+ // Check max jobs limit
91
+ const existingJobs = context.cronManager.listJobs();
92
+ if (existingJobs.length >= MAX_JOBS) {
93
+ return {
94
+ success: false,
95
+ content: "",
96
+ error: `Too many scheduled jobs (max ${MAX_JOBS}). Cancel one first.`,
97
+ };
98
+ }
39
99
  try {
40
100
  const job = context.cronManager.createJob({
41
101
  cron,
42
102
  prompt,
43
103
  recurring,
44
104
  });
105
+ const humanSchedule = cronToHuman(cron);
106
+ const where = "Session-only (not written to disk, dies when Wave exits)";
107
+ const resultMessage = recurring
108
+ ? `Scheduled recurring job ${job.id} (${humanSchedule}). ${where}. Auto-expires after ${DEFAULT_MAX_AGE_DAYS} days. Use CronDelete to cancel sooner.`
109
+ : `Scheduled one-shot task ${job.id} (${humanSchedule}). ${where}. It will fire once then auto-delete.`;
45
110
  return {
46
111
  success: true,
47
- content: JSON.stringify({ id: job.id }, null, 2),
48
- shortResult: `Scheduled job ${job.id}`,
112
+ content: JSON.stringify({ id: job.id, humanSchedule, recurring }, null, 2),
113
+ shortResult: resultMessage,
49
114
  };
50
115
  }
51
116
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"cronDeleteTool.d.ts","sourceRoot":"","sources":["../../src/tools/cronDeleteTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAGjE,eAAO,MAAM,cAAc,EAAE,UA2C5B,CAAC"}
1
+ {"version":3,"file":"cronDeleteTool.d.ts","sourceRoot":"","sources":["../../src/tools/cronDeleteTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAOjE,eAAO,MAAM,cAAc,EAAE,UA4C5B,CAAC"}
@@ -1,11 +1,14 @@
1
1
  import { CRON_DELETE_TOOL_NAME } from "../constants/tools.js";
2
+ const CRON_DELETE_DESCRIPTION = "Cancel a scheduled cron job by ID";
3
+ const CRON_DELETE_PROMPT = `Cancel a cron job previously scheduled with CronCreate. Removes it from the in-memory session store.`;
2
4
  export const cronDeleteTool = {
3
5
  name: CRON_DELETE_TOOL_NAME,
6
+ shouldDefer: true,
4
7
  config: {
5
8
  type: "function",
6
9
  function: {
7
10
  name: CRON_DELETE_TOOL_NAME,
8
- description: "Cancel a cron job previously scheduled with CronCreate. Removes it from the in-memory session store.",
11
+ description: CRON_DELETE_DESCRIPTION,
9
12
  parameters: {
10
13
  type: "object",
11
14
  properties: {
@@ -18,6 +21,7 @@ export const cronDeleteTool = {
18
21
  },
19
22
  },
20
23
  },
24
+ prompt: () => CRON_DELETE_PROMPT,
21
25
  execute: async (args, context) => {
22
26
  const { id } = args;
23
27
  if (!context.cronManager) {
@@ -1 +1 @@
1
- {"version":3,"file":"cronListTool.d.ts","sourceRoot":"","sources":["../../src/tools/cronListTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAGjE,eAAO,MAAM,YAAY,EAAE,UAkC1B,CAAC"}
1
+ {"version":3,"file":"cronListTool.d.ts","sourceRoot":"","sources":["../../src/tools/cronListTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAOjE,eAAO,MAAM,YAAY,EAAE,UAmC1B,CAAC"}
@@ -1,17 +1,21 @@
1
1
  import { CRON_LIST_TOOL_NAME } from "../constants/tools.js";
2
+ const CRON_LIST_DESCRIPTION = "List scheduled cron jobs";
3
+ const CRON_LIST_PROMPT = `List all cron jobs scheduled via CronCreate in this session.`;
2
4
  export const cronListTool = {
3
5
  name: CRON_LIST_TOOL_NAME,
6
+ shouldDefer: true,
4
7
  config: {
5
8
  type: "function",
6
9
  function: {
7
10
  name: CRON_LIST_TOOL_NAME,
8
- description: "List all cron jobs scheduled via CronCreate in this session.",
11
+ description: CRON_LIST_DESCRIPTION,
9
12
  parameters: {
10
13
  type: "object",
11
14
  properties: {},
12
15
  },
13
16
  },
14
17
  },
18
+ prompt: () => CRON_LIST_PROMPT,
15
19
  execute: async (_args, context) => {
16
20
  if (!context.cronManager) {
17
21
  return {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * EnterWorktree tool - creates an isolated git worktree and switches the session into it.
3
+ * Mirrors Claude Code's EnterWorktree tool behavior and prompt.
4
+ */
5
+ import type { ToolPlugin } from "./types.js";
6
+ export declare const ENTER_WORKTREE_TOOL_PROMPT = "Use this tool ONLY when the user explicitly asks to work in a worktree. This tool creates an isolated git worktree and switches the current session into it.\n\n## When to Use\n\n- The user explicitly says \"worktree\" (e.g., \"start a worktree\", \"work in a worktree\", \"create a worktree\", \"use a worktree\")\n\n## When NOT to Use\n\n- The user asks to create a branch, switch branches, or work on a different branch \u2014 use git commands instead\n- The user asks to fix a bug or work on a feature \u2014 use normal git workflow unless they specifically mention worktrees\n- Never use this tool unless the user explicitly mentions \"worktree\"\n\n## Requirements\n\n- Must be in a git repository\n- Must not already be in a worktree\n\n## Behavior\n\n- Creates a new git worktree inside `.wave/worktrees/` with a new branch based on HEAD\n- Switches the session's working directory to the new worktree\n- Use ExitWorktree to leave the worktree mid-session (keep or remove). On session exit, if still in the worktree, the user will be prompted to keep or remove it\n\n## Parameters\n\n- `name` (optional): A name for the worktree. Each \"/\"-separated segment may contain only letters, digits, dots, underscores, and dashes; max 64 chars total. A random name is generated if not provided.\n";
7
+ export declare const enterWorktreeTool: ToolPlugin;
8
+ //# sourceMappingURL=enterWorktreeTool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enterWorktreeTool.d.ts","sourceRoot":"","sources":["../../src/tools/enterWorktreeTool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAetE,eAAO,MAAM,0BAA0B,qxCA0BtC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,UAsI/B,CAAC"}