wave-agent-sdk 0.17.6 → 0.17.7
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 +18 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +114 -1
- package/dist/managers/aiManager.d.ts +1 -0
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +20 -62
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +0 -4
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +7 -2
- package/dist/managers/planManager.d.ts +3 -0
- package/dist/managers/planManager.d.ts.map +1 -1
- package/dist/managers/planManager.js +9 -0
- package/dist/managers/slashCommandManager.d.ts +0 -6
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +0 -170
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +2 -5
- package/dist/prompts/planModeReminders.d.ts +0 -1
- package/dist/prompts/planModeReminders.d.ts.map +1 -1
- package/dist/prompts/planModeReminders.js +3 -12
- package/package.json +1 -1
- package/src/agent.ts +146 -1
- package/src/managers/aiManager.ts +28 -78
- package/src/managers/messageManager.ts +0 -5
- package/src/managers/permissionManager.ts +8 -1
- package/src/managers/planManager.ts +11 -0
- package/src/managers/slashCommandManager.ts +0 -215
- package/src/managers/toolManager.ts +2 -9
- package/src/prompts/planModeReminders.ts +3 -15
|
@@ -13,7 +13,6 @@ export class SlashCommandManager {
|
|
|
13
13
|
this.workdir = options.workdir;
|
|
14
14
|
}
|
|
15
15
|
initialize() {
|
|
16
|
-
this.initializeBuiltinCommands();
|
|
17
16
|
this.loadCustomCommands();
|
|
18
17
|
// Listen for skill refreshes and update skill commands
|
|
19
18
|
const skillManager = this.container.get("SkillManager");
|
|
@@ -29,181 +28,12 @@ export class SlashCommandManager {
|
|
|
29
28
|
get aiManager() {
|
|
30
29
|
return this.container.get("AIManager");
|
|
31
30
|
}
|
|
32
|
-
get backgroundTaskManager() {
|
|
33
|
-
return this.container.get("BackgroundTaskManager");
|
|
34
|
-
}
|
|
35
|
-
get taskManager() {
|
|
36
|
-
return this.container.get("TaskManager");
|
|
37
|
-
}
|
|
38
31
|
get skillManager() {
|
|
39
32
|
return this.container.get("SkillManager");
|
|
40
33
|
}
|
|
41
34
|
get subagentManager() {
|
|
42
35
|
return this.container.get("SubagentManager");
|
|
43
36
|
}
|
|
44
|
-
get memoryService() {
|
|
45
|
-
return this.container.get("MemoryService");
|
|
46
|
-
}
|
|
47
|
-
get hookManager() {
|
|
48
|
-
return this.container.get("HookManager");
|
|
49
|
-
}
|
|
50
|
-
get goalManager() {
|
|
51
|
-
return this.container.get("GoalManager");
|
|
52
|
-
}
|
|
53
|
-
initializeBuiltinCommands() {
|
|
54
|
-
// Register built-in clear command
|
|
55
|
-
this.registerCommand({
|
|
56
|
-
id: "clear",
|
|
57
|
-
name: "clear",
|
|
58
|
-
description: "Clear conversation history and reset session",
|
|
59
|
-
immediate: true,
|
|
60
|
-
handler: async () => {
|
|
61
|
-
this.aiManager.abortAIMessage();
|
|
62
|
-
// Clear any active goal
|
|
63
|
-
this.goalManager?.clearGoal();
|
|
64
|
-
// Capture old session info before clearing
|
|
65
|
-
const oldSessionId = this.messageManager.getSessionId();
|
|
66
|
-
const transcriptPath = this.messageManager.getTranscriptPath();
|
|
67
|
-
// Run SessionEnd hooks (cleanup before clear)
|
|
68
|
-
if (this.hookManager) {
|
|
69
|
-
try {
|
|
70
|
-
await this.hookManager.executeSessionEndHooks("clear", oldSessionId, transcriptPath);
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
logger?.warn(`SessionEnd hooks on clear failed: ${error.message}`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
// Clear messages and generate new session
|
|
77
|
-
this.messageManager.clearMessages();
|
|
78
|
-
this.memoryService.clearCache();
|
|
79
|
-
await this.taskManager.syncWithSession();
|
|
80
|
-
// Run SessionStart hooks (restore context for new session)
|
|
81
|
-
if (this.hookManager) {
|
|
82
|
-
try {
|
|
83
|
-
const newSessionId = this.messageManager.getSessionId();
|
|
84
|
-
const sessionStartResult = await this.hookManager.executeSessionStartHooks("clear", newSessionId, this.messageManager.getTranscriptPath());
|
|
85
|
-
// Inject additionalContext as a meta user message
|
|
86
|
-
if (sessionStartResult.additionalContext) {
|
|
87
|
-
this.messageManager.addUserMessage({
|
|
88
|
-
content: `<system-reminder>\nSessionStart hook additional context: ${sessionStartResult.additionalContext}\n</system-reminder>`,
|
|
89
|
-
isMeta: true,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
// Inject initialUserMessage as a meta user message
|
|
93
|
-
if (sessionStartResult.initialUserMessage) {
|
|
94
|
-
this.messageManager.addUserMessage({
|
|
95
|
-
content: sessionStartResult.initialUserMessage,
|
|
96
|
-
isMeta: true,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
logger?.warn(`SessionStart hooks on clear failed: ${error.message}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
// Register built-in compact command
|
|
107
|
-
this.registerCommand({
|
|
108
|
-
id: "compact",
|
|
109
|
-
name: "compact",
|
|
110
|
-
description: "Compact conversation history to reduce context usage",
|
|
111
|
-
immediate: true,
|
|
112
|
-
handler: async (args, signal) => {
|
|
113
|
-
this.aiManager.abortAIMessage();
|
|
114
|
-
const customInstructions = args?.trim() || undefined;
|
|
115
|
-
await this.aiManager.compactConversation({
|
|
116
|
-
customInstructions,
|
|
117
|
-
abortSignal: signal,
|
|
118
|
-
});
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
// Register built-in goal command
|
|
122
|
-
this.registerCommand({
|
|
123
|
-
id: "goal",
|
|
124
|
-
name: "goal",
|
|
125
|
-
description: "Set, check, or clear an autonomous goal for the session",
|
|
126
|
-
immediate: (args) => {
|
|
127
|
-
const trimmed = args?.trim() ?? "";
|
|
128
|
-
return (!trimmed ||
|
|
129
|
-
["clear", "stop", "off", "reset", "none", "cancel"].includes(trimmed));
|
|
130
|
-
},
|
|
131
|
-
handler: async (args) => {
|
|
132
|
-
const goalManager = this.goalManager;
|
|
133
|
-
if (!goalManager) {
|
|
134
|
-
this.messageManager.addUserMessage({
|
|
135
|
-
content: "Goal manager is not available",
|
|
136
|
-
isMeta: true,
|
|
137
|
-
});
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const trimmed = args?.trim() ?? "";
|
|
141
|
-
// Clear aliases
|
|
142
|
-
if (["clear", "stop", "off", "reset", "none", "cancel"].includes(trimmed)) {
|
|
143
|
-
if (goalManager.isGoalActive()) {
|
|
144
|
-
goalManager.clearGoal();
|
|
145
|
-
this.messageManager.addUserMessage({
|
|
146
|
-
content: "<system-reminder>Goal cleared.</system-reminder>",
|
|
147
|
-
isMeta: true,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
this.messageManager.addUserMessage({
|
|
152
|
-
content: "<system-reminder>No active goal to clear.</system-reminder>",
|
|
153
|
-
isMeta: true,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
// Show status
|
|
159
|
-
if (!trimmed) {
|
|
160
|
-
if (goalManager.isGoalActive()) {
|
|
161
|
-
this.messageManager.addUserMessage({
|
|
162
|
-
content: `<system-reminder>${goalManager.getStatusString()}</system-reminder>`,
|
|
163
|
-
isMeta: true,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
this.messageManager.addUserMessage({
|
|
168
|
-
content: "<system-reminder>No active goal. Use /goal <condition> to set one.</system-reminder>",
|
|
169
|
-
isMeta: true,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
// Check plan mode
|
|
175
|
-
const permissionMode = this.container.has("PermissionMode")
|
|
176
|
-
? this.container.get("PermissionMode")
|
|
177
|
-
: undefined;
|
|
178
|
-
if (permissionMode === "plan") {
|
|
179
|
-
this.messageManager.addUserMessage({
|
|
180
|
-
content: "<system-reminder>Cannot set a goal in plan mode. Exit plan mode first.</system-reminder>",
|
|
181
|
-
isMeta: true,
|
|
182
|
-
});
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
// Set goal
|
|
186
|
-
try {
|
|
187
|
-
goalManager.setGoal(trimmed);
|
|
188
|
-
this.messageManager.addUserMessage({
|
|
189
|
-
content: `<system-reminder>Goal set: ${trimmed}. The agent will work autonomously until this goal is achieved.</system-reminder>`,
|
|
190
|
-
isMeta: true,
|
|
191
|
-
});
|
|
192
|
-
// Add the goal as a user directive to start working
|
|
193
|
-
this.messageManager.addUserMessage({
|
|
194
|
-
content: trimmed,
|
|
195
|
-
});
|
|
196
|
-
this.aiManager.sendAIMessage();
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
this.messageManager.addUserMessage({
|
|
200
|
-
content: `<system-reminder>Failed to set goal: ${error.message}</system-reminder>`,
|
|
201
|
-
isMeta: true,
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
37
|
/**
|
|
208
38
|
* Load custom commands from filesystem
|
|
209
39
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolManager.d.ts","sourceRoot":"","sources":["../../src/managers/toolManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA6B7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,mBAAmB,CAAC;AAO3B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIlD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,wDAAwD;IACxD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED;;;;;GAKG;AACH,cAAM,WAAW;IACf,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,KAAK,CAAC,CAAW;IACzB,OAAO,CAAC,WAAW,CAAC,CAAe;IACnC,OAAO,CAAC,SAAS,CAAY;gBAEjB,OAAO,EAAE,kBAAkB;IAMvC,OAAO,KAAK,UAAU,GAErB;IAED;;OAEG;IACI,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAIvC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,sBAAsB,IAAI,IAAI;IA0CrC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;;;;;;;;;;;OAYG;IACG,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC;IAqJtB,IAAI,IAAI,UAAU,EAAE;IAYpB,cAAc,CAAC,OAAO,CAAC,EAAE;QACvB,kBAAkB,CAAC,EAAE,qBAAqB,EAAE,CAAC;QAC7C,eAAe,CAAC,EAAE,aAAa,EAAE,CAAC;QAClC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,GAAG,0BAA0B,EAAE;
|
|
1
|
+
{"version":3,"file":"toolManager.d.ts","sourceRoot":"","sources":["../../src/managers/toolManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA6B7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,mBAAmB,CAAC;AAO3B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIlD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,wDAAwD;IACxD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED;;;;;GAKG;AACH,cAAM,WAAW;IACf,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,KAAK,CAAC,CAAW;IACzB,OAAO,CAAC,WAAW,CAAC,CAAe;IACnC,OAAO,CAAC,SAAS,CAAY;gBAEjB,OAAO,EAAE,kBAAkB;IAMvC,OAAO,KAAK,UAAU,GAErB;IAED;;OAEG;IACI,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAIvC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,sBAAsB,IAAI,IAAI;IA0CrC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;;;;;;;;;;;OAYG;IACG,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC;IAqJtB,IAAI,IAAI,UAAU,EAAE;IAYpB,cAAc,CAAC,OAAO,CAAC,EAAE;QACvB,kBAAkB,CAAC,EAAE,qBAAqB,EAAE,CAAC;QAC7C,eAAe,CAAC,EAAE,aAAa,EAAE,CAAC;QAClC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,GAAG,0BAA0B,EAAE;IAgDhC;;OAEG;IACI,QAAQ,IAAI,UAAU,EAAE;IAI/B;;OAEG;IACI,iBAAiB,IAAI,cAAc;IAa1C;;;OAGG;IACI,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAIpD;;OAEG;IACI,oBAAoB,IAAI,iBAAiB,GAAG,SAAS;IAI5D;;OAEG;IACI,cAAc,IACjB,OAAO,4BAA4B,EAAE,WAAW,GAChD,SAAS;CAKd;AAGD,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -272,9 +272,7 @@ class ToolManager {
|
|
|
272
272
|
return false;
|
|
273
273
|
}
|
|
274
274
|
if (effectivePermissionMode === "bypassPermissions") {
|
|
275
|
-
if (tool.name === "ExitPlanMode"
|
|
276
|
-
tool.name === "AskUserQuestion" ||
|
|
277
|
-
tool.name === "EnterPlanMode") {
|
|
275
|
+
if (tool.name === "ExitPlanMode") {
|
|
278
276
|
return false;
|
|
279
277
|
}
|
|
280
278
|
}
|
|
@@ -282,8 +280,7 @@ class ToolManager {
|
|
|
282
280
|
return effectivePermissionMode === "plan";
|
|
283
281
|
}
|
|
284
282
|
if (tool.name === "EnterPlanMode") {
|
|
285
|
-
return
|
|
286
|
-
effectivePermissionMode !== "bypassPermissions");
|
|
283
|
+
return effectivePermissionMode !== "plan";
|
|
287
284
|
}
|
|
288
285
|
return true;
|
|
289
286
|
})
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export declare function wrapInSystemReminder(content: string): string;
|
|
2
2
|
export declare function buildPlanModeReminder(planFilePath: string, planExists: boolean, isSubagent?: boolean): string;
|
|
3
|
-
export declare function buildPlanModeSparseReminder(planFilePath: string): string;
|
|
4
3
|
export declare function buildPlanModeReEntryReminder(planFilePath: string): string;
|
|
5
4
|
export declare function buildExitedPlanModeReminder(planFilePath?: string, planExists?: boolean): string;
|
|
6
5
|
//# sourceMappingURL=planModeReminders.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planModeReminders.d.ts","sourceRoot":"","sources":["../../src/prompts/planModeReminders.ts"],"names":[],"mappings":"AAYA,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,OAAO,EACnB,UAAU,GAAE,OAAe,GAC1B,MAAM,CAsFR;AAED,wBAAgB,
|
|
1
|
+
{"version":3,"file":"planModeReminders.d.ts","sourceRoot":"","sources":["../../src/prompts/planModeReminders.ts"],"names":[],"mappings":"AAYA,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,OAAO,EACnB,UAAU,GAAE,OAAe,GAC1B,MAAM,CAsFR;AAED,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAQzE;AAED,wBAAgB,2BAA2B,CACzC,YAAY,CAAC,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,OAAO,GACnB,MAAM,CAIR"}
|
|
@@ -87,23 +87,14 @@ This is critical - your turn should only end with either using the ${ASK_USER_QU
|
|
|
87
87
|
|
|
88
88
|
NOTE: At any point in time through this workflow you should feel free to ask the user questions or clarifications using the ${ASK_USER_QUESTION_TOOL_NAME} tool. Don't make large assumptions about user intent. The goal is to present a well researched plan to the user, and tie any loose ends before implementation begins.`);
|
|
89
89
|
}
|
|
90
|
-
export function buildPlanModeSparseReminder(planFilePath) {
|
|
91
|
-
return wrapInSystemReminder(`Plan mode still active (see full instructions earlier in conversation). Read-only except plan file at ${planFilePath}. End turns with ${ASK_USER_QUESTION_TOOL_NAME} or ${EXIT_PLAN_MODE_TOOL_NAME}.`);
|
|
92
|
-
}
|
|
93
90
|
export function buildPlanModeReEntryReminder(planFilePath) {
|
|
94
91
|
return wrapInSystemReminder(`## Re-entering Plan Mode
|
|
95
92
|
|
|
96
|
-
You are returning to plan mode
|
|
93
|
+
You are returning to plan mode. A plan file exists at ${planFilePath} from your previous session.
|
|
97
94
|
|
|
98
|
-
**Before proceeding with any new planning, you should:**
|
|
99
95
|
1. Read the existing plan file to understand what was previously planned
|
|
100
|
-
2.
|
|
101
|
-
3.
|
|
102
|
-
- **Different task**: If the user's request is for a different task—even if it's similar or related—start fresh by overwriting the existing plan
|
|
103
|
-
- **Same task, continuing**: If this is explicitly a continuation or refinement of the exact same task, modify the existing plan while cleaning up outdated or irrelevant sections
|
|
104
|
-
4. Continue on with the plan process and most importantly you should always edit the plan file one way or the other before calling ${EXIT_PLAN_MODE_TOOL_NAME}
|
|
105
|
-
|
|
106
|
-
Treat this as a fresh planning session. Do not assume the existing plan is relevant without evaluating it first.`);
|
|
96
|
+
2. Decide: if the user's request is a different task, start fresh by overwriting the plan; if it's a continuation, modify the existing plan
|
|
97
|
+
3. Edit the plan file as needed, then call ${EXIT_PLAN_MODE_TOOL_NAME}`);
|
|
107
98
|
}
|
|
108
99
|
export function buildExitedPlanModeReminder(planFilePath, planExists) {
|
|
109
100
|
return wrapInSystemReminder(`## Exited Plan Mode
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -685,7 +685,152 @@ export class Agent {
|
|
|
685
685
|
}
|
|
686
686
|
|
|
687
687
|
public async clearMessages(): Promise<void> {
|
|
688
|
-
|
|
688
|
+
this.aiManager.abortAIMessage();
|
|
689
|
+
|
|
690
|
+
// Clear any active goal
|
|
691
|
+
this.goalManager.clearGoal();
|
|
692
|
+
|
|
693
|
+
// Capture old session info before clearing
|
|
694
|
+
const oldSessionId = this.messageManager.getSessionId();
|
|
695
|
+
const transcriptPath = this.messageManager.getTranscriptPath();
|
|
696
|
+
|
|
697
|
+
// Run SessionEnd hooks (cleanup before clear)
|
|
698
|
+
try {
|
|
699
|
+
await this.hookManager.executeSessionEndHooks(
|
|
700
|
+
"clear",
|
|
701
|
+
oldSessionId,
|
|
702
|
+
transcriptPath,
|
|
703
|
+
);
|
|
704
|
+
} catch (error) {
|
|
705
|
+
this.logger?.warn(
|
|
706
|
+
`SessionEnd hooks on clear failed: ${(error as Error).message}`,
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Clear messages and generate new session
|
|
711
|
+
this.messageManager.clearMessages();
|
|
712
|
+
const memoryService =
|
|
713
|
+
this.container.get<import("./services/memory.js").MemoryService>(
|
|
714
|
+
"MemoryService",
|
|
715
|
+
);
|
|
716
|
+
memoryService?.clearCache();
|
|
717
|
+
await this.taskManager.syncWithSession();
|
|
718
|
+
|
|
719
|
+
// Run SessionStart hooks (restore context for new session)
|
|
720
|
+
try {
|
|
721
|
+
const newSessionId = this.messageManager.getSessionId();
|
|
722
|
+
const sessionStartResult =
|
|
723
|
+
await this.hookManager.executeSessionStartHooks(
|
|
724
|
+
"clear",
|
|
725
|
+
newSessionId,
|
|
726
|
+
this.messageManager.getTranscriptPath(),
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
// Inject additionalContext as a meta user message
|
|
730
|
+
if (sessionStartResult.additionalContext) {
|
|
731
|
+
this.messageManager.addUserMessage({
|
|
732
|
+
content: `<system-reminder>\nSessionStart hook additional context: ${sessionStartResult.additionalContext}\n</system-reminder>`,
|
|
733
|
+
isMeta: true,
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Inject initialUserMessage as a meta user message
|
|
738
|
+
if (sessionStartResult.initialUserMessage) {
|
|
739
|
+
this.messageManager.addUserMessage({
|
|
740
|
+
content: sessionStartResult.initialUserMessage,
|
|
741
|
+
isMeta: true,
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
} catch (error) {
|
|
745
|
+
this.logger?.warn(
|
|
746
|
+
`SessionStart hooks on clear failed: ${(error as Error).message}`,
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
await this.messageManager.saveSession();
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Compact conversation history to reduce context usage
|
|
755
|
+
* @param customInstructions - Optional custom instructions for compaction
|
|
756
|
+
*/
|
|
757
|
+
public async compact(customInstructions?: string): Promise<void> {
|
|
758
|
+
this.aiManager.abortAIMessage();
|
|
759
|
+
|
|
760
|
+
await this.aiManager.compactConversation({
|
|
761
|
+
customInstructions,
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
await this.messageManager.saveSession();
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Set an autonomous goal for the session
|
|
769
|
+
* @param condition - The goal condition to achieve
|
|
770
|
+
*/
|
|
771
|
+
public async setGoal(condition: string): Promise<void> {
|
|
772
|
+
// Check plan mode
|
|
773
|
+
if (this.getPermissionMode() === "plan") {
|
|
774
|
+
this.messageManager.addUserMessage({
|
|
775
|
+
content:
|
|
776
|
+
"<system-reminder>Cannot set a goal in plan mode. Exit plan mode first.</system-reminder>",
|
|
777
|
+
isMeta: true,
|
|
778
|
+
});
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
this.goalManager.setGoal(condition);
|
|
783
|
+
this.messageManager.addUserMessage({
|
|
784
|
+
content: `<system-reminder>Goal set: ${condition}. The agent will work autonomously until this goal is achieved.</system-reminder>`,
|
|
785
|
+
isMeta: true,
|
|
786
|
+
});
|
|
787
|
+
// Add the goal as a user directive to start working
|
|
788
|
+
this.messageManager.addUserMessage({
|
|
789
|
+
content: condition,
|
|
790
|
+
});
|
|
791
|
+
this.aiManager.sendAIMessage();
|
|
792
|
+
|
|
793
|
+
await this.messageManager.saveSession();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Clear the current autonomous goal
|
|
798
|
+
*/
|
|
799
|
+
public async clearGoal(): Promise<void> {
|
|
800
|
+
if (this.goalManager.isGoalActive()) {
|
|
801
|
+
this.goalManager.clearGoal();
|
|
802
|
+
this.messageManager.addUserMessage({
|
|
803
|
+
content: "<system-reminder>Goal cleared.</system-reminder>",
|
|
804
|
+
isMeta: true,
|
|
805
|
+
});
|
|
806
|
+
} else {
|
|
807
|
+
this.messageManager.addUserMessage({
|
|
808
|
+
content: "<system-reminder>No active goal to clear.</system-reminder>",
|
|
809
|
+
isMeta: true,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
await this.messageManager.saveSession();
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Show the current goal status
|
|
818
|
+
*/
|
|
819
|
+
public async showGoalStatus(): Promise<void> {
|
|
820
|
+
if (this.goalManager.isGoalActive()) {
|
|
821
|
+
this.messageManager.addUserMessage({
|
|
822
|
+
content: `<system-reminder>${this.goalManager.getStatusString()}</system-reminder>`,
|
|
823
|
+
isMeta: true,
|
|
824
|
+
});
|
|
825
|
+
} else {
|
|
826
|
+
this.messageManager.addUserMessage({
|
|
827
|
+
content:
|
|
828
|
+
"<system-reminder>No active goal. Use /goal <condition> to set one.</system-reminder>",
|
|
829
|
+
isMeta: true,
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
await this.messageManager.saveSession();
|
|
689
834
|
}
|
|
690
835
|
|
|
691
836
|
/** Unified interrupt method, interrupts both AI messages and command execution */
|
|
@@ -26,7 +26,6 @@ import type { SkillManager } from "./skillManager.js";
|
|
|
26
26
|
import { buildSystemPrompt } from "../prompts/index.js";
|
|
27
27
|
import {
|
|
28
28
|
buildPlanModeReminder,
|
|
29
|
-
buildPlanModeSparseReminder,
|
|
30
29
|
buildPlanModeReEntryReminder,
|
|
31
30
|
buildExitedPlanModeReminder,
|
|
32
31
|
} from "../prompts/planModeReminders.js";
|
|
@@ -138,6 +137,14 @@ export class AIManager {
|
|
|
138
137
|
return this.container.get<PermissionManager>("PermissionManager");
|
|
139
138
|
}
|
|
140
139
|
|
|
140
|
+
private get planManager():
|
|
141
|
+
| import("./planManager.js").PlanManager
|
|
142
|
+
| undefined {
|
|
143
|
+
return this.container.get<import("./planManager.js").PlanManager>(
|
|
144
|
+
"PlanManager",
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
141
148
|
private get configurationService(): ConfigurationService {
|
|
142
149
|
return this.container.get<ConfigurationService>("ConfigurationService")!;
|
|
143
150
|
}
|
|
@@ -253,7 +260,6 @@ export class AIManager {
|
|
|
253
260
|
this.permissionManager.setNeedsPlanModeExitAttachment(false);
|
|
254
261
|
}
|
|
255
262
|
|
|
256
|
-
// Handle plan mode reminders
|
|
257
263
|
if (currentMode !== "plan") return messages;
|
|
258
264
|
|
|
259
265
|
const planFilePath = this.permissionManager.getPlanFilePath();
|
|
@@ -261,83 +267,27 @@ export class AIManager {
|
|
|
261
267
|
|
|
262
268
|
const planExists = existsSync(planFilePath);
|
|
263
269
|
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// Count human turns (non-meta user messages without tool results)
|
|
284
|
-
const hasToolResult = msg.blocks?.some(
|
|
285
|
-
(b: { type: string }) => b.type === "tool",
|
|
286
|
-
);
|
|
287
|
-
if (!hasToolResult) {
|
|
288
|
-
if (!foundLastReminder) {
|
|
289
|
-
humanTurnsSinceLastReminder++;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
// Check for existing plan mode system-reminders
|
|
294
|
-
if (msg.role === "user" && msg.isMeta) {
|
|
295
|
-
const textContent = msg.blocks
|
|
296
|
-
?.filter((b) => b.type === "text")
|
|
297
|
-
.map((b) => ("content" in b ? b.content : ""))
|
|
298
|
-
.join("");
|
|
299
|
-
if (
|
|
300
|
-
textContent?.includes("Plan mode is active") ||
|
|
301
|
-
textContent?.includes("Plan mode still active")
|
|
302
|
-
) {
|
|
303
|
-
planModeReminderCount++;
|
|
304
|
-
if (!foundLastReminder) {
|
|
305
|
-
foundLastReminder = true;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
270
|
+
// One-time plan entry reminder
|
|
271
|
+
const planMgr = this.planManager;
|
|
272
|
+
if (planMgr?.isPlanEntryReminderPending()) {
|
|
273
|
+
if (this.permissionManager.hasExitedPlanModeInSession() && planExists) {
|
|
274
|
+
// Re-entry: use small reminder
|
|
275
|
+
messages.push({
|
|
276
|
+
role: "user",
|
|
277
|
+
content: buildPlanModeReEntryReminder(planFilePath),
|
|
278
|
+
});
|
|
279
|
+
} else {
|
|
280
|
+
// First entry: use full reminder
|
|
281
|
+
messages.push({
|
|
282
|
+
role: "user",
|
|
283
|
+
content: buildPlanModeReminder(
|
|
284
|
+
planFilePath,
|
|
285
|
+
planExists,
|
|
286
|
+
!!this.subagentType,
|
|
287
|
+
),
|
|
288
|
+
});
|
|
308
289
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
// Throttle: only inject every 5 human turns (but always inject on first turn)
|
|
312
|
-
const TURNS_BETWEEN_REMINDERS = 5;
|
|
313
|
-
const FULL_REMINDER_EVERY_N = 5;
|
|
314
|
-
|
|
315
|
-
if (
|
|
316
|
-
foundLastReminder &&
|
|
317
|
-
humanTurnsSinceLastReminder < TURNS_BETWEEN_REMINDERS
|
|
318
|
-
) {
|
|
319
|
-
return messages; // Throttled — skip reminder
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Determine full vs sparse
|
|
323
|
-
// Every 5th reminder is full; rest are sparse
|
|
324
|
-
const reminderNumber = planModeReminderCount + 1;
|
|
325
|
-
const isFull = reminderNumber % FULL_REMINDER_EVERY_N === 1;
|
|
326
|
-
|
|
327
|
-
if (isFull) {
|
|
328
|
-
messages.push({
|
|
329
|
-
role: "user",
|
|
330
|
-
content: buildPlanModeReminder(
|
|
331
|
-
planFilePath,
|
|
332
|
-
planExists,
|
|
333
|
-
!!this.subagentType,
|
|
334
|
-
),
|
|
335
|
-
});
|
|
336
|
-
} else {
|
|
337
|
-
messages.push({
|
|
338
|
-
role: "user",
|
|
339
|
-
content: buildPlanModeSparseReminder(planFilePath),
|
|
340
|
-
});
|
|
290
|
+
planMgr.consumePlanEntryReminder();
|
|
341
291
|
}
|
|
342
292
|
|
|
343
293
|
return messages;
|
|
@@ -548,11 +548,6 @@ export class MessageManager {
|
|
|
548
548
|
this.setSessionId(generateSessionId());
|
|
549
549
|
this.parentSessionId = oldSessionId;
|
|
550
550
|
|
|
551
|
-
// Trigger task list update if this is the main session to ensure continuity
|
|
552
|
-
if (this.sessionType === "main") {
|
|
553
|
-
this.callbacks.onSessionIdChange?.(this.sessionId);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
551
|
// Set new message list
|
|
557
552
|
this.setMessages(newMessages);
|
|
558
553
|
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
EDIT_TOOL_NAME,
|
|
32
32
|
WRITE_TOOL_NAME,
|
|
33
33
|
READ_TOOL_NAME,
|
|
34
|
+
ASK_USER_QUESTION_TOOL_NAME,
|
|
34
35
|
} from "../constants/tools.js";
|
|
35
36
|
import { Container } from "../utils/container.js";
|
|
36
37
|
import { ConfigurationService } from "../services/configurationService.js";
|
|
@@ -440,8 +441,14 @@ export class PermissionManager {
|
|
|
440
441
|
}
|
|
441
442
|
|
|
442
443
|
// 1. If bypassPermissions mode, always allow
|
|
444
|
+
// Exception: tools that require user interaction (e.g. AskUserQuestion)
|
|
445
|
+
// must still prompt the user, matching Claude Code's requiresUserInteraction behavior.
|
|
443
446
|
if (context.permissionMode === "bypassPermissions") {
|
|
444
|
-
|
|
447
|
+
const requiresUserInteraction =
|
|
448
|
+
context.toolName === ASK_USER_QUESTION_TOOL_NAME;
|
|
449
|
+
if (!requiresUserInteraction) {
|
|
450
|
+
return { behavior: "allow" };
|
|
451
|
+
}
|
|
445
452
|
}
|
|
446
453
|
|
|
447
454
|
// 1.0 Check worktree safety for Write and Edit tools
|
|
@@ -16,6 +16,7 @@ import { logger } from "../utils/globalLogger.js";
|
|
|
16
16
|
export class PlanManager {
|
|
17
17
|
private planDir: string;
|
|
18
18
|
private currentPlanFilePath: string | null = null;
|
|
19
|
+
private planEntryReminderPending: boolean = false;
|
|
19
20
|
|
|
20
21
|
constructor(private container: Container) {
|
|
21
22
|
this.planDir = path.join(os.homedir(), ".wave", "plans");
|
|
@@ -80,6 +81,7 @@ export class PlanManager {
|
|
|
80
81
|
// Entering plan mode: clear any pending exit attachment
|
|
81
82
|
// (prevents sending both plan_mode and plan_mode_exit on rapid toggle)
|
|
82
83
|
permissionManager?.setNeedsPlanModeExitAttachment(false);
|
|
84
|
+
this.planEntryReminderPending = true;
|
|
83
85
|
|
|
84
86
|
this.getOrGeneratePlanFilePath(messageManager?.getRootSessionId())
|
|
85
87
|
.then(({ path }) => {
|
|
@@ -94,8 +96,17 @@ export class PlanManager {
|
|
|
94
96
|
permissionManager?.setHasExitedPlanMode(true);
|
|
95
97
|
permissionManager?.setNeedsPlanModeExitAttachment(true);
|
|
96
98
|
permissionManager?.setPlanFilePath(undefined);
|
|
99
|
+
this.planEntryReminderPending = false;
|
|
97
100
|
} else {
|
|
98
101
|
permissionManager?.setPlanFilePath(undefined);
|
|
99
102
|
}
|
|
100
103
|
}
|
|
104
|
+
|
|
105
|
+
public isPlanEntryReminderPending(): boolean {
|
|
106
|
+
return this.planEntryReminderPending;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public consumePlanEntryReminder(): void {
|
|
110
|
+
this.planEntryReminderPending = false;
|
|
111
|
+
}
|
|
101
112
|
}
|