wave-agent-sdk 0.8.4 → 0.9.1
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.map +1 -1
- package/dist/agent.js +5 -8
- package/dist/managers/aiManager.d.ts +6 -10
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +38 -13
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +15 -1
- package/dist/managers/messageManager.d.ts +1 -0
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +8 -2
- package/dist/managers/subagentManager.d.ts +2 -11
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +4 -53
- package/dist/prompts/autoMemory.d.ts +2 -0
- package/dist/prompts/autoMemory.d.ts.map +1 -0
- package/dist/prompts/autoMemory.js +33 -0
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +7 -0
- package/dist/services/aiService.js +6 -7
- package/dist/services/configurationService.d.ts +28 -16
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +87 -28
- package/dist/services/hook.js +2 -2
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +20 -9
- package/dist/services/memory.d.ts +22 -4
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +132 -73
- package/dist/types/configuration.d.ts +2 -0
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/types/history.d.ts +2 -0
- package/dist/types/history.d.ts.map +1 -1
- package/dist/types/hooks.d.ts +2 -0
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +17 -7
- package/dist/utils/containerSetup.d.ts +0 -5
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +11 -24
- package/dist/utils/fileSearch.d.ts +1 -6
- package/dist/utils/fileSearch.d.ts.map +1 -1
- package/dist/utils/fileSearch.js +104 -75
- package/dist/utils/gitUtils.d.ts +6 -0
- package/dist/utils/gitUtils.d.ts.map +1 -1
- package/dist/utils/gitUtils.js +18 -0
- package/dist/utils/promptHistory.d.ts +3 -3
- package/dist/utils/promptHistory.d.ts.map +1 -1
- package/dist/utils/promptHistory.js +12 -6
- package/package.json +2 -1
- package/src/agent.ts +7 -18
- package/src/managers/aiManager.ts +73 -28
- package/src/managers/hookManager.ts +22 -1
- package/src/managers/messageManager.ts +12 -2
- package/src/managers/subagentManager.ts +7 -69
- package/src/prompts/autoMemory.ts +33 -0
- package/src/prompts/index.ts +12 -0
- package/src/services/aiService.ts +8 -8
- package/src/services/configurationService.ts +100 -28
- package/src/services/hook.ts +2 -2
- package/src/services/initializationService.ts +24 -12
- package/src/services/memory.ts +144 -82
- package/src/types/configuration.ts +2 -0
- package/src/types/history.ts +2 -0
- package/src/types/hooks.ts +25 -9
- package/src/utils/containerSetup.ts +11 -33
- package/src/utils/fileSearch.ts +112 -80
- package/src/utils/gitUtils.ts +18 -0
- package/src/utils/promptHistory.ts +20 -6
package/src/types/hooks.ts
CHANGED
|
@@ -26,6 +26,8 @@ export type HookEvent =
|
|
|
26
26
|
export interface HookCommand {
|
|
27
27
|
type: "command";
|
|
28
28
|
command: string;
|
|
29
|
+
async?: boolean;
|
|
30
|
+
timeout?: number; // seconds
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
// Hook event configuration with optional pattern matching
|
|
@@ -104,15 +106,29 @@ export function isValidHookEvent(event: string): event is HookEvent {
|
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
export function isValidHookCommand(cmd: unknown): cmd is HookCommand {
|
|
107
|
-
|
|
108
|
-
typeof cmd
|
|
109
|
-
cmd
|
|
110
|
-
"type" in cmd
|
|
111
|
-
cmd.type
|
|
112
|
-
"command" in cmd
|
|
113
|
-
typeof cmd.command
|
|
114
|
-
cmd.command.length
|
|
115
|
-
)
|
|
109
|
+
if (
|
|
110
|
+
typeof cmd !== "object" ||
|
|
111
|
+
cmd === null ||
|
|
112
|
+
!("type" in cmd) ||
|
|
113
|
+
cmd.type !== "command" ||
|
|
114
|
+
!("command" in cmd) ||
|
|
115
|
+
typeof cmd.command !== "string" ||
|
|
116
|
+
cmd.command.length === 0
|
|
117
|
+
) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const hookCmd = cmd as Record<string, unknown>;
|
|
122
|
+
|
|
123
|
+
if ("async" in hookCmd && typeof hookCmd.async !== "boolean") {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if ("timeout" in hookCmd && typeof hookCmd.timeout !== "number") {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return true;
|
|
116
132
|
}
|
|
117
133
|
|
|
118
134
|
export function isValidHookEventConfig(
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as fs from "fs/promises";
|
|
2
1
|
import { Container } from "./container.js";
|
|
3
2
|
import { ForegroundTaskManager } from "../managers/foregroundTaskManager.js";
|
|
4
3
|
import { BackgroundTaskManager } from "../managers/backgroundTaskManager.js";
|
|
@@ -21,6 +20,7 @@ import { SubagentManager } from "../managers/subagentManager.js";
|
|
|
21
20
|
import { LiveConfigManager } from "../managers/liveConfigManager.js";
|
|
22
21
|
import { ConfigurationService } from "../services/configurationService.js";
|
|
23
22
|
import { ReversionService } from "../services/reversionService.js";
|
|
23
|
+
import { MemoryService } from "../services/memory.js";
|
|
24
24
|
import type { AgentOptions } from "../types/index.js";
|
|
25
25
|
import type {
|
|
26
26
|
PermissionMode,
|
|
@@ -29,7 +29,6 @@ import type {
|
|
|
29
29
|
BackgroundTask,
|
|
30
30
|
ToolPermissionContext,
|
|
31
31
|
} from "../types/index.js";
|
|
32
|
-
import type { GatewayConfig, ModelConfig } from "../types/config.js";
|
|
33
32
|
|
|
34
33
|
import { logger } from "./globalLogger.js";
|
|
35
34
|
|
|
@@ -48,12 +47,6 @@ export interface AgentContainerSetupOptions {
|
|
|
48
47
|
setPermissionMode: (mode: PermissionMode) => void;
|
|
49
48
|
addPermissionRule: (rule: string) => Promise<void>;
|
|
50
49
|
addUsage: (usage: Usage) => void;
|
|
51
|
-
|
|
52
|
-
// Getters
|
|
53
|
-
getGatewayConfig: () => GatewayConfig;
|
|
54
|
-
getModelConfig: () => ModelConfig;
|
|
55
|
-
getMaxInputTokens: () => number;
|
|
56
|
-
getLanguage: () => string | undefined;
|
|
57
50
|
}
|
|
58
51
|
|
|
59
52
|
export function setupAgentContainer(
|
|
@@ -72,10 +65,6 @@ export function setupAgentContainer(
|
|
|
72
65
|
setPermissionMode,
|
|
73
66
|
addPermissionRule,
|
|
74
67
|
addUsage,
|
|
75
|
-
getGatewayConfig,
|
|
76
|
-
getModelConfig,
|
|
77
|
-
getMaxInputTokens,
|
|
78
|
-
getLanguage,
|
|
79
68
|
} = setupOptions;
|
|
80
69
|
|
|
81
70
|
const callbacks = options.callbacks || {};
|
|
@@ -85,6 +74,9 @@ export function setupAgentContainer(
|
|
|
85
74
|
container.register("ForegroundTaskManager", foregroundTaskManager);
|
|
86
75
|
container.register("ConfigurationService", configurationService);
|
|
87
76
|
|
|
77
|
+
const memoryService = new MemoryService(container);
|
|
78
|
+
container.register("MemoryService", memoryService);
|
|
79
|
+
|
|
88
80
|
const memoryRuleManager = new MemoryRuleManager(container, { workdir });
|
|
89
81
|
container.register("MemoryRuleManager", memoryRuleManager);
|
|
90
82
|
|
|
@@ -139,6 +131,10 @@ export function setupAgentContainer(
|
|
|
139
131
|
container.register("LspManager", lspManager);
|
|
140
132
|
|
|
141
133
|
const permissionManager = new PermissionManager(container, { workdir });
|
|
134
|
+
if (configurationService.resolveAutoMemoryEnabled()) {
|
|
135
|
+
const autoMemoryDir = memoryService.getAutoMemoryDirectory(workdir);
|
|
136
|
+
permissionManager.updateAdditionalDirectories([autoMemoryDir]);
|
|
137
|
+
}
|
|
142
138
|
container.register("PermissionManager", permissionManager);
|
|
143
139
|
permissionManager.setOnConfiguredDefaultModeChange((mode) => {
|
|
144
140
|
handlePlanModeTransition(mode);
|
|
@@ -198,17 +194,9 @@ export function setupAgentContainer(
|
|
|
198
194
|
if (decision.clearContext) {
|
|
199
195
|
messageManager.clearMessages();
|
|
200
196
|
if (planFilePath) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
content: `Implement the following plan:\n\n${planContent}`,
|
|
205
|
-
});
|
|
206
|
-
} catch (error) {
|
|
207
|
-
logger.warn("Failed to read plan file for context clearing", {
|
|
208
|
-
planFilePath,
|
|
209
|
-
error: error instanceof Error ? error.message : String(error),
|
|
210
|
-
});
|
|
211
|
-
}
|
|
197
|
+
messageManager.addUserMessage({
|
|
198
|
+
content: `Implement the plan at ${planFilePath}`,
|
|
199
|
+
});
|
|
212
200
|
}
|
|
213
201
|
}
|
|
214
202
|
|
|
@@ -246,11 +234,6 @@ export function setupAgentContainer(
|
|
|
246
234
|
onSubagentLatestTotalTokensChange:
|
|
247
235
|
callbacks.onSubagentLatestTotalTokensChange,
|
|
248
236
|
},
|
|
249
|
-
getGatewayConfig,
|
|
250
|
-
getModelConfig,
|
|
251
|
-
getMaxInputTokens,
|
|
252
|
-
getLanguage,
|
|
253
|
-
getEnvironmentVars: () => configurationService.getEnvironmentVars(),
|
|
254
237
|
onUsageAdded: (usage: Usage) => addUsage(usage),
|
|
255
238
|
});
|
|
256
239
|
container.register("SubagentManager", subagentManager);
|
|
@@ -263,11 +246,6 @@ export function setupAgentContainer(
|
|
|
263
246
|
workdir,
|
|
264
247
|
systemPrompt,
|
|
265
248
|
stream,
|
|
266
|
-
getGatewayConfig,
|
|
267
|
-
getModelConfig,
|
|
268
|
-
getMaxInputTokens,
|
|
269
|
-
getLanguage,
|
|
270
|
-
getEnvironmentVars: () => configurationService.getEnvironmentVars(),
|
|
271
249
|
});
|
|
272
250
|
container.register("AIManager", aiManager);
|
|
273
251
|
|
package/src/utils/fileSearch.ts
CHANGED
|
@@ -1,26 +1,78 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { rgPath } from "@vscode/ripgrep";
|
|
3
|
+
import fuzzysort from "fuzzysort";
|
|
2
4
|
import { getGlobIgnorePatterns } from "./fileFilter.js";
|
|
3
5
|
import type { FileItem } from "../types/fileSearch.js";
|
|
6
|
+
import { logger } from "./globalLogger.js";
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
|
-
*
|
|
9
|
+
* Execute ripgrep to get all file paths
|
|
7
10
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
async function getAllFiles(workingDirectory: string): Promise<string[]> {
|
|
12
|
+
if (!rgPath) {
|
|
13
|
+
throw new Error("ripgrep is not available");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ignorePatterns = getGlobIgnorePatterns(workingDirectory);
|
|
17
|
+
const rgArgs = ["--files", "--color=never", "--hidden"];
|
|
18
|
+
for (const pattern of ignorePatterns) {
|
|
19
|
+
rgArgs.push("--glob", `!${pattern}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const child = spawn(rgPath, rgArgs, {
|
|
24
|
+
cwd: workingDirectory,
|
|
25
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
let stdout = "";
|
|
29
|
+
let stderr = "";
|
|
30
|
+
|
|
31
|
+
child.stdout?.on("data", (data) => {
|
|
32
|
+
stdout += data.toString();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
child.stderr?.on("data", (data) => {
|
|
36
|
+
stderr += data.toString();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
child.on("close", (code) => {
|
|
40
|
+
if (code !== 0 && code !== 1) {
|
|
41
|
+
reject(new Error(`ripgrep failed: ${stderr}`));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const files = stdout
|
|
45
|
+
.trim()
|
|
46
|
+
.split("\n")
|
|
47
|
+
.filter((f) => f.length > 0)
|
|
48
|
+
.map((f) => f.replace(/\\/g, "/")); // Normalize to forward slashes
|
|
49
|
+
resolve(files);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
child.on("error", (err) => {
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
19
55
|
});
|
|
20
|
-
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Derive directory paths from file paths
|
|
60
|
+
*/
|
|
61
|
+
function deriveDirectories(files: string[]): string[] {
|
|
62
|
+
const dirs = new Set<string>();
|
|
63
|
+
for (const file of files) {
|
|
64
|
+
const parts = file.split("/");
|
|
65
|
+
// Add all parent directories
|
|
66
|
+
for (let i = 1; i < parts.length; i++) {
|
|
67
|
+
const dir = parts.slice(0, i).join("/") + "/";
|
|
68
|
+
dirs.add(dir);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return Array.from(dirs);
|
|
72
|
+
}
|
|
21
73
|
|
|
22
74
|
/**
|
|
23
|
-
* Search files and directories using
|
|
75
|
+
* Search files and directories using fuzzy matching
|
|
24
76
|
*/
|
|
25
77
|
export const searchFiles = async (
|
|
26
78
|
query: string,
|
|
@@ -32,83 +84,63 @@ export const searchFiles = async (
|
|
|
32
84
|
const { maxResults = 10, workingDirectory = process.cwd() } = options || {};
|
|
33
85
|
|
|
34
86
|
try {
|
|
35
|
-
const
|
|
36
|
-
ignore: getGlobIgnorePatterns(workingDirectory),
|
|
37
|
-
maxDepth: 10,
|
|
38
|
-
nocase: true, // Case insensitive
|
|
39
|
-
dot: true, // Include hidden files and directories
|
|
40
|
-
cwd: workingDirectory, // Specify search root directory
|
|
41
|
-
withFileTypes: true, // Get Path objects instead of strings
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Build glob patterns based on query
|
|
45
|
-
let patterns: string[] = [];
|
|
87
|
+
const files = await getAllFiles(workingDirectory);
|
|
46
88
|
|
|
47
89
|
if (!query.trim()) {
|
|
48
|
-
// When query is empty, show some common file types and directories
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// Match directory names containing query
|
|
61
|
-
`**/*${query}*/`,
|
|
62
|
-
// Match directories containing query in path
|
|
63
|
-
`**/${query}*/`,
|
|
64
|
-
];
|
|
65
|
-
}
|
|
90
|
+
// When query is empty, show some common file types and top-level directories
|
|
91
|
+
const commonExtensions = new Set([
|
|
92
|
+
"ts",
|
|
93
|
+
"tsx",
|
|
94
|
+
"js",
|
|
95
|
+
"jsx",
|
|
96
|
+
"json",
|
|
97
|
+
"py",
|
|
98
|
+
"java",
|
|
99
|
+
]);
|
|
100
|
+
const results: FileItem[] = [];
|
|
101
|
+
const seenDirs = new Set<string>();
|
|
66
102
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Use globIterate to get results one by one
|
|
78
|
-
const iterator = globIterate(pattern, globOptions) as AsyncGenerator<
|
|
79
|
-
Path,
|
|
80
|
-
void,
|
|
81
|
-
void
|
|
82
|
-
>;
|
|
83
|
-
|
|
84
|
-
for await (const pathObj of iterator) {
|
|
85
|
-
if (collectedPaths.length >= maxResults) {
|
|
86
|
-
// Stop the iterator when we have enough results
|
|
87
|
-
break;
|
|
103
|
+
for (const file of files) {
|
|
104
|
+
const parts = file.split("/");
|
|
105
|
+
if (parts.length > 1) {
|
|
106
|
+
const topDir = parts[0] + "/";
|
|
107
|
+
if (!seenDirs.has(topDir)) {
|
|
108
|
+
seenDirs.add(topDir);
|
|
109
|
+
results.push({ path: topDir, type: "directory" });
|
|
110
|
+
}
|
|
88
111
|
}
|
|
89
112
|
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
collectedPaths.push(pathObj);
|
|
113
|
+
const ext = file.split(".").pop();
|
|
114
|
+
if (ext && commonExtensions.has(ext)) {
|
|
115
|
+
results.push({ path: file, type: "file" });
|
|
94
116
|
}
|
|
95
117
|
}
|
|
118
|
+
|
|
119
|
+
// Sort: directories first, then files, then alphabetically
|
|
120
|
+
return results
|
|
121
|
+
.sort((a, b) => {
|
|
122
|
+
if (a.type === "directory" && b.type === "file") return -1;
|
|
123
|
+
if (a.type === "file" && b.type === "directory") return 1;
|
|
124
|
+
return a.path.localeCompare(b.path);
|
|
125
|
+
})
|
|
126
|
+
.slice(0, maxResults);
|
|
96
127
|
}
|
|
97
128
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
const directories = deriveDirectories(files);
|
|
130
|
+
const allItems: FileItem[] = [
|
|
131
|
+
...files.map((f) => ({ path: f, type: "file" as const })),
|
|
132
|
+
...directories.map((d) => ({ path: d, type: "directory" as const })),
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
const fuzzyResults = fuzzysort.go(query, allItems, {
|
|
136
|
+
key: "path",
|
|
137
|
+
limit: maxResults,
|
|
138
|
+
threshold: 0,
|
|
105
139
|
});
|
|
106
140
|
|
|
107
|
-
|
|
108
|
-
const fileItems = convertPathsToFileItems(collectedPaths);
|
|
109
|
-
return fileItems;
|
|
141
|
+
return fuzzyResults.map((res) => res.obj);
|
|
110
142
|
} catch (error) {
|
|
111
|
-
|
|
143
|
+
logger.error("Fuzzy search error:", error);
|
|
112
144
|
return [];
|
|
113
145
|
}
|
|
114
146
|
};
|
package/src/utils/gitUtils.ts
CHANGED
|
@@ -41,6 +41,24 @@ export function getGitRepoRoot(cwd: string): string {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Get the common directory of the git repository (handles worktrees)
|
|
46
|
+
* @param cwd Working directory
|
|
47
|
+
* @returns Repository common directory path
|
|
48
|
+
*/
|
|
49
|
+
export function getGitCommonDir(cwd: string): string {
|
|
50
|
+
try {
|
|
51
|
+
const commonDir = execSync("git rev-parse --git-common-dir", {
|
|
52
|
+
cwd,
|
|
53
|
+
encoding: "utf8",
|
|
54
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
55
|
+
}).trim();
|
|
56
|
+
return path.resolve(cwd, commonDir);
|
|
57
|
+
} catch {
|
|
58
|
+
return getGitRepoRoot(cwd);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
44
62
|
/**
|
|
45
63
|
* Get the default remote branch (e.g., origin/main)
|
|
46
64
|
* @param cwd Working directory
|
|
@@ -22,7 +22,11 @@ export class PromptHistoryManager {
|
|
|
22
22
|
/**
|
|
23
23
|
* Add a new prompt to history
|
|
24
24
|
*/
|
|
25
|
-
static async addEntry(
|
|
25
|
+
static async addEntry(
|
|
26
|
+
prompt: string,
|
|
27
|
+
sessionId?: string,
|
|
28
|
+
longTextMap?: Record<string, string>,
|
|
29
|
+
): Promise<void> {
|
|
26
30
|
try {
|
|
27
31
|
if (!prompt.trim()) return;
|
|
28
32
|
|
|
@@ -30,6 +34,8 @@ export class PromptHistoryManager {
|
|
|
30
34
|
const entry: PromptEntry = {
|
|
31
35
|
prompt,
|
|
32
36
|
timestamp: Date.now(),
|
|
37
|
+
sessionId,
|
|
38
|
+
longTextMap,
|
|
33
39
|
};
|
|
34
40
|
|
|
35
41
|
const line = JSON.stringify(entry) + "\n";
|
|
@@ -71,7 +77,7 @@ export class PromptHistoryManager {
|
|
|
71
77
|
/**
|
|
72
78
|
* Get all history entries
|
|
73
79
|
*/
|
|
74
|
-
static async getHistory(): Promise<PromptEntry[]> {
|
|
80
|
+
static async getHistory(sessionId?: string): Promise<PromptEntry[]> {
|
|
75
81
|
try {
|
|
76
82
|
if (!fs.existsSync(PROMPT_HISTORY_FILE)) {
|
|
77
83
|
return [];
|
|
@@ -91,13 +97,18 @@ export class PromptHistoryManager {
|
|
|
91
97
|
})
|
|
92
98
|
.filter((entry): entry is PromptEntry => entry !== null);
|
|
93
99
|
|
|
100
|
+
// Filter by sessionId if provided
|
|
101
|
+
const filteredEntries = sessionId
|
|
102
|
+
? entries.filter((entry) => entry.sessionId === sessionId)
|
|
103
|
+
: entries;
|
|
104
|
+
|
|
94
105
|
// Deduplicate by prompt, keeping the most recent one
|
|
95
106
|
const uniqueEntries: PromptEntry[] = [];
|
|
96
107
|
const seenPrompts = new Set<string>();
|
|
97
108
|
|
|
98
109
|
// Process from newest to oldest
|
|
99
|
-
for (let i =
|
|
100
|
-
const entry =
|
|
110
|
+
for (let i = filteredEntries.length - 1; i >= 0; i--) {
|
|
111
|
+
const entry = filteredEntries[i];
|
|
101
112
|
if (!seenPrompts.has(entry.prompt)) {
|
|
102
113
|
uniqueEntries.push(entry);
|
|
103
114
|
seenPrompts.add(entry.prompt);
|
|
@@ -114,9 +125,12 @@ export class PromptHistoryManager {
|
|
|
114
125
|
/**
|
|
115
126
|
* Search history by query
|
|
116
127
|
*/
|
|
117
|
-
static async searchHistory(
|
|
128
|
+
static async searchHistory(
|
|
129
|
+
query: string,
|
|
130
|
+
sessionId?: string,
|
|
131
|
+
): Promise<PromptEntry[]> {
|
|
118
132
|
try {
|
|
119
|
-
const history = await this.getHistory();
|
|
133
|
+
const history = await this.getHistory(sessionId);
|
|
120
134
|
if (!query.trim()) {
|
|
121
135
|
return history;
|
|
122
136
|
}
|