wave-agent-sdk 0.19.9 → 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/builtin/plugins/sdd/scripts/session-start.js +1 -1
- package/builtin/plugins/sdd/skills/specify/SKILL.md +3 -4
- package/builtin/skills/settings/ENV.md +15 -9
- package/builtin/skills/settings/HOOKS.md +27 -2
- package/dist/agent.d.ts +9 -20
- package/dist/agent.js +28 -99
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/managers/aiManager.d.ts +71 -8
- package/dist/managers/aiManager.js +290 -85
- package/dist/managers/backgroundTaskManager.d.ts +6 -0
- package/dist/managers/backgroundTaskManager.js +11 -0
- package/dist/managers/bangManager.d.ts +6 -0
- package/dist/managers/bangManager.js +11 -0
- package/dist/managers/hookManager.d.ts +8 -2
- package/dist/managers/hookManager.js +14 -4
- package/dist/managers/mcpManager.d.ts +18 -4
- package/dist/managers/mcpManager.js +40 -18
- 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/managers/toolManager.js +5 -0
- 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.d.ts +21 -2
- package/dist/services/configurationService.js +86 -24
- package/dist/services/initializationService.js +14 -4
- package/dist/services/interactionService.js +35 -7
- package/dist/services/remoteSettingsService.d.ts +12 -0
- package/dist/services/remoteSettingsService.js +15 -1
- package/dist/services/session.d.ts +13 -0
- package/dist/services/session.js +64 -0
- package/dist/services/taskManager.js +7 -1
- package/dist/tools/bashTool.js +1 -0
- package/dist/tools/enterWorktreeTool.js +14 -3
- package/dist/tools/exitWorktreeTool.js +11 -10
- package/dist/tools/types.d.ts +7 -0
- package/dist/types/agent.d.ts +0 -2
- package/dist/types/config.d.ts +9 -0
- package/dist/types/core.d.ts +1 -1
- package/dist/types/hooks.d.ts +2 -2
- package/dist/utils/containerSetup.js +13 -4
- package/dist/utils/openaiClient.js +2 -1
- package/dist/utils/pathEncoder.js +7 -2
- package/dist/utils/worktreeUtils.d.ts +17 -0
- package/dist/utils/worktreeUtils.js +339 -1
- package/package.json +1 -1
- package/src/agent.ts +43 -112
- package/src/index.ts +1 -0
- package/src/managers/aiManager.ts +389 -110
- package/src/managers/backgroundTaskManager.ts +15 -0
- package/src/managers/bangManager.ts +15 -0
- package/src/managers/hookManager.ts +20 -5
- package/src/managers/mcpManager.ts +60 -18
- package/src/managers/messageManager.ts +51 -23
- package/src/managers/subagentManager.ts +36 -25
- package/src/managers/toolManager.ts +7 -0
- 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 +100 -24
- package/src/services/initializationService.ts +17 -4
- package/src/services/interactionService.ts +49 -6
- package/src/services/remoteSettingsService.ts +16 -1
- package/src/services/session.ts +68 -0
- package/src/services/taskManager.ts +10 -1
- package/src/tools/bashTool.ts +1 -0
- package/src/tools/enterWorktreeTool.ts +19 -2
- package/src/tools/exitWorktreeTool.ts +15 -12
- package/src/tools/types.ts +7 -0
- package/src/types/agent.ts +0 -6
- package/src/types/config.ts +9 -0
- package/src/types/core.ts +1 -1
- package/src/types/hooks.ts +2 -2
- package/src/utils/containerSetup.ts +15 -5
- package/src/utils/openaiClient.ts +2 -0
- package/src/utils/pathEncoder.ts +7 -2
- package/src/utils/worktreeUtils.ts +401 -1
- 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
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type HookExecutionResult,
|
|
14
14
|
type HookValidationResult,
|
|
15
15
|
type SessionEndSource,
|
|
16
|
+
type SessionStartSource,
|
|
16
17
|
HookConfigurationError,
|
|
17
18
|
isValidHookEvent,
|
|
18
19
|
isValidHookEventConfig,
|
|
@@ -26,6 +27,7 @@ import { executeCommand, isCommandSafe } from "../services/hook.js";
|
|
|
26
27
|
import { MessageSource } from "../types/index.js";
|
|
27
28
|
import type { MessageManager } from "./messageManager.js";
|
|
28
29
|
import { Container } from "../utils/container.js";
|
|
30
|
+
import type { ConfigurationService } from "../services/configurationService.js";
|
|
29
31
|
|
|
30
32
|
import { logger } from "../utils/globalLogger.js";
|
|
31
33
|
|
|
@@ -46,6 +48,19 @@ export class HookManager {
|
|
|
46
48
|
this.matcher = matcher;
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Merged env for this session (OS env overlaid with the per-session settings
|
|
53
|
+
* snapshot). Hook subprocesses spawn with this env so settings.json `env`
|
|
54
|
+
* vars reach hooks without polluting other sessions in one stdio process.
|
|
55
|
+
*/
|
|
56
|
+
private get sessionEnv(): Record<string, string> {
|
|
57
|
+
return (
|
|
58
|
+
this.container
|
|
59
|
+
.get<ConfigurationService>("ConfigurationService")
|
|
60
|
+
?.getMergedEnv?.() ?? (process.env as Record<string, string>)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
49
64
|
/**
|
|
50
65
|
* Load hook configuration from programmatic source (AgentOptions.hooks)
|
|
51
66
|
*/
|
|
@@ -916,7 +931,7 @@ export class HookManager {
|
|
|
916
931
|
* Collects additionalContext and initialUserMessage from hook stdout.
|
|
917
932
|
*/
|
|
918
933
|
async executeSessionStartHooks(
|
|
919
|
-
source:
|
|
934
|
+
source: SessionStartSource,
|
|
920
935
|
sessionId: string,
|
|
921
936
|
transcriptPath: string,
|
|
922
937
|
agentType?: string,
|
|
@@ -935,7 +950,7 @@ export class HookManager {
|
|
|
935
950
|
source,
|
|
936
951
|
agentType,
|
|
937
952
|
env: Object.fromEntries(
|
|
938
|
-
Object.entries(
|
|
953
|
+
Object.entries(this.sessionEnv).filter((e) => e[1] !== undefined),
|
|
939
954
|
) as Record<string, string>,
|
|
940
955
|
};
|
|
941
956
|
|
|
@@ -989,7 +1004,7 @@ export class HookManager {
|
|
|
989
1004
|
cwd: this.workdir,
|
|
990
1005
|
endSource: source,
|
|
991
1006
|
env: Object.fromEntries(
|
|
992
|
-
Object.entries(
|
|
1007
|
+
Object.entries(this.sessionEnv).filter((e) => e[1] !== undefined),
|
|
993
1008
|
) as Record<string, string>,
|
|
994
1009
|
};
|
|
995
1010
|
|
|
@@ -1024,7 +1039,7 @@ export class HookManager {
|
|
|
1024
1039
|
cwd: this.workdir,
|
|
1025
1040
|
compactInstructions: customInstructions,
|
|
1026
1041
|
env: Object.fromEntries(
|
|
1027
|
-
Object.entries(
|
|
1042
|
+
Object.entries(this.sessionEnv).filter((e) => e[1] !== undefined),
|
|
1028
1043
|
) as Record<string, string>,
|
|
1029
1044
|
};
|
|
1030
1045
|
|
|
@@ -1061,7 +1076,7 @@ export class HookManager {
|
|
|
1061
1076
|
cwd: this.workdir,
|
|
1062
1077
|
compactSummary,
|
|
1063
1078
|
env: Object.fromEntries(
|
|
1064
|
-
Object.entries(
|
|
1079
|
+
Object.entries(this.sessionEnv).filter((e) => e[1] !== undefined),
|
|
1065
1080
|
) as Record<string, string>,
|
|
1066
1081
|
};
|
|
1067
1082
|
|
|
@@ -9,6 +9,7 @@ import { ChatCompletionFunctionTool } from "openai/resources.js";
|
|
|
9
9
|
import { createMcpToolPlugin, findToolServer } from "../utils/mcpUtils.js";
|
|
10
10
|
import type { ToolPlugin, ToolResult, ToolContext } from "../tools/types.js";
|
|
11
11
|
import { Container } from "../utils/container.js";
|
|
12
|
+
import type { ConfigurationService } from "../services/configurationService.js";
|
|
12
13
|
import type {
|
|
13
14
|
Logger,
|
|
14
15
|
McpServerConfig,
|
|
@@ -42,7 +43,21 @@ export interface McpManagerOptions {
|
|
|
42
43
|
*/
|
|
43
44
|
const WAVE_TEMPLATE_VARS = ["WAVE_PLUGIN_ROOT", "CLAUDE_PLUGIN_ROOT"];
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Expand environment variables in a string value.
|
|
48
|
+
* Supports ${VAR} and ${VAR:-default} patterns.
|
|
49
|
+
*
|
|
50
|
+
* @param env - Merged env to resolve against (session snapshot over OS env).
|
|
51
|
+
* Defaults to `process.env` for backward compatibility with tests
|
|
52
|
+
* and standalone callers.
|
|
53
|
+
*/
|
|
54
|
+
export function expandEnvVars(
|
|
55
|
+
value: string,
|
|
56
|
+
env: Record<string, string | undefined> = process.env as Record<
|
|
57
|
+
string,
|
|
58
|
+
string | undefined
|
|
59
|
+
>,
|
|
60
|
+
): string {
|
|
46
61
|
return value.replace(/\$\{([^}]+)\}/g, (_match, expr: string) => {
|
|
47
62
|
const [varName, ...rest] = expr.split(":-");
|
|
48
63
|
const defaultValue = rest.join(":-");
|
|
@@ -50,45 +65,53 @@ export function expandEnvVars(value: string): string {
|
|
|
50
65
|
if (WAVE_TEMPLATE_VARS.includes(varName)) {
|
|
51
66
|
return _match; // return original ${...} string untouched
|
|
52
67
|
}
|
|
53
|
-
return process.env[varName] ?? defaultValue;
|
|
68
|
+
return env[varName] ?? process.env[varName] ?? defaultValue;
|
|
54
69
|
});
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
/**
|
|
58
73
|
* Walk an MCP config and resolve environment variables in all string fields.
|
|
59
|
-
*
|
|
60
|
-
* handled at spawn time
|
|
74
|
+
* Expands ${VAR} against the session env snapshot (over OS env); falls back to
|
|
75
|
+
* process.env. WAVE_PLUGIN_ROOT is skipped — handled at spawn time.
|
|
61
76
|
*/
|
|
62
|
-
export function resolveMcpConfig(
|
|
77
|
+
export function resolveMcpConfig(
|
|
78
|
+
config: McpConfig,
|
|
79
|
+
env: Record<string, string | undefined> = process.env as Record<
|
|
80
|
+
string,
|
|
81
|
+
string | undefined
|
|
82
|
+
>,
|
|
83
|
+
): McpConfig {
|
|
63
84
|
const resolved: McpConfig = { mcpServers: {} };
|
|
64
85
|
|
|
65
86
|
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
66
87
|
const resolvedServer: McpServerConfig = { ...serverConfig };
|
|
67
88
|
|
|
68
89
|
if (resolvedServer.command) {
|
|
69
|
-
resolvedServer.command = expandEnvVars(resolvedServer.command);
|
|
90
|
+
resolvedServer.command = expandEnvVars(resolvedServer.command, env);
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
if (resolvedServer.args) {
|
|
73
|
-
resolvedServer.args = resolvedServer.args.map(
|
|
94
|
+
resolvedServer.args = resolvedServer.args.map((a) =>
|
|
95
|
+
expandEnvVars(a, env),
|
|
96
|
+
);
|
|
74
97
|
}
|
|
75
98
|
|
|
76
99
|
if (resolvedServer.env) {
|
|
77
100
|
const resolvedEnv: Record<string, string> = {};
|
|
78
101
|
for (const [key, val] of Object.entries(resolvedServer.env)) {
|
|
79
|
-
resolvedEnv[key] = expandEnvVars(val);
|
|
102
|
+
resolvedEnv[key] = expandEnvVars(val, env);
|
|
80
103
|
}
|
|
81
104
|
resolvedServer.env = resolvedEnv;
|
|
82
105
|
}
|
|
83
106
|
|
|
84
107
|
if (resolvedServer.url) {
|
|
85
|
-
resolvedServer.url = expandEnvVars(resolvedServer.url);
|
|
108
|
+
resolvedServer.url = expandEnvVars(resolvedServer.url, env);
|
|
86
109
|
}
|
|
87
110
|
|
|
88
111
|
if (resolvedServer.headers) {
|
|
89
112
|
const resolvedHeaders: Record<string, string> = {};
|
|
90
113
|
for (const [key, val] of Object.entries(resolvedServer.headers)) {
|
|
91
|
-
resolvedHeaders[key] = expandEnvVars(val);
|
|
114
|
+
resolvedHeaders[key] = expandEnvVars(val, env);
|
|
92
115
|
}
|
|
93
116
|
resolvedServer.headers = resolvedHeaders;
|
|
94
117
|
}
|
|
@@ -119,6 +142,19 @@ export class McpManager {
|
|
|
119
142
|
this.mcpServers = options.mcpServers;
|
|
120
143
|
}
|
|
121
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Merged env for this session: OS env overlaid with the per-session settings
|
|
147
|
+
* snapshot. MCP env-var expansion (${VAR}) resolves against this so multiple
|
|
148
|
+
* sessions in one `wave --stdio` process don't read each other's settings env.
|
|
149
|
+
*/
|
|
150
|
+
private get envSnapshot(): Record<string, string> {
|
|
151
|
+
return (
|
|
152
|
+
this.container
|
|
153
|
+
.get<ConfigurationService>("ConfigurationService")
|
|
154
|
+
?.getMergedEnv?.() ?? (process.env as Record<string, string>)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
122
158
|
/**
|
|
123
159
|
* Initialize MCP manager with working directory and optionally auto-connect
|
|
124
160
|
*/
|
|
@@ -189,7 +225,7 @@ export class McpManager {
|
|
|
189
225
|
try {
|
|
190
226
|
const configContent = await fs.readFile(this.configPath, "utf-8");
|
|
191
227
|
const rawConfig: McpConfig = JSON.parse(configContent);
|
|
192
|
-
const workspaceConfig = resolveMcpConfig(rawConfig);
|
|
228
|
+
const workspaceConfig = resolveMcpConfig(rawConfig, this.envSnapshot);
|
|
193
229
|
|
|
194
230
|
// Extract original (pre-resolution) URLs for safe display
|
|
195
231
|
const originalUrls: Record<string, string | undefined> = {};
|
|
@@ -284,28 +320,31 @@ export class McpManager {
|
|
|
284
320
|
// Capture original URL before any resolution for safe display
|
|
285
321
|
const originalUrl = config.url;
|
|
286
322
|
|
|
287
|
-
// Expand env vars
|
|
323
|
+
// Expand env vars against the session snapshot (over OS env, e.g. ${TAVILY_API_KEY})
|
|
324
|
+
const env = this.envSnapshot;
|
|
288
325
|
const resolvedConfig: McpServerConfig = { ...config };
|
|
289
326
|
if (resolvedConfig.command) {
|
|
290
|
-
resolvedConfig.command = expandEnvVars(resolvedConfig.command);
|
|
327
|
+
resolvedConfig.command = expandEnvVars(resolvedConfig.command, env);
|
|
291
328
|
}
|
|
292
329
|
if (resolvedConfig.args) {
|
|
293
|
-
resolvedConfig.args = resolvedConfig.args.map(
|
|
330
|
+
resolvedConfig.args = resolvedConfig.args.map((a) =>
|
|
331
|
+
expandEnvVars(a, env),
|
|
332
|
+
);
|
|
294
333
|
}
|
|
295
334
|
if (resolvedConfig.env) {
|
|
296
335
|
const resolvedEnv: Record<string, string> = {};
|
|
297
336
|
for (const [key, val] of Object.entries(resolvedConfig.env)) {
|
|
298
|
-
resolvedEnv[key] = expandEnvVars(val);
|
|
337
|
+
resolvedEnv[key] = expandEnvVars(val, env);
|
|
299
338
|
}
|
|
300
339
|
resolvedConfig.env = resolvedEnv;
|
|
301
340
|
}
|
|
302
341
|
if (resolvedConfig.url) {
|
|
303
|
-
resolvedConfig.url = expandEnvVars(resolvedConfig.url);
|
|
342
|
+
resolvedConfig.url = expandEnvVars(resolvedConfig.url, env);
|
|
304
343
|
}
|
|
305
344
|
if (resolvedConfig.headers) {
|
|
306
345
|
const resolvedHeaders: Record<string, string> = {};
|
|
307
346
|
for (const [key, val] of Object.entries(resolvedConfig.headers)) {
|
|
308
|
-
resolvedHeaders[key] = expandEnvVars(val);
|
|
347
|
+
resolvedHeaders[key] = expandEnvVars(val, env);
|
|
309
348
|
}
|
|
310
349
|
resolvedConfig.headers = resolvedHeaders;
|
|
311
350
|
}
|
|
@@ -433,8 +472,11 @@ export class McpManager {
|
|
|
433
472
|
);
|
|
434
473
|
}
|
|
435
474
|
|
|
475
|
+
// Base env = OS env overlaid with this session's settings snapshot, so
|
|
476
|
+
// custom env vars from settings.json reach the MCP server subprocess
|
|
477
|
+
// without polluting other sessions sharing one `wave --stdio` process.
|
|
436
478
|
const env: Record<string, string> = {
|
|
437
|
-
...
|
|
479
|
+
...this.envSnapshot,
|
|
438
480
|
...(server.config.env || {}),
|
|
439
481
|
};
|
|
440
482
|
|
|
@@ -37,7 +37,6 @@ import { READ_TOOL_NAME } from "../constants/tools.js";
|
|
|
37
37
|
import { Container } from "../utils/container.js";
|
|
38
38
|
|
|
39
39
|
export interface MessageManagerCallbacks {
|
|
40
|
-
onMessagesChange?: (messages: Message[]) => void;
|
|
41
40
|
onSessionIdChange?: (sessionId: string) => void;
|
|
42
41
|
onLatestTotalTokensChange?: (latestTotalTokens: number) => void;
|
|
43
42
|
onUsagesChange?: (usages: Usage[]) => void;
|
|
@@ -64,9 +63,17 @@ export interface MessageManagerCallbacks {
|
|
|
64
63
|
onCompactBlockAdded?: (content: string) => void;
|
|
65
64
|
onCompactionStateChange?: (isCompacting: boolean) => void;
|
|
66
65
|
// Bang callback
|
|
67
|
-
onAddBangMessage?: (command: string) => void;
|
|
68
|
-
onUpdateBangMessage?: (
|
|
69
|
-
|
|
66
|
+
onAddBangMessage?: (command: string, messageId: string) => void;
|
|
67
|
+
onUpdateBangMessage?: (
|
|
68
|
+
command: string,
|
|
69
|
+
output: string,
|
|
70
|
+
messageId: string,
|
|
71
|
+
) => void;
|
|
72
|
+
onCompleteBangMessage?: (
|
|
73
|
+
command: string,
|
|
74
|
+
exitCode: number,
|
|
75
|
+
messageId: string,
|
|
76
|
+
) => void;
|
|
70
77
|
onInfoBlockAdded?: (content: string) => void;
|
|
71
78
|
// Rewind callbacks
|
|
72
79
|
onShowRewind?: () => void;
|
|
@@ -322,8 +329,6 @@ export class MessageManager {
|
|
|
322
329
|
this.extractFileReadsFromMessage(messages[messages.length - 1]);
|
|
323
330
|
this.extractSkillInvocationsFromMessage(messages[messages.length - 1]);
|
|
324
331
|
}
|
|
325
|
-
|
|
326
|
-
this.callbacks.onMessagesChange?.([...messages]);
|
|
327
332
|
}
|
|
328
333
|
|
|
329
334
|
/**
|
|
@@ -339,6 +344,20 @@ export class MessageManager {
|
|
|
339
344
|
return;
|
|
340
345
|
}
|
|
341
346
|
|
|
347
|
+
// CC-aligned lazy materialization: when the session file has not been
|
|
348
|
+
// materialized yet (no saved messages) and every unsaved message is a
|
|
349
|
+
// meta message (isMeta: true, e.g. SessionStart hook context), skip
|
|
350
|
+
// persistence. Persisting meta-only sessions would create "0 tokens /
|
|
351
|
+
// No content" ghost entries in the resume list. The meta messages stay
|
|
352
|
+
// in memory and are flushed together with the first real user/assistant
|
|
353
|
+
// message (matches CC's pendingEntries buffering).
|
|
354
|
+
if (
|
|
355
|
+
this.savedMessageCount === 0 &&
|
|
356
|
+
unsavedMessages.every((m) => m.isMeta)
|
|
357
|
+
) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
342
361
|
// Create session if needed (only when we have messages to save)
|
|
343
362
|
if (this.savedMessageCount === 0) {
|
|
344
363
|
// This is the first time saving messages, so create the session
|
|
@@ -622,7 +641,9 @@ export class MessageManager {
|
|
|
622
641
|
command,
|
|
623
642
|
});
|
|
624
643
|
this.setMessages(updatedMessages);
|
|
625
|
-
|
|
644
|
+
// The bang message is appended as the last message
|
|
645
|
+
const messageId = this.messages[this.messages.length - 1]?.id ?? "";
|
|
646
|
+
this.callbacks.onAddBangMessage?.(command, messageId);
|
|
626
647
|
}
|
|
627
648
|
|
|
628
649
|
public updateBangMessage(command: string, output: string): void {
|
|
@@ -632,7 +653,8 @@ export class MessageManager {
|
|
|
632
653
|
output,
|
|
633
654
|
});
|
|
634
655
|
this.setMessages(updatedMessages);
|
|
635
|
-
this.
|
|
656
|
+
const messageId = this.findBangMessageId(command) ?? "";
|
|
657
|
+
this.callbacks.onUpdateBangMessage?.(command, output, messageId);
|
|
636
658
|
}
|
|
637
659
|
|
|
638
660
|
public completeBangMessage(
|
|
@@ -647,7 +669,25 @@ export class MessageManager {
|
|
|
647
669
|
output,
|
|
648
670
|
});
|
|
649
671
|
this.setMessages(updatedMessages);
|
|
650
|
-
this.
|
|
672
|
+
const messageId = this.findBangMessageId(command) ?? "";
|
|
673
|
+
this.callbacks.onCompleteBangMessage?.(command, exitCode, messageId);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Find the message ID of the most recent message containing a bang block
|
|
678
|
+
* for the given command. Bang callbacks do not carry a block ID, so the
|
|
679
|
+
* message is located by matching the command against bang blocks.
|
|
680
|
+
*/
|
|
681
|
+
private findBangMessageId(command: string): string | undefined {
|
|
682
|
+
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
683
|
+
const message = this.messages[i];
|
|
684
|
+
if (message.role !== "user") continue;
|
|
685
|
+
const hasBangBlock = message.blocks?.some(
|
|
686
|
+
(block) => block.type === "bang" && block.command === command,
|
|
687
|
+
);
|
|
688
|
+
if (hasBangBlock) return message.id;
|
|
689
|
+
}
|
|
690
|
+
return undefined;
|
|
651
691
|
}
|
|
652
692
|
|
|
653
693
|
public addNotificationMessage(
|
|
@@ -700,7 +740,6 @@ export class MessageManager {
|
|
|
700
740
|
/**
|
|
701
741
|
* Finalize a streaming block of the given type by setting its stage to "end".
|
|
702
742
|
* Fires the corresponding incremental callback with chunk="" to signal finalization.
|
|
703
|
-
* Does NOT call onMessagesChange — the caller is responsible for that.
|
|
704
743
|
* Returns true if a block was finalized.
|
|
705
744
|
*/
|
|
706
745
|
private finalizeStreamingBlock(
|
|
@@ -807,8 +846,6 @@ export class MessageManager {
|
|
|
807
846
|
});
|
|
808
847
|
|
|
809
848
|
// Note: Subagent-specific callbacks are now handled by SubagentManager
|
|
810
|
-
|
|
811
|
-
this.callbacks.onMessagesChange?.([...this.messages]); // Still need to notify of changes
|
|
812
849
|
}
|
|
813
850
|
|
|
814
851
|
/**
|
|
@@ -871,8 +908,6 @@ export class MessageManager {
|
|
|
871
908
|
accumulated: newAccumulatedReasoning,
|
|
872
909
|
stage: "streaming",
|
|
873
910
|
});
|
|
874
|
-
|
|
875
|
-
this.callbacks.onMessagesChange?.([...this.messages]); // Still need to notify of changes
|
|
876
911
|
}
|
|
877
912
|
|
|
878
913
|
/**
|
|
@@ -885,15 +920,8 @@ export class MessageManager {
|
|
|
885
920
|
const lastMessage = this.messages[this.messages.length - 1];
|
|
886
921
|
if (lastMessage.role !== "assistant") return;
|
|
887
922
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
lastMessage,
|
|
891
|
-
"reasoning",
|
|
892
|
-
);
|
|
893
|
-
|
|
894
|
-
if (textFinalized || reasoningFinalized) {
|
|
895
|
-
this.callbacks.onMessagesChange?.([...this.messages]);
|
|
896
|
-
}
|
|
923
|
+
this.finalizeStreamingBlock(lastMessage, "text");
|
|
924
|
+
this.finalizeStreamingBlock(lastMessage, "reasoning");
|
|
897
925
|
}
|
|
898
926
|
|
|
899
927
|
/**
|
|
@@ -790,6 +790,7 @@ export class SubagentManager {
|
|
|
790
790
|
private createSubagentCallbacks(subagentId: string) {
|
|
791
791
|
return {
|
|
792
792
|
onUserMessageAdded: (params: UserMessageParams) => {
|
|
793
|
+
this.refreshSubagentState(subagentId);
|
|
793
794
|
// Forward user message events to parent via SubagentManager callbacks
|
|
794
795
|
if (this.callbacks?.onSubagentUserMessageAdded) {
|
|
795
796
|
this.callbacks.onSubagentUserMessageAdded(subagentId, params);
|
|
@@ -797,6 +798,7 @@ export class SubagentManager {
|
|
|
797
798
|
},
|
|
798
799
|
|
|
799
800
|
onAssistantMessageAdded: (messageId: string) => {
|
|
801
|
+
this.refreshSubagentState(subagentId);
|
|
800
802
|
// Forward assistant message events to parent via SubagentManager callbacks
|
|
801
803
|
if (this.callbacks?.onSubagentAssistantMessageAdded) {
|
|
802
804
|
this.callbacks.onSubagentAssistantMessageAdded(subagentId, messageId);
|
|
@@ -809,6 +811,7 @@ export class SubagentManager {
|
|
|
809
811
|
accumulated: string;
|
|
810
812
|
stage: "streaming" | "end";
|
|
811
813
|
}) => {
|
|
814
|
+
this.refreshSubagentState(subagentId);
|
|
812
815
|
// Forward assistant content updates to parent via SubagentManager callbacks
|
|
813
816
|
if (this.callbacks?.onSubagentAssistantContentUpdated) {
|
|
814
817
|
this.callbacks.onSubagentAssistantContentUpdated({
|
|
@@ -823,6 +826,7 @@ export class SubagentManager {
|
|
|
823
826
|
accumulated: string;
|
|
824
827
|
stage: "streaming" | "end";
|
|
825
828
|
}) => {
|
|
829
|
+
this.refreshSubagentState(subagentId);
|
|
826
830
|
// Forward assistant reasoning updates to parent via SubagentManager callbacks
|
|
827
831
|
if (this.callbacks?.onSubagentAssistantReasoningUpdated) {
|
|
828
832
|
this.callbacks.onSubagentAssistantReasoningUpdated({
|
|
@@ -833,6 +837,7 @@ export class SubagentManager {
|
|
|
833
837
|
},
|
|
834
838
|
|
|
835
839
|
onToolBlockUpdated: (params: ToolBlockUpdateCallbackParams) => {
|
|
840
|
+
this.refreshSubagentState(subagentId);
|
|
836
841
|
const instance = this.instances.get(subagentId);
|
|
837
842
|
if (instance) {
|
|
838
843
|
// Log tool execution to file only when finalized
|
|
@@ -852,31 +857,6 @@ export class SubagentManager {
|
|
|
852
857
|
}
|
|
853
858
|
},
|
|
854
859
|
|
|
855
|
-
// These callbacks will be handled by the parent agent
|
|
856
|
-
onMessagesChange: (messages: Message[]) => {
|
|
857
|
-
const instance = this.instances.get(subagentId);
|
|
858
|
-
if (instance) {
|
|
859
|
-
instance.messages = messages;
|
|
860
|
-
// Compute usedTools from messages (last 2 tool blocks)
|
|
861
|
-
const toolBlocks = messages.flatMap(
|
|
862
|
-
(m) => m.blocks?.filter((b) => b.type === "tool") ?? [],
|
|
863
|
-
);
|
|
864
|
-
const last2 = toolBlocks.slice(-2);
|
|
865
|
-
instance.usedTools = last2.map((tb) => ({
|
|
866
|
-
name: tb.name ?? "",
|
|
867
|
-
parameters: tb.parameters ?? "",
|
|
868
|
-
compactParams: tb.compactParams,
|
|
869
|
-
stage: tb.stage,
|
|
870
|
-
}));
|
|
871
|
-
// Trigger the onUpdate callback if provided
|
|
872
|
-
instance.onUpdate?.();
|
|
873
|
-
// Forward subagent message changes to parent via callbacks
|
|
874
|
-
if (this.callbacks?.onSubagentMessagesChange) {
|
|
875
|
-
this.callbacks.onSubagentMessagesChange(subagentId, messages);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
},
|
|
879
|
-
|
|
880
860
|
onLatestTotalTokensChange: (tokens: number) => {
|
|
881
861
|
const instance = this.instances.get(subagentId);
|
|
882
862
|
if (instance) {
|
|
@@ -890,6 +870,7 @@ export class SubagentManager {
|
|
|
890
870
|
},
|
|
891
871
|
|
|
892
872
|
onErrorBlockAdded: (error: string) => {
|
|
873
|
+
this.refreshSubagentState(subagentId);
|
|
893
874
|
const instance = this.instances.get(subagentId);
|
|
894
875
|
if (instance?.logStream) {
|
|
895
876
|
instance.logStream.write(
|
|
@@ -899,4 +880,34 @@ export class SubagentManager {
|
|
|
899
880
|
},
|
|
900
881
|
};
|
|
901
882
|
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Pull the latest messages from the subagent instance's MessageManager and
|
|
886
|
+
* refresh the instance's cached messages, usedTools, onUpdate and the
|
|
887
|
+
* onSubagentMessagesChange forwarding. Triggered by incremental callbacks.
|
|
888
|
+
*/
|
|
889
|
+
private refreshSubagentState(subagentId: string): void {
|
|
890
|
+
const instance = this.instances.get(subagentId);
|
|
891
|
+
if (!instance) return;
|
|
892
|
+
|
|
893
|
+
const messages = instance.messageManager.getMessages();
|
|
894
|
+
instance.messages = messages;
|
|
895
|
+
// Compute usedTools from messages (last 2 tool blocks)
|
|
896
|
+
const toolBlocks = messages.flatMap(
|
|
897
|
+
(m) => m.blocks?.filter((b) => b.type === "tool") ?? [],
|
|
898
|
+
);
|
|
899
|
+
const last2 = toolBlocks.slice(-2);
|
|
900
|
+
instance.usedTools = last2.map((tb) => ({
|
|
901
|
+
name: tb.name ?? "",
|
|
902
|
+
parameters: tb.parameters ?? "",
|
|
903
|
+
compactParams: tb.compactParams,
|
|
904
|
+
stage: tb.stage,
|
|
905
|
+
}));
|
|
906
|
+
// Trigger the onUpdate callback if provided
|
|
907
|
+
instance.onUpdate?.();
|
|
908
|
+
// Forward subagent message changes to parent via callbacks
|
|
909
|
+
if (this.callbacks?.onSubagentMessagesChange) {
|
|
910
|
+
this.callbacks.onSubagentMessagesChange(subagentId, messages);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
902
913
|
}
|
|
@@ -253,6 +253,13 @@ class ToolManager {
|
|
|
253
253
|
"WorkflowManager",
|
|
254
254
|
)
|
|
255
255
|
: undefined,
|
|
256
|
+
sessionEnv: this.container.has("ConfigurationService")
|
|
257
|
+
? this.container
|
|
258
|
+
.get<
|
|
259
|
+
import("../services/configurationService.js").ConfigurationService
|
|
260
|
+
>("ConfigurationService")
|
|
261
|
+
?.getMergedEnv?.()
|
|
262
|
+
: undefined,
|
|
256
263
|
sessionId: context.sessionId,
|
|
257
264
|
toolCallId: context.toolCallId,
|
|
258
265
|
};
|
package/src/prompts/index.ts
CHANGED
|
@@ -346,10 +346,6 @@ export function formatCompactSummary(summary: string): string {
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
export const WEB_CONTENT_SYSTEM_PROMPT = `You are a helpful assistant that extracts information from web content. The content is provided in Markdown format.`;
|
|
349
|
-
export const BTW_SYSTEM_PROMPT = `You are a helpful assistant. Answer the user's side question based on the conversation history.
|
|
350
|
-
Do NOT say things like "Let me try...", "I'll now...", "Let me check...", or promise to take any action.
|
|
351
|
-
If you don't know the answer, say so - do not offer to look it up or investigate.
|
|
352
|
-
Simply answer the question with the information you have.`;
|
|
353
349
|
|
|
354
350
|
export function buildSystemPrompt(
|
|
355
351
|
basePrompt: string | undefined,
|