wave-agent-sdk 1.0.0 → 1.0.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 +9 -20
- package/dist/agent.js +23 -97
- package/dist/managers/aiManager.d.ts +63 -8
- package/dist/managers/aiManager.js +270 -80
- package/dist/managers/messageManager.d.ts +9 -5
- package/dist/managers/messageManager.js +36 -12
- package/dist/managers/subagentManager.d.ts +6 -0
- package/dist/managers/subagentManager.js +33 -22
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +0 -4
- package/dist/services/aiService.d.ts +1 -34
- package/dist/services/aiService.js +18 -130
- package/dist/services/autoMemoryService.d.ts +27 -2
- package/dist/services/autoMemoryService.js +124 -36
- package/dist/services/configurationService.js +14 -1
- package/dist/services/session.d.ts +13 -0
- package/dist/services/session.js +64 -0
- package/dist/types/agent.d.ts +0 -2
- package/dist/types/config.d.ts +7 -0
- package/dist/types/core.d.ts +1 -1
- package/dist/utils/containerSetup.js +12 -3
- package/package.json +1 -1
- package/src/agent.ts +36 -110
- package/src/managers/aiManager.ts +366 -105
- package/src/managers/messageManager.ts +51 -23
- package/src/managers/subagentManager.ts +36 -25
- package/src/prompts/index.ts +0 -4
- package/src/services/aiService.ts +25 -203
- package/src/services/autoMemoryService.ts +145 -39
- package/src/services/configurationService.ts +16 -1
- package/src/services/session.ts +68 -0
- package/src/types/agent.ts +0 -6
- package/src/types/config.ts +7 -0
- package/src/types/core.ts +1 -1
- package/src/utils/containerSetup.ts +12 -4
- package/dist/constants/goalPrompts.d.ts +0 -1
- package/dist/constants/goalPrompts.js +0 -10
- package/dist/managers/goalManager.d.ts +0 -42
- package/dist/managers/goalManager.js +0 -177
- package/src/constants/goalPrompts.ts +0 -10
- package/src/managers/goalManager.ts +0 -232
package/dist/services/session.js
CHANGED
|
@@ -481,6 +481,70 @@ export async function cleanupEmptyProjectDirectories() {
|
|
|
481
481
|
// Ignore errors if base directory doesn't exist or can't be accessed
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
|
+
/**
|
|
485
|
+
* Clean up "ghost" session files that contain only meta messages
|
|
486
|
+
* (isMeta: true) — e.g. sessions where a SessionStart hook injected a
|
|
487
|
+
* system-reminder but no real user/assistant message was ever sent.
|
|
488
|
+
*
|
|
489
|
+
* Such sessions are no longer created thanks to lazy materialization in
|
|
490
|
+
* saveSession(); this one-time sweep removes files that predate that change
|
|
491
|
+
* so they stop showing up as "0 tokens / No content" entries in the resume
|
|
492
|
+
* list.
|
|
493
|
+
*
|
|
494
|
+
* @returns Promise that resolves to the number of files deleted
|
|
495
|
+
*/
|
|
496
|
+
export async function cleanupMetaOnlySessions() {
|
|
497
|
+
// Do not perform cleanup operations in test environment
|
|
498
|
+
if (process.env.NODE_ENV === "test") {
|
|
499
|
+
return 0;
|
|
500
|
+
}
|
|
501
|
+
let deletedCount = 0;
|
|
502
|
+
try {
|
|
503
|
+
const projectDirs = await fs.readdir(SESSION_DIR);
|
|
504
|
+
for (const projectDirName of projectDirs) {
|
|
505
|
+
const projectPath = join(SESSION_DIR, projectDirName);
|
|
506
|
+
try {
|
|
507
|
+
const stat = await fs.stat(projectPath);
|
|
508
|
+
if (!stat.isDirectory()) {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
const files = await fs.readdir(projectPath);
|
|
512
|
+
for (const file of files) {
|
|
513
|
+
if (!file.endsWith(".jsonl")) {
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
const filePath = join(projectPath, file);
|
|
517
|
+
try {
|
|
518
|
+
// Fast path: a file whose last message is not meta contains a
|
|
519
|
+
// real message, so it can never be meta-only.
|
|
520
|
+
const jsonlHandler = new JsonlHandler();
|
|
521
|
+
const lastMessage = await jsonlHandler.getLastMessage(filePath);
|
|
522
|
+
if (!lastMessage?.isMeta) {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
const messages = await jsonlHandler.read(filePath);
|
|
526
|
+
if (messages.length > 0 && messages.every((m) => m.isMeta)) {
|
|
527
|
+
await fs.unlink(filePath);
|
|
528
|
+
deletedCount++;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
catch {
|
|
532
|
+
// Skip corrupted or unreadable files
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
// Skip directories we can't access
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
// Ignore errors if base directory doesn't exist or can't be accessed
|
|
545
|
+
}
|
|
546
|
+
return deletedCount;
|
|
547
|
+
}
|
|
484
548
|
/**
|
|
485
549
|
* Check if a session exists in JSONL storage (new approach)
|
|
486
550
|
*
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -98,6 +98,4 @@ export interface AgentCallbacks extends MessageManagerCallbacks, BackgroundTaskM
|
|
|
98
98
|
onCommandRunningChange?: (running: boolean) => void;
|
|
99
99
|
onWorkdirChange?: (newCwd: string) => void;
|
|
100
100
|
onQueuedMessagesChange?: (messages: QueuedMessage[]) => void;
|
|
101
|
-
onGoalStateChange?: (active: boolean, condition?: string, elapsed?: string) => void;
|
|
102
|
-
onGoalEvaluating?: (evaluating: boolean) => void;
|
|
103
101
|
}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -29,4 +29,11 @@ export interface ModelConfig {
|
|
|
29
29
|
options?: Record<string, unknown>;
|
|
30
30
|
/** Fast model generation params (resolved from models[fastModel].options) */
|
|
31
31
|
fastModelOptions?: Record<string, unknown>;
|
|
32
|
+
/**
|
|
33
|
+
* Fast-model-only disable-thinking params passed through verbatim
|
|
34
|
+
* (e.g. `{ thinking: { type: "disabled" } }`). Applied only in fast-model
|
|
35
|
+
* scenarios (webFetch content processing, `model: fastModel` subagents),
|
|
36
|
+
* never in the agent loop. `{}` clears the default.
|
|
37
|
+
*/
|
|
38
|
+
disableThinkingOptions?: Record<string, unknown>;
|
|
32
39
|
}
|
package/dist/types/core.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface Usage {
|
|
|
22
22
|
completion_tokens: number;
|
|
23
23
|
total_tokens: number;
|
|
24
24
|
model?: string;
|
|
25
|
-
operation_type?: "agent" | "compact"
|
|
25
|
+
operation_type?: "agent" | "compact";
|
|
26
26
|
cache_read_input_tokens?: number;
|
|
27
27
|
cache_creation_input_tokens?: number;
|
|
28
28
|
cache_creation?: {
|
|
@@ -16,7 +16,6 @@ import { SlashCommandManager } from "../managers/slashCommandManager.js";
|
|
|
16
16
|
import { PluginManager } from "../managers/pluginManager.js";
|
|
17
17
|
import { BangManager } from "../managers/bangManager.js";
|
|
18
18
|
import { CronManager } from "../managers/cronManager.js";
|
|
19
|
-
import { GoalManager } from "../managers/goalManager.js";
|
|
20
19
|
import { WorkflowManager } from "../managers/workflowManager.js";
|
|
21
20
|
import { MemoryRuleManager } from "../managers/MemoryRuleManager.js";
|
|
22
21
|
import { ReversionManager } from "../managers/reversionManager.js";
|
|
@@ -24,6 +23,7 @@ import { SubagentManager } from "../managers/subagentManager.js";
|
|
|
24
23
|
import { ForkedAgentManager } from "../managers/forkedAgentManager.js";
|
|
25
24
|
import { LiveConfigManager } from "../managers/liveConfigManager.js";
|
|
26
25
|
import { ReversionService } from "../services/reversionService.js";
|
|
26
|
+
import { cleanupMetaOnlySessions } from "../services/session.js";
|
|
27
27
|
import { MemoryService } from "../services/memory.js";
|
|
28
28
|
import { AutoMemoryService } from "../services/autoMemoryService.js";
|
|
29
29
|
import { USER_MEMORY_FILE } from "./constants.js";
|
|
@@ -144,6 +144,17 @@ export function setupAgentContainer(setupOptions) {
|
|
|
144
144
|
reversionService.cleanupOldSessions(30).catch((error) => {
|
|
145
145
|
logger.error("Failed to cleanup old file history:", error);
|
|
146
146
|
});
|
|
147
|
+
// Remove pre-existing meta-only session files (SessionStart hook context
|
|
148
|
+
// with no real messages) that were persisted before lazy materialization.
|
|
149
|
+
cleanupMetaOnlySessions()
|
|
150
|
+
.then((count) => {
|
|
151
|
+
if (count > 0) {
|
|
152
|
+
logger.debug(`Removed ${count} meta-only session file(s)`);
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
.catch((error) => {
|
|
156
|
+
logger.error("Failed to cleanup meta-only session files:", error);
|
|
157
|
+
});
|
|
147
158
|
const reversionManager = new ReversionManager(container);
|
|
148
159
|
container.register("ReversionManager", reversionManager);
|
|
149
160
|
const canUseToolWithPermissionRequest = options.canUseTool
|
|
@@ -255,8 +266,6 @@ export function setupAgentContainer(setupOptions) {
|
|
|
255
266
|
const cronManager = new CronManager(container, messageManager.getSessionId());
|
|
256
267
|
container.register("CronManager", cronManager);
|
|
257
268
|
cronManager.start();
|
|
258
|
-
const goalManager = new GoalManager(container);
|
|
259
|
-
container.register("GoalManager", goalManager);
|
|
260
269
|
const workflowManager = new WorkflowManager(container);
|
|
261
270
|
container.register("WorkflowManager", workflowManager);
|
|
262
271
|
return container;
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { McpManager } from "./managers/mcpManager.js";
|
|
|
8
8
|
import { LspManager } from "./managers/lspManager.js";
|
|
9
9
|
import { BangManager } from "./managers/bangManager.js";
|
|
10
10
|
import { CronManager } from "./managers/cronManager.js";
|
|
11
|
-
import { GoalManager } from "./managers/goalManager.js";
|
|
12
11
|
import { BackgroundTaskManager } from "./managers/backgroundTaskManager.js";
|
|
13
12
|
import { MessageQueue, type QueuedMessage } from "./managers/messageQueue.js";
|
|
14
13
|
import { SlashCommandManager } from "./managers/slashCommandManager.js";
|
|
@@ -38,9 +37,6 @@ import { LiveConfigManager } from "./managers/liveConfigManager.js";
|
|
|
38
37
|
import { configValidator } from "./utils/configValidator.js";
|
|
39
38
|
import { SkillManager } from "./managers/skillManager.js";
|
|
40
39
|
import { TaskManager } from "./services/taskManager.js";
|
|
41
|
-
import { btw } from "./services/aiService.js";
|
|
42
|
-
import { convertMessagesForAPI } from "./utils/convertMessagesForAPI.js";
|
|
43
|
-
import { supportsVision } from "./utils/modelCapabilities.js";
|
|
44
40
|
import type { WorktreeSession } from "./utils/worktreeSession.js";
|
|
45
41
|
import { parseTaskNotificationXml } from "./utils/notificationXml.js";
|
|
46
42
|
import { InitializationService } from "./services/initializationService.js";
|
|
@@ -73,7 +69,6 @@ export class Agent {
|
|
|
73
69
|
private pluginManager: PluginManager; // Add plugin manager instance
|
|
74
70
|
private skillManager: SkillManager; // Add skill manager instance
|
|
75
71
|
private cronManager: CronManager; // Add cron manager instance
|
|
76
|
-
private goalManager: GoalManager; // Add goal manager instance
|
|
77
72
|
private hookManager: HookManager; // Add hooks manager instance
|
|
78
73
|
private reversionManager: ReversionManager;
|
|
79
74
|
private messageQueue: MessageQueue; // Unified queue for messages, bang commands, and notifications
|
|
@@ -208,7 +203,6 @@ export class Agent {
|
|
|
208
203
|
this.pluginManager = this.container.get("PluginManager")!;
|
|
209
204
|
this.bangManager = this.container.get("BangManager")!;
|
|
210
205
|
this.cronManager = this.container.get("CronManager")!;
|
|
211
|
-
this.goalManager = this.container.get("GoalManager")!;
|
|
212
206
|
this.messageQueue = this.container.get("MessageQueue")!;
|
|
213
207
|
|
|
214
208
|
// Wire up CWD change callback from AIManager to sync Agent's workdir
|
|
@@ -238,16 +232,6 @@ export class Agent {
|
|
|
238
232
|
if (options.permissionMode) {
|
|
239
233
|
this.setPermissionMode(options.permissionMode);
|
|
240
234
|
}
|
|
241
|
-
|
|
242
|
-
// Wire up goal state change callback
|
|
243
|
-
this.goalManager.setOnGoalStateChange((active, condition, elapsed) => {
|
|
244
|
-
this.options.callbacks?.onGoalStateChange?.(active, condition, elapsed);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Wire up goal evaluating callback
|
|
248
|
-
this.goalManager.setOnGoalEvaluating((evaluating) => {
|
|
249
|
-
this.options.callbacks?.onGoalEvaluating?.(evaluating);
|
|
250
|
-
});
|
|
251
235
|
}
|
|
252
236
|
|
|
253
237
|
// Public getter methods
|
|
@@ -340,16 +324,6 @@ export class Agent {
|
|
|
340
324
|
.filter((m) => m.type !== "notification");
|
|
341
325
|
}
|
|
342
326
|
|
|
343
|
-
/** Get goal status string */
|
|
344
|
-
public get goalStatus(): string {
|
|
345
|
-
return this.goalManager.getStatusString();
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/** Check if a goal is active */
|
|
349
|
-
public get isGoalActive(): boolean {
|
|
350
|
-
return this.goalManager.isGoalActive();
|
|
351
|
-
}
|
|
352
|
-
|
|
353
327
|
/**
|
|
354
328
|
* Remove a queued message by index
|
|
355
329
|
* @param index - The index of the message to remove
|
|
@@ -709,9 +683,6 @@ export class Agent {
|
|
|
709
683
|
public async clearMessages(): Promise<void> {
|
|
710
684
|
this.aiManager.abortAIMessage();
|
|
711
685
|
|
|
712
|
-
// Clear any active goal
|
|
713
|
-
this.goalManager.clearGoal();
|
|
714
|
-
|
|
715
686
|
// Capture old session info before clearing
|
|
716
687
|
const oldSessionId = this.messageManager.getSessionId();
|
|
717
688
|
const transcriptPath = this.messageManager.getTranscriptPath();
|
|
@@ -781,75 +752,6 @@ export class Agent {
|
|
|
781
752
|
await this.messageManager.saveSession();
|
|
782
753
|
}
|
|
783
754
|
|
|
784
|
-
/**
|
|
785
|
-
* Set an autonomous goal for the session
|
|
786
|
-
* @param condition - The goal condition to achieve
|
|
787
|
-
*/
|
|
788
|
-
public async setGoal(condition: string): Promise<void> {
|
|
789
|
-
// Check plan mode
|
|
790
|
-
if (this.getPermissionMode() === "plan") {
|
|
791
|
-
this.messageManager.addUserMessage({
|
|
792
|
-
content:
|
|
793
|
-
"<system-reminder>Cannot set a goal in plan mode. Exit plan mode first.</system-reminder>",
|
|
794
|
-
isMeta: true,
|
|
795
|
-
});
|
|
796
|
-
return;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
this.goalManager.setGoal(condition);
|
|
800
|
-
this.messageManager.addUserMessage({
|
|
801
|
-
content: `<system-reminder>Goal set: ${condition}. The agent will work autonomously until this goal is achieved.</system-reminder>`,
|
|
802
|
-
isMeta: true,
|
|
803
|
-
});
|
|
804
|
-
// Add the goal as a user directive to start working
|
|
805
|
-
this.messageManager.addUserMessage({
|
|
806
|
-
content: condition,
|
|
807
|
-
});
|
|
808
|
-
this.aiManager.sendAIMessage();
|
|
809
|
-
|
|
810
|
-
await this.messageManager.saveSession();
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
/**
|
|
814
|
-
* Clear the current autonomous goal
|
|
815
|
-
*/
|
|
816
|
-
public async clearGoal(): Promise<void> {
|
|
817
|
-
if (this.goalManager.isGoalActive()) {
|
|
818
|
-
this.goalManager.clearGoal();
|
|
819
|
-
this.messageManager.addUserMessage({
|
|
820
|
-
content: "<system-reminder>Goal cleared.</system-reminder>",
|
|
821
|
-
isMeta: true,
|
|
822
|
-
});
|
|
823
|
-
} else {
|
|
824
|
-
this.messageManager.addUserMessage({
|
|
825
|
-
content: "<system-reminder>No active goal to clear.</system-reminder>",
|
|
826
|
-
isMeta: true,
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
await this.messageManager.saveSession();
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* Show the current goal status
|
|
835
|
-
*/
|
|
836
|
-
public async showGoalStatus(): Promise<void> {
|
|
837
|
-
if (this.goalManager.isGoalActive()) {
|
|
838
|
-
this.messageManager.addUserMessage({
|
|
839
|
-
content: `<system-reminder>${this.goalManager.getStatusString()}</system-reminder>`,
|
|
840
|
-
isMeta: true,
|
|
841
|
-
});
|
|
842
|
-
} else {
|
|
843
|
-
this.messageManager.addUserMessage({
|
|
844
|
-
content:
|
|
845
|
-
"<system-reminder>No active goal. Use /goal <condition> to set one.</system-reminder>",
|
|
846
|
-
isMeta: true,
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
await this.messageManager.saveSession();
|
|
851
|
-
}
|
|
852
|
-
|
|
853
755
|
/** Unified interrupt method, interrupts both AI messages and command execution */
|
|
854
756
|
public abortMessage(): void {
|
|
855
757
|
// Guard: prevent tryDispatch (triggered by abortAIMessage → setIsLoading(false))
|
|
@@ -994,6 +896,21 @@ export class Agent {
|
|
|
994
896
|
this.subagentManager.cleanup();
|
|
995
897
|
// Cleanup forked agent manager
|
|
996
898
|
await this.forkedAgentManager.cleanup();
|
|
899
|
+
// Drain an in-flight auto-memory extraction fork so the process doesn't
|
|
900
|
+
// exit mid-extraction
|
|
901
|
+
try {
|
|
902
|
+
const autoMemoryService =
|
|
903
|
+
this.container.get<
|
|
904
|
+
import("./services/autoMemoryService.js").AutoMemoryService
|
|
905
|
+
>("AutoMemoryService");
|
|
906
|
+
if (autoMemoryService) {
|
|
907
|
+
await autoMemoryService.drain();
|
|
908
|
+
}
|
|
909
|
+
} catch (error) {
|
|
910
|
+
this.logger?.warn(
|
|
911
|
+
`Auto-memory extraction drain failed: ${(error as Error).message}`,
|
|
912
|
+
);
|
|
913
|
+
}
|
|
997
914
|
// Cleanup skill manager
|
|
998
915
|
await this.skillManager.destroy();
|
|
999
916
|
// Cleanup live configuration reload
|
|
@@ -1018,21 +935,30 @@ export class Agent {
|
|
|
1018
935
|
}
|
|
1019
936
|
|
|
1020
937
|
/**
|
|
1021
|
-
* Ask a side question without
|
|
938
|
+
* Ask a side question without interrupting the main agent.
|
|
939
|
+
*
|
|
940
|
+
* Runs a single-turn fork of the conversation that reuses the main loop's
|
|
941
|
+
* system prompt, tools, and memory injection (prompt-cache friendly) and
|
|
942
|
+
* never executes tools. The abort signal lets the UI cancel a pending
|
|
943
|
+
* question (loading dismiss); aborts propagate as thrown errors.
|
|
944
|
+
*
|
|
1022
945
|
* @param question - The side question to ask
|
|
946
|
+
* @param abortSignal - Optional signal to abort the pending request
|
|
1023
947
|
* @returns Promise that resolves to the AI's answer
|
|
1024
948
|
*/
|
|
1025
|
-
public async askBtw(
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
messages,
|
|
949
|
+
public async askBtw(
|
|
950
|
+
question: string,
|
|
951
|
+
abortSignal?: AbortSignal,
|
|
952
|
+
onContent?: (content: string) => void,
|
|
953
|
+
onReasoning?: (content: string) => void,
|
|
954
|
+
): Promise<string> {
|
|
955
|
+
const result = await this.aiManager.runBtwFork(
|
|
1033
956
|
question,
|
|
1034
|
-
|
|
1035
|
-
|
|
957
|
+
abortSignal,
|
|
958
|
+
onContent,
|
|
959
|
+
onReasoning,
|
|
960
|
+
);
|
|
961
|
+
return result.content ?? result.error ?? "No response received";
|
|
1036
962
|
}
|
|
1037
963
|
|
|
1038
964
|
/**
|
|
@@ -1066,7 +992,7 @@ export class Agent {
|
|
|
1066
992
|
images?: Array<{ path: string; mimeType: string }>,
|
|
1067
993
|
): Promise<void> {
|
|
1068
994
|
// If the agent is busy, enqueue the message — unless it's an immediate
|
|
1069
|
-
// slash command (e.g., /
|
|
995
|
+
// slash command (e.g., /clear, /compact) that should execute
|
|
1070
996
|
// right away even while AI is processing
|
|
1071
997
|
if (this.aiManager.isLoading || this.isCommandRunning) {
|
|
1072
998
|
const trimmed = content.trim();
|