wave-agent-sdk 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +92 -23
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +340 -137
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/managers/aiManager.d.ts +14 -36
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +74 -77
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +4 -3
- package/dist/managers/hookManager.d.ts +3 -8
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +39 -29
- package/dist/managers/liveConfigManager.d.ts +55 -18
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/liveConfigManager.js +372 -90
- package/dist/managers/lspManager.d.ts +43 -0
- package/dist/managers/lspManager.d.ts.map +1 -0
- package/dist/managers/lspManager.js +326 -0
- package/dist/managers/messageManager.d.ts +8 -16
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +52 -74
- package/dist/managers/permissionManager.d.ts +66 -0
- package/dist/managers/permissionManager.d.ts.map +1 -0
- package/dist/managers/permissionManager.js +208 -0
- package/dist/managers/skillManager.d.ts +1 -0
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +2 -1
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +0 -1
- package/dist/managers/subagentManager.d.ts +8 -23
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +97 -117
- package/dist/managers/toolManager.d.ts +38 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +66 -2
- package/dist/services/aiService.d.ts +3 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +123 -30
- package/dist/services/configurationService.d.ts +116 -0
- package/dist/services/configurationService.d.ts.map +1 -0
- package/dist/services/configurationService.js +585 -0
- package/dist/services/fileWatcher.d.ts.map +1 -1
- package/dist/services/fileWatcher.js +5 -6
- package/dist/services/hook.d.ts +7 -124
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +46 -458
- package/dist/services/jsonlHandler.d.ts +24 -15
- package/dist/services/jsonlHandler.d.ts.map +1 -1
- package/dist/services/jsonlHandler.js +67 -88
- package/dist/services/memory.d.ts +0 -9
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +2 -49
- package/dist/services/session.d.ts +82 -33
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +275 -181
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +72 -13
- package/dist/tools/deleteFileTool.d.ts.map +1 -1
- package/dist/tools/deleteFileTool.js +25 -0
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +30 -6
- package/dist/tools/lspTool.d.ts +6 -0
- package/dist/tools/lspTool.d.ts.map +1 -0
- package/dist/tools/lspTool.js +589 -0
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +26 -7
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +111 -2
- package/dist/tools/skillTool.js +2 -2
- package/dist/tools/todoWriteTool.d.ts.map +1 -1
- package/dist/tools/todoWriteTool.js +23 -0
- package/dist/tools/types.d.ts +11 -8
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +25 -9
- package/dist/types/commands.d.ts +0 -1
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +69 -0
- package/dist/types/configuration.d.ts.map +1 -0
- package/dist/types/configuration.js +8 -0
- package/dist/types/core.d.ts +10 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/environment.d.ts +41 -0
- package/dist/types/environment.d.ts.map +1 -1
- package/dist/types/fileSearch.d.ts +5 -0
- package/dist/types/fileSearch.d.ts.map +1 -0
- package/dist/types/fileSearch.js +1 -0
- package/dist/types/hooks.d.ts +11 -2
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +1 -7
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +5 -0
- package/dist/types/lsp.d.ts +90 -0
- package/dist/types/lsp.d.ts.map +1 -0
- package/dist/types/lsp.js +4 -0
- package/dist/types/messaging.d.ts +6 -11
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/permissions.d.ts +35 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/permissions.js +12 -0
- package/dist/types/session.d.ts +1 -6
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/skills.d.ts +1 -0
- package/dist/types/skills.d.ts.map +1 -1
- package/dist/types/tools.d.ts +35 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +4 -0
- package/dist/utils/abortUtils.d.ts +34 -0
- package/dist/utils/abortUtils.d.ts.map +1 -0
- package/dist/utils/abortUtils.js +92 -0
- package/dist/utils/bashHistory.d.ts +4 -0
- package/dist/utils/bashHistory.d.ts.map +1 -1
- package/dist/utils/bashHistory.js +21 -4
- package/dist/utils/builtinSubagents.d.ts +7 -0
- package/dist/utils/builtinSubagents.d.ts.map +1 -0
- package/dist/utils/builtinSubagents.js +65 -0
- package/dist/utils/cacheControlUtils.d.ts +8 -33
- package/dist/utils/cacheControlUtils.d.ts.map +1 -1
- package/dist/utils/cacheControlUtils.js +83 -126
- package/dist/utils/constants.d.ts +0 -12
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +1 -13
- package/dist/utils/convertMessagesForAPI.d.ts +2 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +33 -14
- package/dist/utils/fileSearch.d.ts +14 -0
- package/dist/utils/fileSearch.d.ts.map +1 -0
- package/dist/utils/fileSearch.js +88 -0
- package/dist/utils/fileUtils.d.ts +14 -2
- package/dist/utils/fileUtils.d.ts.map +1 -1
- package/dist/utils/fileUtils.js +101 -17
- package/dist/utils/globalLogger.d.ts +0 -14
- package/dist/utils/globalLogger.d.ts.map +1 -1
- package/dist/utils/globalLogger.js +0 -16
- package/dist/utils/largeOutputHandler.d.ts +15 -0
- package/dist/utils/largeOutputHandler.d.ts.map +1 -0
- package/dist/utils/largeOutputHandler.js +40 -0
- package/dist/utils/markdownParser.d.ts.map +1 -1
- package/dist/utils/markdownParser.js +1 -17
- package/dist/utils/messageOperations.d.ts +1 -11
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +7 -24
- package/dist/utils/pathEncoder.d.ts +4 -0
- package/dist/utils/pathEncoder.d.ts.map +1 -1
- package/dist/utils/pathEncoder.js +16 -9
- package/dist/utils/subagentParser.d.ts +2 -2
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +10 -7
- package/dist/utils/tokenEstimator.d.ts +39 -0
- package/dist/utils/tokenEstimator.d.ts.map +1 -0
- package/dist/utils/tokenEstimator.js +55 -0
- package/package.json +5 -8
- package/src/agent.ts +460 -216
- package/src/index.ts +2 -0
- package/src/managers/aiManager.ts +107 -111
- package/src/managers/backgroundBashManager.ts +4 -3
- package/src/managers/hookManager.ts +44 -39
- package/src/managers/liveConfigManager.ts +524 -138
- package/src/managers/lspManager.ts +434 -0
- package/src/managers/messageManager.ts +73 -103
- package/src/managers/permissionManager.ts +276 -0
- package/src/managers/skillManager.ts +3 -1
- package/src/managers/slashCommandManager.ts +1 -2
- package/src/managers/subagentManager.ts +116 -159
- package/src/managers/toolManager.ts +95 -3
- package/src/services/aiService.ts +207 -26
- package/src/services/configurationService.ts +762 -0
- package/src/services/fileWatcher.ts +5 -6
- package/src/services/hook.ts +50 -631
- package/src/services/jsonlHandler.ts +84 -100
- package/src/services/memory.ts +2 -59
- package/src/services/session.ts +338 -213
- package/src/tools/bashTool.ts +89 -16
- package/src/tools/deleteFileTool.ts +36 -0
- package/src/tools/editTool.ts +41 -7
- package/src/tools/lspTool.ts +760 -0
- package/src/tools/multiEditTool.ts +37 -8
- package/src/tools/readTool.ts +125 -2
- package/src/tools/skillTool.ts +2 -2
- package/src/tools/todoWriteTool.ts +33 -1
- package/src/tools/types.ts +15 -9
- package/src/tools/writeTool.ts +36 -10
- package/src/types/commands.ts +0 -1
- package/src/types/config.ts +5 -0
- package/src/types/configuration.ts +73 -0
- package/src/types/core.ts +11 -0
- package/src/types/environment.ts +44 -0
- package/src/types/fileSearch.ts +4 -0
- package/src/types/hooks.ts +14 -11
- package/src/types/index.ts +5 -0
- package/src/types/lsp.ts +96 -0
- package/src/types/messaging.ts +8 -13
- package/src/types/permissions.ts +48 -0
- package/src/types/session.ts +3 -8
- package/src/types/skills.ts +1 -0
- package/src/types/tools.ts +38 -0
- package/src/utils/abortUtils.ts +118 -0
- package/src/utils/bashHistory.ts +28 -4
- package/src/utils/builtinSubagents.ts +71 -0
- package/src/utils/cacheControlUtils.ts +106 -171
- package/src/utils/constants.ts +1 -16
- package/src/utils/convertMessagesForAPI.ts +38 -14
- package/src/utils/fileSearch.ts +107 -0
- package/src/utils/fileUtils.ts +114 -19
- package/src/utils/globalLogger.ts +0 -17
- package/src/utils/largeOutputHandler.ts +55 -0
- package/src/utils/markdownParser.ts +1 -19
- package/src/utils/messageOperations.ts +7 -35
- package/src/utils/pathEncoder.ts +24 -9
- package/src/utils/subagentParser.ts +11 -8
- package/src/utils/tokenEstimator.ts +68 -0
- package/dist/constants/events.d.ts +0 -28
- package/dist/constants/events.d.ts.map +0 -1
- package/dist/constants/events.js +0 -27
- package/dist/services/configurationWatcher.d.ts +0 -120
- package/dist/services/configurationWatcher.d.ts.map +0 -1
- package/dist/services/configurationWatcher.js +0 -439
- package/dist/services/memoryStore.d.ts +0 -81
- package/dist/services/memoryStore.d.ts.map +0 -1
- package/dist/services/memoryStore.js +0 -200
- package/dist/types/memoryStore.d.ts +0 -82
- package/dist/types/memoryStore.d.ts.map +0 -1
- package/dist/types/memoryStore.js +0 -7
- package/dist/utils/configResolver.d.ts +0 -65
- package/dist/utils/configResolver.d.ts.map +0 -1
- package/dist/utils/configResolver.js +0 -210
- package/src/constants/events.ts +0 -38
- package/src/services/configurationWatcher.ts +0 -622
- package/src/services/memoryStore.ts +0 -279
- package/src/types/memoryStore.ts +0 -94
- package/src/utils/configResolver.ts +0 -302
package/src/utils/fileUtils.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
import { homedir } from "node:os";
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Reads the first line of a file efficiently using Node.js readline.
|
|
@@ -7,7 +11,6 @@ import fs from "node:fs";
|
|
|
7
11
|
* @return {Promise<string>} - The first non-empty line of the file, or an empty string otherwise.
|
|
8
12
|
*/
|
|
9
13
|
export async function readFirstLine(filePath: string): Promise<string> {
|
|
10
|
-
const { createReadStream } = fs;
|
|
11
14
|
const { createInterface } = await import("node:readline");
|
|
12
15
|
|
|
13
16
|
const fileStream = createReadStream(filePath);
|
|
@@ -34,32 +37,124 @@ export async function readFirstLine(filePath: string): Promise<string> {
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|
|
37
|
-
* Reads a file from the end and returns the
|
|
40
|
+
* Reads a file from the end and returns the last non-empty line.
|
|
41
|
+
*
|
|
42
|
+
* This version supports files that end with:
|
|
43
|
+
* - "\n" (Unix-style, including modern macOS)
|
|
44
|
+
* - "\r\n" (Windows-style)
|
|
45
|
+
* - "\r" (older Mac-style, HL7, etc.)
|
|
38
46
|
*
|
|
39
47
|
* @param {string} filePath - The path to the file.
|
|
48
|
+
* @param {number} [minLength=1] - Minimum length for the returned line.
|
|
40
49
|
* @return {Promise<string>} - The last non-empty line of the file, or an empty string if no non-empty lines found.
|
|
41
50
|
*/
|
|
42
|
-
export async function getLastLine(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
export async function getLastLine(
|
|
52
|
+
filePath: string,
|
|
53
|
+
minLength = 1,
|
|
54
|
+
): Promise<string> {
|
|
55
|
+
let fileHandle;
|
|
47
56
|
try {
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
const stats = await fs.stat(filePath);
|
|
58
|
+
const fileSize = stats.size;
|
|
59
|
+
|
|
60
|
+
if (fileSize === 0) return "";
|
|
61
|
+
|
|
62
|
+
fileHandle = await fs.open(filePath, "r");
|
|
63
|
+
const bufferSize = 8 * 1024; // 8KB buffer is usually enough for the last line
|
|
64
|
+
const buffer = Buffer.alloc(bufferSize);
|
|
65
|
+
|
|
66
|
+
let lineEnd: number | null = null;
|
|
67
|
+
let lineStart: number | null = null;
|
|
68
|
+
let currentPosition = fileSize;
|
|
69
|
+
|
|
70
|
+
while (currentPosition > 0 && lineStart === null) {
|
|
71
|
+
const readSize = Math.min(bufferSize, currentPosition);
|
|
72
|
+
currentPosition -= readSize;
|
|
73
|
+
|
|
74
|
+
const { bytesRead } = await fileHandle.read(
|
|
75
|
+
buffer,
|
|
76
|
+
0,
|
|
77
|
+
readSize,
|
|
78
|
+
currentPosition,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
for (let i = bytesRead - 1; i >= 0; i--) {
|
|
82
|
+
const charCode = buffer[i];
|
|
83
|
+
if (lineEnd === null) {
|
|
84
|
+
// Still looking for the end of the last non-empty line (skip trailing newlines and whitespace)
|
|
85
|
+
if (charCode > 32) {
|
|
86
|
+
lineEnd = currentPosition + i + 1;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// Looking for the start of the line (the newline before it)
|
|
90
|
+
if (charCode === 10 || charCode === 13) {
|
|
91
|
+
lineStart = currentPosition + i + 1;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
57
95
|
}
|
|
58
96
|
}
|
|
59
97
|
|
|
60
|
-
return "";
|
|
98
|
+
if (lineEnd === null) return "";
|
|
99
|
+
if (lineStart === null) lineStart = 0;
|
|
100
|
+
|
|
101
|
+
const length = lineEnd - lineStart;
|
|
102
|
+
if (length < minLength) return "";
|
|
103
|
+
|
|
104
|
+
const resultBuffer = Buffer.alloc(length);
|
|
105
|
+
await fileHandle.read(resultBuffer, 0, length, lineStart);
|
|
106
|
+
const result = resultBuffer.toString("utf8").trim();
|
|
107
|
+
return result.length >= minLength ? result : "";
|
|
61
108
|
} catch {
|
|
62
|
-
// If
|
|
109
|
+
// If reading fails (e.g., file doesn't exist), return empty string
|
|
63
110
|
return "";
|
|
111
|
+
} finally {
|
|
112
|
+
if (fileHandle) {
|
|
113
|
+
await fileHandle.close();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Ensures that a pattern is present in the global git ignore file.
|
|
120
|
+
*
|
|
121
|
+
* @param {string} pattern - The pattern to add to global git ignore.
|
|
122
|
+
*/
|
|
123
|
+
export async function ensureGlobalGitIgnore(pattern: string): Promise<void> {
|
|
124
|
+
try {
|
|
125
|
+
let globalIgnorePath: string;
|
|
126
|
+
try {
|
|
127
|
+
globalIgnorePath = execSync("git config --get core.excludesfile", {
|
|
128
|
+
encoding: "utf8",
|
|
129
|
+
}).trim();
|
|
130
|
+
} catch {
|
|
131
|
+
// If not set, use default paths
|
|
132
|
+
const xdgConfigHome =
|
|
133
|
+
process.env.XDG_CONFIG_HOME || path.join(homedir(), ".config");
|
|
134
|
+
globalIgnorePath = path.join(xdgConfigHome, "git", "ignore");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!globalIgnorePath) return;
|
|
138
|
+
|
|
139
|
+
// Ensure directory exists
|
|
140
|
+
await fs.mkdir(path.dirname(globalIgnorePath), { recursive: true });
|
|
141
|
+
|
|
142
|
+
let content = "";
|
|
143
|
+
try {
|
|
144
|
+
content = await fs.readFile(globalIgnorePath, "utf8");
|
|
145
|
+
} catch {
|
|
146
|
+
// File doesn't exist
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const lines = content.split("\n").map((line) => line.trim());
|
|
150
|
+
if (!lines.includes(pattern)) {
|
|
151
|
+
const newContent =
|
|
152
|
+
content.endsWith("\n") || content === ""
|
|
153
|
+
? `${content}${pattern}\n`
|
|
154
|
+
: `${content}\n${pattern}\n`;
|
|
155
|
+
await fs.writeFile(globalIgnorePath, newContent, "utf8");
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
// Ignore errors
|
|
64
159
|
}
|
|
65
160
|
}
|
|
@@ -46,23 +46,6 @@ export function setGlobalLogger(logger: Logger | null): void {
|
|
|
46
46
|
globalLogger = logger;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/**
|
|
50
|
-
* Retrieve the current global logger instance
|
|
51
|
-
*
|
|
52
|
-
* @returns Current logger instance or null if unconfigured
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* ```typescript
|
|
56
|
-
* const currentLogger = getGlobalLogger();
|
|
57
|
-
* if (currentLogger) {
|
|
58
|
-
* currentLogger.info('Direct logger access');
|
|
59
|
-
* }
|
|
60
|
-
* ```
|
|
61
|
-
*/
|
|
62
|
-
export function getGlobalLogger(): Logger | null {
|
|
63
|
-
return globalLogger;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
49
|
/**
|
|
67
50
|
* Reset global logger to unconfigured state
|
|
68
51
|
* Equivalent to setGlobalLogger(null)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { writeFile } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { logger } from "./globalLogger.js";
|
|
5
|
+
import {
|
|
6
|
+
estimateTokenCount,
|
|
7
|
+
getTokenUsageDescription,
|
|
8
|
+
} from "./tokenEstimator.js";
|
|
9
|
+
|
|
10
|
+
// Token threshold for writing output to temp file (20k tokens)
|
|
11
|
+
export const LARGE_OUTPUT_TOKEN_THRESHOLD = 20000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Handle large command output by writing to temporary file when token threshold is exceeded
|
|
15
|
+
*
|
|
16
|
+
* Uses token-based threshold (20k tokens) to determine when output should be written to temp file.
|
|
17
|
+
* This provides accurate estimation of actual token cost for LLM processing.
|
|
18
|
+
*
|
|
19
|
+
* @param output - The command output string
|
|
20
|
+
* @returns Object containing processed content and optional file path
|
|
21
|
+
*/
|
|
22
|
+
export async function handleLargeOutput(output: string): Promise<{
|
|
23
|
+
content: string;
|
|
24
|
+
filePath?: string;
|
|
25
|
+
}> {
|
|
26
|
+
const estimatedTokens = estimateTokenCount(output);
|
|
27
|
+
|
|
28
|
+
// Check token threshold
|
|
29
|
+
if (estimatedTokens <= LARGE_OUTPUT_TOKEN_THRESHOLD) {
|
|
30
|
+
return { content: output };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Create temp file for large output
|
|
35
|
+
const tempFileName = `bash-output-${Date.now()}-${Math.random().toString(36).substr(2, 9)}.txt`;
|
|
36
|
+
const tempFilePath = join(tmpdir(), tempFileName);
|
|
37
|
+
|
|
38
|
+
await writeFile(tempFilePath, output, "utf8");
|
|
39
|
+
|
|
40
|
+
const sizeKB = Math.round(output.length / 1024);
|
|
41
|
+
const tokenDescription = getTokenUsageDescription(
|
|
42
|
+
output,
|
|
43
|
+
LARGE_OUTPUT_TOKEN_THRESHOLD,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
content: `Large output (${sizeKB} KB, ${tokenDescription}) written to temporary file. Use the Read tool to access the full content.`,
|
|
48
|
+
filePath: tempFilePath,
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
logger.warn(`Failed to write large output to temp file: ${error}`);
|
|
52
|
+
// Fallback to direct output if temp file creation fails
|
|
53
|
+
return { content: output };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -37,18 +37,7 @@ function parseFrontmatter(content: string): {
|
|
|
37
37
|
const key = trimmedLine.slice(0, colonIndex).trim();
|
|
38
38
|
const value = trimmedLine.slice(colonIndex + 1).trim();
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
if (key === "allowed-tools" && value) {
|
|
42
|
-
// Simple array parsing: "tool1, tool2, tool3" or "[tool1, tool2]"
|
|
43
|
-
let arrayValue = value;
|
|
44
|
-
if (arrayValue.startsWith("[") && arrayValue.endsWith("]")) {
|
|
45
|
-
arrayValue = arrayValue.slice(1, -1);
|
|
46
|
-
}
|
|
47
|
-
frontmatter[key] = arrayValue
|
|
48
|
-
.split(",")
|
|
49
|
-
.map((s) => s.trim())
|
|
50
|
-
.filter(Boolean);
|
|
51
|
-
} else if (value) {
|
|
40
|
+
if (value) {
|
|
52
41
|
frontmatter[key] = value;
|
|
53
42
|
}
|
|
54
43
|
}
|
|
@@ -73,13 +62,6 @@ export function parseMarkdownFile(filePath: string): ParsedMarkdownFile {
|
|
|
73
62
|
if (frontmatter) {
|
|
74
63
|
config = {};
|
|
75
64
|
|
|
76
|
-
if (
|
|
77
|
-
frontmatter["allowed-tools"] &&
|
|
78
|
-
Array.isArray(frontmatter["allowed-tools"])
|
|
79
|
-
) {
|
|
80
|
-
config.allowedTools = frontmatter["allowed-tools"] as string[];
|
|
81
|
-
}
|
|
82
|
-
|
|
83
65
|
if (frontmatter.model && typeof frontmatter.model === "string") {
|
|
84
66
|
config.model = frontmatter.model;
|
|
85
67
|
}
|
|
@@ -47,12 +47,6 @@ export type AgentToolBlockUpdateParams = Omit<
|
|
|
47
47
|
"messages"
|
|
48
48
|
>;
|
|
49
49
|
|
|
50
|
-
export interface AddDiffBlockParams {
|
|
51
|
-
messages: Message[];
|
|
52
|
-
path: string;
|
|
53
|
-
diffResult: Array<{ value: string; added?: boolean; removed?: boolean }>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
50
|
export interface AddErrorBlockParams {
|
|
57
51
|
messages: Message[];
|
|
58
52
|
error: string;
|
|
@@ -186,7 +180,7 @@ export const addAssistantMessageToMessages = (
|
|
|
186
180
|
content?: string,
|
|
187
181
|
toolCalls?: ChatCompletionMessageFunctionToolCall[],
|
|
188
182
|
usage?: Usage,
|
|
189
|
-
|
|
183
|
+
additionalFields?: Record<string, unknown>,
|
|
190
184
|
): Message[] => {
|
|
191
185
|
const blocks: Message["blocks"] = [];
|
|
192
186
|
|
|
@@ -213,34 +207,12 @@ export const addAssistantMessageToMessages = (
|
|
|
213
207
|
role: "assistant",
|
|
214
208
|
blocks,
|
|
215
209
|
usage, // Include usage data if provided
|
|
216
|
-
...(
|
|
210
|
+
...(additionalFields ? { additionalFields: { ...additionalFields } } : {}),
|
|
217
211
|
};
|
|
218
212
|
|
|
219
213
|
return [...messages, initialAssistantMessage];
|
|
220
214
|
};
|
|
221
215
|
|
|
222
|
-
// Update File Operation Block of the last assistant message
|
|
223
|
-
export const addDiffBlockToMessage = ({
|
|
224
|
-
messages,
|
|
225
|
-
path,
|
|
226
|
-
diffResult,
|
|
227
|
-
}: AddDiffBlockParams): Message[] => {
|
|
228
|
-
const newMessages = [...messages];
|
|
229
|
-
// Find the last assistant message
|
|
230
|
-
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
231
|
-
if (newMessages[i].role === "assistant") {
|
|
232
|
-
// Directly add diff block instead of replacing existing blocks
|
|
233
|
-
newMessages[i].blocks.push({
|
|
234
|
-
type: "diff",
|
|
235
|
-
path: path,
|
|
236
|
-
diffResult: diffResult,
|
|
237
|
-
});
|
|
238
|
-
break;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return newMessages;
|
|
242
|
-
};
|
|
243
|
-
|
|
244
216
|
// Update Tool Block of the last assistant message
|
|
245
217
|
export const updateToolBlockInMessage = ({
|
|
246
218
|
messages,
|
|
@@ -453,7 +425,7 @@ export const addCommandOutputMessage = ({
|
|
|
453
425
|
command,
|
|
454
426
|
}: AddCommandOutputParams): Message[] => {
|
|
455
427
|
const outputMessage: Message = {
|
|
456
|
-
role: "
|
|
428
|
+
role: "user",
|
|
457
429
|
blocks: [
|
|
458
430
|
{
|
|
459
431
|
type: "command_output",
|
|
@@ -475,10 +447,10 @@ export const updateCommandOutputInMessage = ({
|
|
|
475
447
|
output,
|
|
476
448
|
}: UpdateCommandOutputParams): Message[] => {
|
|
477
449
|
const newMessages = [...messages];
|
|
478
|
-
// Find the last
|
|
450
|
+
// Find the last user message with a command_output block for this command
|
|
479
451
|
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
480
452
|
const msg = newMessages[i];
|
|
481
|
-
if (msg.role === "
|
|
453
|
+
if (msg.role === "user") {
|
|
482
454
|
const commandBlock = msg.blocks.find(
|
|
483
455
|
(block) =>
|
|
484
456
|
block.type === "command_output" &&
|
|
@@ -501,10 +473,10 @@ export const completeCommandInMessage = ({
|
|
|
501
473
|
exitCode,
|
|
502
474
|
}: CompleteCommandParams): Message[] => {
|
|
503
475
|
const newMessages = [...messages];
|
|
504
|
-
// Find the last
|
|
476
|
+
// Find the last user message with a command_output block for this command
|
|
505
477
|
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
506
478
|
const msg = newMessages[i];
|
|
507
|
-
if (msg.role === "
|
|
479
|
+
if (msg.role === "user") {
|
|
508
480
|
const commandBlock = msg.blocks.find(
|
|
509
481
|
(block) =>
|
|
510
482
|
block.type === "command_output" &&
|
package/src/utils/pathEncoder.ts
CHANGED
|
@@ -190,9 +190,9 @@ export class PathEncoder {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
|
-
*
|
|
193
|
+
* Get project directory info without creating the directory
|
|
194
194
|
*/
|
|
195
|
-
async
|
|
195
|
+
async getProjectDirectory(
|
|
196
196
|
originalPath: string,
|
|
197
197
|
baseSessionDir: string,
|
|
198
198
|
): Promise<ProjectDirectory> {
|
|
@@ -221,13 +221,6 @@ export class PathEncoder {
|
|
|
221
221
|
pathHash = this.generateHash(resolvedPath, this.options.hashLength);
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
// Ensure the encoded directory exists
|
|
225
|
-
try {
|
|
226
|
-
await mkdir(encodedPath, { recursive: true });
|
|
227
|
-
} catch {
|
|
228
|
-
// Ignore errors if directory already exists
|
|
229
|
-
}
|
|
230
|
-
|
|
231
224
|
return {
|
|
232
225
|
originalPath: resolvedPath,
|
|
233
226
|
encodedName,
|
|
@@ -237,6 +230,28 @@ export class PathEncoder {
|
|
|
237
230
|
};
|
|
238
231
|
}
|
|
239
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Create project directory entity from original path
|
|
235
|
+
*/
|
|
236
|
+
async createProjectDirectory(
|
|
237
|
+
originalPath: string,
|
|
238
|
+
baseSessionDir: string,
|
|
239
|
+
): Promise<ProjectDirectory> {
|
|
240
|
+
const projectDirectory = await this.getProjectDirectory(
|
|
241
|
+
originalPath,
|
|
242
|
+
baseSessionDir,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// Ensure the encoded directory exists
|
|
246
|
+
try {
|
|
247
|
+
await mkdir(projectDirectory.encodedPath, { recursive: true });
|
|
248
|
+
} catch {
|
|
249
|
+
// Ignore errors if directory already exists
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return projectDirectory;
|
|
253
|
+
}
|
|
254
|
+
|
|
240
255
|
/**
|
|
241
256
|
* Validate that an encoded name is filesystem-safe
|
|
242
257
|
*/
|
|
@@ -9,7 +9,7 @@ export interface SubagentConfiguration {
|
|
|
9
9
|
model?: string;
|
|
10
10
|
systemPrompt: string;
|
|
11
11
|
filePath: string;
|
|
12
|
-
scope: "project" | "user";
|
|
12
|
+
scope: "project" | "user" | "builtin";
|
|
13
13
|
priority: number;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -101,11 +101,11 @@ function validateConfiguration(
|
|
|
101
101
|
throw new Error(`Missing required field 'description' in ${filePath}`);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
// Validate name pattern
|
|
105
|
-
const namePattern = /^[a-
|
|
104
|
+
// Validate name pattern - allow letters (upper/lowercase), numbers, and hyphens
|
|
105
|
+
const namePattern = /^[a-zA-Z][a-zA-Z0-9-]*$/;
|
|
106
106
|
if (!namePattern.test(config.name)) {
|
|
107
107
|
throw new Error(
|
|
108
|
-
`Invalid subagent name '${config.name}' in ${filePath}. Must start with a letter and contain only
|
|
108
|
+
`Invalid subagent name '${config.name}' in ${filePath}. Must start with a letter and contain only letters, numbers, and hyphens.`,
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -187,7 +187,7 @@ function scanSubagentDirectory(
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
/**
|
|
190
|
-
* Load all subagent configurations from project and user directories
|
|
190
|
+
* Load all subagent configurations from project and user directories, plus built-in subagents
|
|
191
191
|
*/
|
|
192
192
|
export async function loadSubagentConfigurations(
|
|
193
193
|
workdir: string,
|
|
@@ -195,14 +195,17 @@ export async function loadSubagentConfigurations(
|
|
|
195
195
|
const projectDir = join(workdir, ".wave", "agents");
|
|
196
196
|
const userDir = join(process.env.HOME || "~", ".wave", "agents");
|
|
197
197
|
|
|
198
|
+
// Load configurations from all sources
|
|
199
|
+
const { getBuiltinSubagents } = await import("./builtinSubagents.js");
|
|
200
|
+
const builtinConfigs = getBuiltinSubagents();
|
|
198
201
|
const projectConfigs = scanSubagentDirectory(projectDir, "project");
|
|
199
202
|
const userConfigs = scanSubagentDirectory(userDir, "user");
|
|
200
203
|
|
|
201
|
-
// Merge configurations, with project configs taking precedence
|
|
204
|
+
// Merge configurations, with project configs taking highest precedence
|
|
202
205
|
const configMap = new Map<string, SubagentConfiguration>();
|
|
203
206
|
|
|
204
|
-
// Process in reverse priority order (
|
|
205
|
-
for (const config of [...userConfigs, ...projectConfigs]) {
|
|
207
|
+
// Process in reverse priority order (built-in first, then user, then project)
|
|
208
|
+
for (const config of [...builtinConfigs, ...userConfigs, ...projectConfigs]) {
|
|
206
209
|
configMap.set(config.name, config);
|
|
207
210
|
}
|
|
208
211
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple token estimation utility for text content
|
|
3
|
+
*
|
|
4
|
+
* This provides a fast approximation of token count without requiring
|
|
5
|
+
* actual tokenization, which would be expensive for large content.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Estimate the number of tokens in a text string
|
|
10
|
+
*
|
|
11
|
+
* Uses a simple heuristic based on character count and common patterns:
|
|
12
|
+
* - Average token length varies by language and content type
|
|
13
|
+
* - English text: ~4-5 characters per token
|
|
14
|
+
* - Code/structured text: ~3-4 characters per token
|
|
15
|
+
* - Numbers/symbols: ~2-3 characters per token
|
|
16
|
+
*
|
|
17
|
+
* This function uses a conservative estimate of 4 characters per token
|
|
18
|
+
* which works well for mixed content (text + code + symbols).
|
|
19
|
+
*
|
|
20
|
+
* @param text - The text to estimate tokens for
|
|
21
|
+
* @returns Estimated number of tokens
|
|
22
|
+
*/
|
|
23
|
+
export function estimateTokenCount(text: string): number {
|
|
24
|
+
if (!text || text.length === 0) {
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Base estimation: 4 characters per token (conservative)
|
|
29
|
+
const baseEstimate = Math.ceil(text.length / 4);
|
|
30
|
+
|
|
31
|
+
// Adjust for whitespace (spaces don't contribute much to token count)
|
|
32
|
+
const whitespaceCount = (text.match(/\s/g) || []).length;
|
|
33
|
+
const adjustedEstimate = Math.ceil((text.length - whitespaceCount * 0.5) / 4);
|
|
34
|
+
|
|
35
|
+
// Use the more conservative (higher) estimate
|
|
36
|
+
return Math.max(baseEstimate, adjustedEstimate);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if estimated token count exceeds a threshold
|
|
41
|
+
*
|
|
42
|
+
* @param text - The text to check
|
|
43
|
+
* @param threshold - Token threshold (default: 20,000)
|
|
44
|
+
* @returns True if estimated tokens exceed threshold
|
|
45
|
+
*/
|
|
46
|
+
export function exceedsTokenThreshold(
|
|
47
|
+
text: string,
|
|
48
|
+
threshold: number = 20000,
|
|
49
|
+
): boolean {
|
|
50
|
+
return estimateTokenCount(text) > threshold;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get a human-readable description of estimated token usage
|
|
55
|
+
*
|
|
56
|
+
* @param text - The text to analyze
|
|
57
|
+
* @param threshold - Token threshold for comparison
|
|
58
|
+
* @returns Description string with token count and threshold info
|
|
59
|
+
*/
|
|
60
|
+
export function getTokenUsageDescription(
|
|
61
|
+
text: string,
|
|
62
|
+
threshold: number = 20000,
|
|
63
|
+
): string {
|
|
64
|
+
const estimatedTokens = estimateTokenCount(text);
|
|
65
|
+
const exceedsThreshold = estimatedTokens > threshold;
|
|
66
|
+
|
|
67
|
+
return `${estimatedTokens.toLocaleString()} tokens (${exceedsThreshold ? "exceeds" : "within"} ${threshold.toLocaleString()} limit)`;
|
|
68
|
+
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event name constants to prevent typos and ensure consistency
|
|
3
|
-
*
|
|
4
|
-
* Using constants instead of magic strings prevents bugs like:
|
|
5
|
-
* - ConfigurationWatcher emitting 'configurationChange'
|
|
6
|
-
* - LiveConfigManager listening for 'configurationChanged'
|
|
7
|
-
*
|
|
8
|
-
* This pattern ensures compile-time validation of event names.
|
|
9
|
-
*/
|
|
10
|
-
export declare const CONFIGURATION_EVENTS: {
|
|
11
|
-
readonly CONFIGURATION_CHANGE: "configurationChange";
|
|
12
|
-
readonly WATCHER_ERROR: "watcherError";
|
|
13
|
-
};
|
|
14
|
-
export declare const FILE_WATCHER_EVENTS: {
|
|
15
|
-
readonly CHANGE: "change";
|
|
16
|
-
readonly CREATE: "create";
|
|
17
|
-
readonly DELETE: "delete";
|
|
18
|
-
readonly RENAME: "rename";
|
|
19
|
-
};
|
|
20
|
-
export declare const MEMORY_STORE_EVENTS: {
|
|
21
|
-
readonly FILE_ADDED: "fileAdded";
|
|
22
|
-
readonly FILE_CHANGED: "fileChanged";
|
|
23
|
-
readonly FILE_REMOVED: "fileRemoved";
|
|
24
|
-
};
|
|
25
|
-
export type ConfigurationEventName = (typeof CONFIGURATION_EVENTS)[keyof typeof CONFIGURATION_EVENTS];
|
|
26
|
-
export type FileWatcherEventName = (typeof FILE_WATCHER_EVENTS)[keyof typeof FILE_WATCHER_EVENTS];
|
|
27
|
-
export type MemoryStoreEventName = (typeof MEMORY_STORE_EVENTS)[keyof typeof MEMORY_STORE_EVENTS];
|
|
28
|
-
//# sourceMappingURL=events.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/constants/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,eAAO,MAAM,oBAAoB;;;CAGvB,CAAC;AAGX,eAAO,MAAM,mBAAmB;;;;;CAKtB,CAAC;AAGX,eAAO,MAAM,mBAAmB;;;;CAItB,CAAC;AAGX,MAAM,MAAM,sBAAsB,GAChC,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,OAAO,oBAAoB,CAAC,CAAC;AACnE,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC;AACjE,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC"}
|
package/dist/constants/events.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event name constants to prevent typos and ensure consistency
|
|
3
|
-
*
|
|
4
|
-
* Using constants instead of magic strings prevents bugs like:
|
|
5
|
-
* - ConfigurationWatcher emitting 'configurationChange'
|
|
6
|
-
* - LiveConfigManager listening for 'configurationChanged'
|
|
7
|
-
*
|
|
8
|
-
* This pattern ensures compile-time validation of event names.
|
|
9
|
-
*/
|
|
10
|
-
// Configuration Watcher Events
|
|
11
|
-
export const CONFIGURATION_EVENTS = {
|
|
12
|
-
CONFIGURATION_CHANGE: "configurationChange",
|
|
13
|
-
WATCHER_ERROR: "watcherError",
|
|
14
|
-
};
|
|
15
|
-
// File Watcher Events
|
|
16
|
-
export const FILE_WATCHER_EVENTS = {
|
|
17
|
-
CHANGE: "change",
|
|
18
|
-
CREATE: "create",
|
|
19
|
-
DELETE: "delete",
|
|
20
|
-
RENAME: "rename",
|
|
21
|
-
};
|
|
22
|
-
// Memory Store Events
|
|
23
|
-
export const MEMORY_STORE_EVENTS = {
|
|
24
|
-
FILE_ADDED: "fileAdded",
|
|
25
|
-
FILE_CHANGED: "fileChanged",
|
|
26
|
-
FILE_REMOVED: "fileRemoved",
|
|
27
|
-
};
|