wave-agent-sdk 1.0.5 → 1.0.6
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 +23 -1
- package/dist/agent.js +34 -4
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -8
- package/dist/managers/messageManager.d.ts +1 -3
- package/dist/managers/messageManager.js +13 -6
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/index.d.ts +3 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +2 -1
- package/dist/utils/messageOperations.js +2 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1184
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -917
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -659
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
package/dist/agent.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ export declare class Agent {
|
|
|
14
14
|
private permissionManager;
|
|
15
15
|
private planManager;
|
|
16
16
|
private subagentManager;
|
|
17
|
-
private forkedAgentManager;
|
|
18
17
|
private slashCommandManager;
|
|
19
18
|
private pluginManager;
|
|
20
19
|
private skillManager;
|
|
@@ -24,6 +23,7 @@ export declare class Agent {
|
|
|
24
23
|
private messageQueue;
|
|
25
24
|
private dispatchPromise;
|
|
26
25
|
private isAborting;
|
|
26
|
+
private dispatchAborted;
|
|
27
27
|
private memoryRuleManager;
|
|
28
28
|
private liveConfigManager;
|
|
29
29
|
private taskManager;
|
|
@@ -269,6 +269,28 @@ export declare class Agent {
|
|
|
269
269
|
* @returns Promise that resolves to the AI's answer
|
|
270
270
|
*/
|
|
271
271
|
askBtw(question: string, abortSignal?: AbortSignal, onContent?: (content: string) => void, onReasoning?: (content: string) => void): Promise<string>;
|
|
272
|
+
/**
|
|
273
|
+
* Start a fork subagent in the background (the "/subtask" command path).
|
|
274
|
+
*
|
|
275
|
+
* The fork inherits the parent's full conversation context — same system
|
|
276
|
+
* prompt, tools, model, and message prefix — so the prompt cache is reused,
|
|
277
|
+
* then works independently in the background. On completion its final
|
|
278
|
+
* response is delivered back to the main conversation as a task notification
|
|
279
|
+
* carrying the `<result>` tag. Recursion is prevented inside the fork (the
|
|
280
|
+
* Agent tool and Task tools are denied).
|
|
281
|
+
*
|
|
282
|
+
* @param prompt - The task description for the fork subagent
|
|
283
|
+
* @param options - Background task label and optional loose turn bound
|
|
284
|
+
* @param options.description - Display label for the background task
|
|
285
|
+
* @param options.maxTurns - Loose turn bound (defaults to 200, aligned with
|
|
286
|
+
* Claude Code's fork subagent)
|
|
287
|
+
* @param abortSignal - Optional signal to abort the fork
|
|
288
|
+
* @returns Promise that resolves to the background task ID
|
|
289
|
+
*/
|
|
290
|
+
forkSubagent(prompt: string, options: {
|
|
291
|
+
description: string;
|
|
292
|
+
maxTurns?: number;
|
|
293
|
+
}, abortSignal?: AbortSignal): Promise<string>;
|
|
272
294
|
/**
|
|
273
295
|
* Send a message to the AI agent with optional images
|
|
274
296
|
*
|
package/dist/agent.js
CHANGED
|
@@ -54,6 +54,7 @@ export class Agent {
|
|
|
54
54
|
this.bangManager = null;
|
|
55
55
|
this.dispatchPromise = null; // Track current dispatch for teardown
|
|
56
56
|
this.isAborting = false; // Guard: prevents tryDispatch from firing during abortMessage
|
|
57
|
+
this.dispatchAborted = false; // Set on abort while a dispatch is running: suppress the .finally re-check so preserved notifications don't get dispatched after abort
|
|
57
58
|
this.sessionStartTime = Date.now();
|
|
58
59
|
const { logger, workdir, systemPrompt, stream = true } = options;
|
|
59
60
|
// Set working directory early as we need it for loading configuration
|
|
@@ -106,7 +107,6 @@ export class Agent {
|
|
|
106
107
|
this.toolManager = this.container.get("ToolManager");
|
|
107
108
|
this.liveConfigManager = this.container.get("LiveConfigManager");
|
|
108
109
|
this.subagentManager = this.container.get("SubagentManager");
|
|
109
|
-
this.forkedAgentManager = this.container.get("ForkedAgentManager");
|
|
110
110
|
this.aiManager = this.container.get("AIManager");
|
|
111
111
|
this.slashCommandManager = this.container.get("SlashCommandManager");
|
|
112
112
|
this.pluginManager = this.container.get("PluginManager");
|
|
@@ -265,6 +265,8 @@ export class Agent {
|
|
|
265
265
|
tryDispatch() {
|
|
266
266
|
if (this.isAborting)
|
|
267
267
|
return; // Suppress dispatch during abort to prevent queued notifications from being dispatched as a side-effect
|
|
268
|
+
if (this.dispatchAborted)
|
|
269
|
+
return; // Aborted mid-dispatch: don't start a new dispatch (the user interrupted this turn)
|
|
268
270
|
if (this.messageQueue.state !== "idle")
|
|
269
271
|
return;
|
|
270
272
|
if (!this.messageQueue.hasPending())
|
|
@@ -279,7 +281,10 @@ export class Agent {
|
|
|
279
281
|
.finally(() => {
|
|
280
282
|
this.messageQueue.transitionTo("idle");
|
|
281
283
|
this.dispatchPromise = null;
|
|
282
|
-
this.
|
|
284
|
+
if (!this.dispatchAborted) {
|
|
285
|
+
this.tryDispatch(); // Re-check after processing
|
|
286
|
+
}
|
|
287
|
+
this.dispatchAborted = false;
|
|
283
288
|
});
|
|
284
289
|
}
|
|
285
290
|
/**
|
|
@@ -571,6 +576,12 @@ export class Agent {
|
|
|
571
576
|
// from dispatching preserved notifications as a new AI turn during the abort.
|
|
572
577
|
this.isAborting = true;
|
|
573
578
|
try {
|
|
579
|
+
// If a queued dispatch is in flight (e.g. a notification turn was being
|
|
580
|
+
// processed), remember the abort so the .finally re-check won't start a
|
|
581
|
+
// new dispatch with the preserved notifications right after aborting.
|
|
582
|
+
if (this.dispatchPromise) {
|
|
583
|
+
this.dispatchAborted = true;
|
|
584
|
+
}
|
|
574
585
|
if (this.aiManager.isLoading || this.isCommandRunning) {
|
|
575
586
|
// Clear user-facing queue items first to prevent processQueuedMessage
|
|
576
587
|
// from dequeuing when abortAIMessage triggers onLoadingChange(false).
|
|
@@ -684,8 +695,6 @@ export class Agent {
|
|
|
684
695
|
}
|
|
685
696
|
// Cleanup subagent manager
|
|
686
697
|
this.subagentManager.cleanup();
|
|
687
|
-
// Cleanup forked agent manager
|
|
688
|
-
await this.forkedAgentManager.cleanup();
|
|
689
698
|
// Drain an in-flight auto-memory extraction fork so the process doesn't
|
|
690
699
|
// exit mid-extraction
|
|
691
700
|
try {
|
|
@@ -732,6 +741,27 @@ export class Agent {
|
|
|
732
741
|
const result = await this.aiManager.runBtwFork(question, abortSignal, onContent, onReasoning);
|
|
733
742
|
return result.content ?? result.error ?? "No response received";
|
|
734
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* Start a fork subagent in the background (the "/subtask" command path).
|
|
746
|
+
*
|
|
747
|
+
* The fork inherits the parent's full conversation context — same system
|
|
748
|
+
* prompt, tools, model, and message prefix — so the prompt cache is reused,
|
|
749
|
+
* then works independently in the background. On completion its final
|
|
750
|
+
* response is delivered back to the main conversation as a task notification
|
|
751
|
+
* carrying the `<result>` tag. Recursion is prevented inside the fork (the
|
|
752
|
+
* Agent tool and Task tools are denied).
|
|
753
|
+
*
|
|
754
|
+
* @param prompt - The task description for the fork subagent
|
|
755
|
+
* @param options - Background task label and optional loose turn bound
|
|
756
|
+
* @param options.description - Display label for the background task
|
|
757
|
+
* @param options.maxTurns - Loose turn bound (defaults to 200, aligned with
|
|
758
|
+
* Claude Code's fork subagent)
|
|
759
|
+
* @param abortSignal - Optional signal to abort the fork
|
|
760
|
+
* @returns Promise that resolves to the background task ID
|
|
761
|
+
*/
|
|
762
|
+
async forkSubagent(prompt, options, abortSignal) {
|
|
763
|
+
return this.aiManager.runForkSubagent(prompt, options, abortSignal);
|
|
764
|
+
}
|
|
735
765
|
/**
|
|
736
766
|
* Send a message to the AI agent with optional images
|
|
737
767
|
*
|
|
@@ -40,6 +40,8 @@ export declare class AIManager {
|
|
|
40
40
|
private modelOverride?;
|
|
41
41
|
private _onCwdChange?;
|
|
42
42
|
private originalWorkdir;
|
|
43
|
+
/** Pending cwd-recovery notice (set when getWorkdir() auto-recovers), consumed by tools */
|
|
44
|
+
private pendingCwdRecovery;
|
|
43
45
|
private consecutiveCompactionFailures;
|
|
44
46
|
private readonly maxTurns?;
|
|
45
47
|
/** Tracks file mtime/hash at read time for staleness detection on Edit/Write */
|
|
@@ -77,6 +79,16 @@ export declare class AIManager {
|
|
|
77
79
|
getAutoMemoryEnabled(): boolean;
|
|
78
80
|
getWorktreeBaseRef(): "fresh" | "head";
|
|
79
81
|
getWorkdir(): string;
|
|
82
|
+
/**
|
|
83
|
+
* Consume a pending cwd-recovery notice (if any). After the session
|
|
84
|
+
* workdir was auto-recovered because the previous directory disappeared,
|
|
85
|
+
* the first tool that calls this surfaces the notice to the model.
|
|
86
|
+
* Returns null when no recovery is pending.
|
|
87
|
+
*/
|
|
88
|
+
consumeCwdRecovery(): {
|
|
89
|
+
from: string;
|
|
90
|
+
to: string;
|
|
91
|
+
} | null;
|
|
80
92
|
getOriginalWorkdir(): string;
|
|
81
93
|
/**
|
|
82
94
|
* Update the working directory mid-session (e.g., when entering/exiting a worktree).
|
|
@@ -155,6 +167,30 @@ export declare class AIManager {
|
|
|
155
167
|
deniedToolMessage?: string;
|
|
156
168
|
maxTurns?: number;
|
|
157
169
|
}, abortSignal?: AbortSignal): Promise<ForkLoopResult>;
|
|
170
|
+
/**
|
|
171
|
+
* Fork subagent ("/subtask"): spawn a background agent that inherits the
|
|
172
|
+
* parent's full conversation context — the same system prompt, tools, model,
|
|
173
|
+
* and message prefix as the main loop — so the forked request prefix matches
|
|
174
|
+
* exactly and the prompt cache is reused. The fork runs to completion in the
|
|
175
|
+
* background via the shared BackgroundTaskManager (visible in /tasks, log at
|
|
176
|
+
* `os.tmpdir()/wave-subagent-<taskId>.log`); its final response is enqueued
|
|
177
|
+
* as a task notification carrying the `<result>` tag, so the main agent
|
|
178
|
+
* picks it up on the next turn and can act on it.
|
|
179
|
+
*
|
|
180
|
+
* Recursion is prevented by denying the Agent tool and the Task management
|
|
181
|
+
* tools (the fork cannot spawn further subagents or mutate the shared task
|
|
182
|
+
* list), and by honoring the parent's denied rules for everything else.
|
|
183
|
+
* Gate-approved tools execute locally in the stripped fork context (no
|
|
184
|
+
* permission prompts), mirroring the other fork paths.
|
|
185
|
+
*
|
|
186
|
+
* Returns the background task ID (empty string when no BackgroundTaskManager
|
|
187
|
+
* is registered, in which case the fork still runs but without task
|
|
188
|
+
* bookkeeping or notification).
|
|
189
|
+
*/
|
|
190
|
+
runForkSubagent(prompt: string, options: {
|
|
191
|
+
description: string;
|
|
192
|
+
maxTurns?: number;
|
|
193
|
+
}, abortSignal?: AbortSignal): Promise<string>;
|
|
158
194
|
/**
|
|
159
195
|
* Parse a fork tool call's JSON arguments, recovering truncated JSON the
|
|
160
196
|
* same way the main loop does. Unparseable arguments fall back to `{}` and
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import * as aiService from "../services/aiService.js";
|
|
2
2
|
import { convertMessagesForAPI } from "../utils/convertMessagesForAPI.js";
|
|
3
3
|
import { supportsVision } from "../utils/modelCapabilities.js";
|
|
4
|
-
import { parseTaskNotificationXml } from "../utils/notificationXml.js";
|
|
4
|
+
import { parseTaskNotificationXml, taskNotificationToXml, } from "../utils/notificationXml.js";
|
|
5
5
|
import { calculateComprehensiveTotalTokens } from "../utils/tokenCalculation.js";
|
|
6
6
|
import { estimateTokens } from "../utils/tokenEstimate.js";
|
|
7
7
|
import { getTaskReminderTurnCounts, maybeInjectTaskReminder, TASK_REMINDER_CONFIG, } from "../utils/taskReminder.js";
|
|
8
|
-
import { existsSync } from "node:fs";
|
|
8
|
+
import { createWriteStream, existsSync } from "node:fs";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
9
11
|
import { buildSystemPrompt, formatCompactSummary, getCompactPrompt, } from "../prompts/index.js";
|
|
10
12
|
import { buildPlanModeReminder, buildPlanModeReEntryReminder, buildExitedPlanModeReminder, wrapInSystemReminder, } from "../prompts/planModeReminders.js";
|
|
11
13
|
import { recoverTruncatedJson } from "../utils/stringUtils.js";
|
|
14
|
+
import { AGENT_TOOL_NAME, TASK_CREATE_TOOL_NAME, TASK_GET_TOOL_NAME, TASK_LIST_TOOL_NAME, TASK_UPDATE_TOOL_NAME, } from "../constants/tools.js";
|
|
15
|
+
import { addConsolidatedAbortListener } from "../utils/abortUtils.js";
|
|
12
16
|
import { logger } from "../utils/globalLogger.js";
|
|
13
17
|
import { startInteractionSpan, endInteractionSpan, startLLMRequestSpan, endLLMRequestSpan, resetTracingState, } from "../telemetry/sessionTracing.js";
|
|
14
18
|
import { logOTelEvent } from "../telemetry/events.js";
|
|
@@ -60,6 +64,8 @@ export class AIManager {
|
|
|
60
64
|
this.turnGeneration = 0; // Guards against concurrent sendAIMessage turns
|
|
61
65
|
this.abortController = null;
|
|
62
66
|
this.toolAbortController = null;
|
|
67
|
+
/** Pending cwd-recovery notice (set when getWorkdir() auto-recovers), consumed by tools */
|
|
68
|
+
this.pendingCwdRecovery = null;
|
|
63
69
|
this.consecutiveCompactionFailures = 0;
|
|
64
70
|
/** Tracks file mtime/hash at read time for staleness detection on Edit/Write */
|
|
65
71
|
this.readFileState = new Map();
|
|
@@ -151,7 +157,45 @@ export class AIManager {
|
|
|
151
157
|
return this.configurationService.resolveWorktreeBaseRef();
|
|
152
158
|
}
|
|
153
159
|
getWorkdir() {
|
|
154
|
-
|
|
160
|
+
const workdir = this.container.get("Workdir") ?? process.cwd();
|
|
161
|
+
if (existsSync(workdir)) {
|
|
162
|
+
return workdir;
|
|
163
|
+
}
|
|
164
|
+
// The stored workdir no longer exists — e.g. the current worktree
|
|
165
|
+
// directory was removed manually with `git worktree remove` outside
|
|
166
|
+
// wave's exit flow. Node spawn() chdirs to `cwd` before spawning, so
|
|
167
|
+
// every tool that spawns a subprocess (Bash, Grep, background tasks)
|
|
168
|
+
// would fail with ENOENT and cripple the session. Recover to the
|
|
169
|
+
// original workdir (main repo) so the session keeps working.
|
|
170
|
+
if (!existsSync(this.originalWorkdir)) {
|
|
171
|
+
// No valid fallback — keep the stale value (sessions started in a
|
|
172
|
+
// directory that was deleted since cannot recover to anything real).
|
|
173
|
+
return workdir;
|
|
174
|
+
}
|
|
175
|
+
this.pendingCwdRecovery = { from: workdir, to: this.originalWorkdir };
|
|
176
|
+
// If the removed directory was this session's worktree, drop the stale
|
|
177
|
+
// worktree session state so ExitWorktree no-ops and EnterWorktree can
|
|
178
|
+
// start a fresh session.
|
|
179
|
+
const worktreeSession = this.getWorktreeSession();
|
|
180
|
+
if (worktreeSession &&
|
|
181
|
+
path.resolve(worktreeSession.worktreePath) === path.resolve(workdir)) {
|
|
182
|
+
this.setWorktreeSession(null);
|
|
183
|
+
}
|
|
184
|
+
logger.warn(`Working directory ${workdir} no longer exists; recovered session workdir to ${this.originalWorkdir}`);
|
|
185
|
+
this.container.register("Workdir", this.originalWorkdir);
|
|
186
|
+
this._onCwdChange?.(this.originalWorkdir);
|
|
187
|
+
return this.originalWorkdir;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Consume a pending cwd-recovery notice (if any). After the session
|
|
191
|
+
* workdir was auto-recovered because the previous directory disappeared,
|
|
192
|
+
* the first tool that calls this surfaces the notice to the model.
|
|
193
|
+
* Returns null when no recovery is pending.
|
|
194
|
+
*/
|
|
195
|
+
consumeCwdRecovery() {
|
|
196
|
+
const recovery = this.pendingCwdRecovery;
|
|
197
|
+
this.pendingCwdRecovery = null;
|
|
198
|
+
return recovery;
|
|
155
199
|
}
|
|
156
200
|
getOriginalWorkdir() {
|
|
157
201
|
return this.originalWorkdir;
|
|
@@ -628,6 +672,179 @@ export class AIManager {
|
|
|
628
672
|
}
|
|
629
673
|
return result;
|
|
630
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Fork subagent ("/subtask"): spawn a background agent that inherits the
|
|
677
|
+
* parent's full conversation context — the same system prompt, tools, model,
|
|
678
|
+
* and message prefix as the main loop — so the forked request prefix matches
|
|
679
|
+
* exactly and the prompt cache is reused. The fork runs to completion in the
|
|
680
|
+
* background via the shared BackgroundTaskManager (visible in /tasks, log at
|
|
681
|
+
* `os.tmpdir()/wave-subagent-<taskId>.log`); its final response is enqueued
|
|
682
|
+
* as a task notification carrying the `<result>` tag, so the main agent
|
|
683
|
+
* picks it up on the next turn and can act on it.
|
|
684
|
+
*
|
|
685
|
+
* Recursion is prevented by denying the Agent tool and the Task management
|
|
686
|
+
* tools (the fork cannot spawn further subagents or mutate the shared task
|
|
687
|
+
* list), and by honoring the parent's denied rules for everything else.
|
|
688
|
+
* Gate-approved tools execute locally in the stripped fork context (no
|
|
689
|
+
* permission prompts), mirroring the other fork paths.
|
|
690
|
+
*
|
|
691
|
+
* Returns the background task ID (empty string when no BackgroundTaskManager
|
|
692
|
+
* is registered, in which case the fork still runs but without task
|
|
693
|
+
* bookkeeping or notification).
|
|
694
|
+
*/
|
|
695
|
+
async runForkSubagent(prompt, options, abortSignal) {
|
|
696
|
+
const modelConfig = this.getModelConfig();
|
|
697
|
+
// Snapshot the conversation synchronously: the /subtask handler records
|
|
698
|
+
// the command echo in the transcript after this call, so the fork never
|
|
699
|
+
// sees its own trigger message duplicated.
|
|
700
|
+
const historyMessages = convertMessagesForAPI(this.messageManager.getMessages(), { supportsVision: supportsVision(modelConfig.capabilities) });
|
|
701
|
+
const backgroundTaskManager = this.backgroundTaskManager;
|
|
702
|
+
const permissionManager = this.permissionManager;
|
|
703
|
+
const messageQueue = this.container.has("MessageQueue")
|
|
704
|
+
? this.container.get("MessageQueue")
|
|
705
|
+
: undefined;
|
|
706
|
+
// Combine the caller's signal with an internal one so a task kill
|
|
707
|
+
// (BackgroundTaskManager.stopTask / cleanup) aborts the in-flight fork.
|
|
708
|
+
const abortController = new AbortController();
|
|
709
|
+
let abortCleanup;
|
|
710
|
+
if (abortSignal) {
|
|
711
|
+
abortCleanup = addConsolidatedAbortListener(abortSignal, [
|
|
712
|
+
() => abortController.abort(),
|
|
713
|
+
]);
|
|
714
|
+
}
|
|
715
|
+
// Recursion prevention: deny the Agent tool and Task tools, then honor the
|
|
716
|
+
// parent's denied rules for everything else.
|
|
717
|
+
const deniedTools = new Set([
|
|
718
|
+
AGENT_TOOL_NAME,
|
|
719
|
+
TASK_CREATE_TOOL_NAME,
|
|
720
|
+
TASK_GET_TOOL_NAME,
|
|
721
|
+
TASK_UPDATE_TOOL_NAME,
|
|
722
|
+
TASK_LIST_TOOL_NAME,
|
|
723
|
+
]);
|
|
724
|
+
const canUseTool = (name) => {
|
|
725
|
+
if (deniedTools.has(name))
|
|
726
|
+
return false;
|
|
727
|
+
if (permissionManager?.isToolDenied(name))
|
|
728
|
+
return false;
|
|
729
|
+
return true;
|
|
730
|
+
};
|
|
731
|
+
const deniedToolMessage = "The Agent tool and Task tools are not available in a fork subagent.";
|
|
732
|
+
// Background task bookkeeping mirrors subagentManager.executeAgent.
|
|
733
|
+
let taskId = "";
|
|
734
|
+
let logStream;
|
|
735
|
+
if (backgroundTaskManager) {
|
|
736
|
+
taskId = backgroundTaskManager.generateId();
|
|
737
|
+
const startTime = Date.now();
|
|
738
|
+
const logPath = path.join(os.tmpdir(), `wave-subagent-${taskId}.log`);
|
|
739
|
+
logStream = createWriteStream(logPath, { flags: "a" });
|
|
740
|
+
backgroundTaskManager.addTask({
|
|
741
|
+
id: taskId,
|
|
742
|
+
type: "subagent",
|
|
743
|
+
status: "running",
|
|
744
|
+
startTime,
|
|
745
|
+
description: truncateWithMarker(options.description, 1000),
|
|
746
|
+
stdout: "",
|
|
747
|
+
stderr: "",
|
|
748
|
+
outputPath: logPath,
|
|
749
|
+
onStop: () => {
|
|
750
|
+
logStream?.destroy();
|
|
751
|
+
logStream = undefined;
|
|
752
|
+
abortController.abort();
|
|
753
|
+
},
|
|
754
|
+
});
|
|
755
|
+
logStream.write(`[${new Date().toISOString()}] Fork subagent started: ${options.description}\n`);
|
|
756
|
+
}
|
|
757
|
+
const finish = async (status, content) => {
|
|
758
|
+
if (!backgroundTaskManager || !taskId)
|
|
759
|
+
return;
|
|
760
|
+
const task = backgroundTaskManager.getTask(taskId);
|
|
761
|
+
if (!task)
|
|
762
|
+
return;
|
|
763
|
+
const wasAlreadyKilled = task.status === "killed";
|
|
764
|
+
const endTime = Date.now();
|
|
765
|
+
if (status === "completed") {
|
|
766
|
+
task.status = "completed";
|
|
767
|
+
task.stdout = content;
|
|
768
|
+
logStream?.write(`[${new Date().toISOString()}] Final response:\n${content}\n`);
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
task.status = "failed";
|
|
772
|
+
task.stderr = content;
|
|
773
|
+
logStream?.write(`[${new Date().toISOString()}] Fork subagent failed: ${content}\n`);
|
|
774
|
+
}
|
|
775
|
+
task.endTime = endTime;
|
|
776
|
+
if (task.startTime)
|
|
777
|
+
task.runtime = endTime - task.startTime;
|
|
778
|
+
logStream?.end();
|
|
779
|
+
logStream = undefined;
|
|
780
|
+
// Skip the notification when the task was already stopped (e.g. by main
|
|
781
|
+
// agent shutdown) — mirrors subagentManager's wasAlreadyKilled guard.
|
|
782
|
+
if (!wasAlreadyKilled && messageQueue) {
|
|
783
|
+
const description = truncateWithMarker(options.description, 1000);
|
|
784
|
+
const summary = status === "completed"
|
|
785
|
+
? `Subtask "${description}" completed`
|
|
786
|
+
: `Subtask "${description}" failed: ${content}`;
|
|
787
|
+
messageQueue.enqueueNotification(taskNotificationToXml({
|
|
788
|
+
type: "task_notification",
|
|
789
|
+
taskId,
|
|
790
|
+
taskType: "agent",
|
|
791
|
+
status,
|
|
792
|
+
summary,
|
|
793
|
+
...(status === "completed" && { result: content }),
|
|
794
|
+
}));
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
// Fire-and-forget: the fork runs to completion in the background; the
|
|
798
|
+
// caller gets the task ID immediately.
|
|
799
|
+
(async () => {
|
|
800
|
+
try {
|
|
801
|
+
const result = await this.runForkLoop(historyMessages, prompt, {
|
|
802
|
+
// /subtask aligns with Claude Code's fork subagent
|
|
803
|
+
// (forkSubagent.ts:65): a loose upper bound that practically
|
|
804
|
+
// never binds.
|
|
805
|
+
maxTurns: options.maxTurns ?? 200,
|
|
806
|
+
canUseTool,
|
|
807
|
+
deniedToolMessage,
|
|
808
|
+
}, abortController.signal);
|
|
809
|
+
if (result.usage && this.callbacks?.onUsageAdded) {
|
|
810
|
+
this.callbacks.onUsageAdded({
|
|
811
|
+
...result.usage,
|
|
812
|
+
model: modelConfig.model,
|
|
813
|
+
operation_type: "agent",
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
if (result.content?.trim()) {
|
|
817
|
+
await finish("completed", result.content);
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
await finish("failed", "Fork subagent produced no text response");
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
// Abort is the expected exit for a killed task; stopTask has already
|
|
825
|
+
// marked it "killed", so no notification is sent.
|
|
826
|
+
if (abortController.signal.aborted) {
|
|
827
|
+
const task = backgroundTaskManager?.getTask(taskId);
|
|
828
|
+
if (task && task.status === "running") {
|
|
829
|
+
task.status = "killed";
|
|
830
|
+
task.endTime = Date.now();
|
|
831
|
+
if (task.startTime)
|
|
832
|
+
task.runtime = task.endTime - task.startTime;
|
|
833
|
+
}
|
|
834
|
+
logStream?.end();
|
|
835
|
+
logStream = undefined;
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
logger?.error("Fork subagent execution failed:", error);
|
|
839
|
+
await finish("failed", error instanceof Error ? error.message : String(error));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
finally {
|
|
843
|
+
abortCleanup?.();
|
|
844
|
+
}
|
|
845
|
+
})();
|
|
846
|
+
return taskId;
|
|
847
|
+
}
|
|
631
848
|
/**
|
|
632
849
|
* Parse a fork tool call's JSON arguments, recovering truncated JSON the
|
|
633
850
|
* same way the main loop does. Unparseable arguments fall back to `{}` and
|
|
@@ -1348,9 +1565,15 @@ ${question}`;
|
|
|
1348
1565
|
await this.messageManager.saveSession();
|
|
1349
1566
|
// Clear temporary rules
|
|
1350
1567
|
this.permissionManager?.clearTemporaryRules();
|
|
1351
|
-
// Clear abort controllers
|
|
1352
|
-
|
|
1353
|
-
|
|
1568
|
+
// Clear abort controllers (only if we still own them — a concurrent
|
|
1569
|
+
// turn may have already registered its own controller, and clearing
|
|
1570
|
+
// it would orphan that turn and make it uninterruptible)
|
|
1571
|
+
if (this.abortController === abortController) {
|
|
1572
|
+
this.abortController = null;
|
|
1573
|
+
}
|
|
1574
|
+
if (this.toolAbortController === toolAbortController) {
|
|
1575
|
+
this.toolAbortController = null;
|
|
1576
|
+
}
|
|
1354
1577
|
// Execute Stop/SubagentStop hooks only if the operation was not aborted
|
|
1355
1578
|
const isCurrentlyAborted = abortController.signal.aborted || toolAbortController.signal.aborted;
|
|
1356
1579
|
if (!isCurrentlyAborted) {
|
|
@@ -1373,11 +1596,19 @@ ${question}`;
|
|
|
1373
1596
|
}
|
|
1374
1597
|
}
|
|
1375
1598
|
// Inject pending notifications from background tasks (after Stop hooks,
|
|
1376
|
-
// aligned with Claude Code which fires Stop hooks unconditionally)
|
|
1599
|
+
// aligned with Claude Code which fires Stop hooks unconditionally).
|
|
1600
|
+
// Skip injection when this turn was aborted: folding a pending
|
|
1601
|
+
// notification in would restart the loop and resurrect the aborted
|
|
1602
|
+
// turn — if a concurrent sendMessage is already running (e.g. the
|
|
1603
|
+
// queue's "send now" button), it would spawn a second, orphaned,
|
|
1604
|
+
// uninterruptible agent loop. The notification stays queued for the
|
|
1605
|
+
// next turn.
|
|
1377
1606
|
const messageQueue = this.container.has("MessageQueue")
|
|
1378
1607
|
? this.container.get("MessageQueue")
|
|
1379
1608
|
: undefined;
|
|
1380
|
-
if (messageQueue &&
|
|
1609
|
+
if (messageQueue &&
|
|
1610
|
+
messageQueue.hasNotifications() &&
|
|
1611
|
+
!isCurrentlyAborted) {
|
|
1381
1612
|
const notifications = messageQueue.drainNotifications();
|
|
1382
1613
|
for (const notification of notifications) {
|
|
1383
1614
|
const block = parseTaskNotificationXml(notification);
|
|
@@ -1388,6 +1619,7 @@ ${question}`;
|
|
|
1388
1619
|
status: block.status,
|
|
1389
1620
|
summary: block.summary,
|
|
1390
1621
|
outputFile: block.outputFile,
|
|
1622
|
+
result: block.result,
|
|
1391
1623
|
});
|
|
1392
1624
|
}
|
|
1393
1625
|
}
|
|
@@ -13,13 +13,11 @@ export interface MessageManagerCallbacks {
|
|
|
13
13
|
onAssistantContentUpdated?: (params: {
|
|
14
14
|
messageId: string;
|
|
15
15
|
chunk: string;
|
|
16
|
-
accumulated: string;
|
|
17
16
|
stage: "streaming" | "end";
|
|
18
17
|
}) => void;
|
|
19
18
|
onAssistantReasoningUpdated?: (params: {
|
|
20
19
|
messageId: string;
|
|
21
20
|
chunk: string;
|
|
22
|
-
accumulated: string;
|
|
23
21
|
stage: "streaming" | "end";
|
|
24
22
|
}) => void;
|
|
25
23
|
onToolBlockUpdated?: (params: ToolBlockUpdateCallbackParams) => void;
|
|
@@ -180,7 +178,7 @@ export declare class MessageManager {
|
|
|
180
178
|
/**
|
|
181
179
|
* Update the current assistant message content during streaming
|
|
182
180
|
* This method updates the last assistant message's content without creating a new message
|
|
183
|
-
* FR-001: Tracks and provides
|
|
181
|
+
* FR-001: Tracks and provides chunk (new content since last update)
|
|
184
182
|
*/
|
|
185
183
|
updateCurrentMessageContent(newAccumulatedContent: string): void;
|
|
186
184
|
/**
|
|
@@ -366,6 +366,16 @@ export class MessageManager {
|
|
|
366
366
|
async compactMessagesAndUpdateSession(compactedContent, usage) {
|
|
367
367
|
// Get last 2 API rounds to preserve (structurally safe boundary)
|
|
368
368
|
const lastThreeMessages = getLastApiRounds(this.messages, 2);
|
|
369
|
+
// The turn that triggered compaction has already finished streaming, so
|
|
370
|
+
// finalize any still-'streaming' text/reasoning blocks on the preserved
|
|
371
|
+
// last assistant message. Otherwise hosts that pull a full snapshot after
|
|
372
|
+
// compaction (or the loop's own finalizeStreamingBlocks, which no-ops once
|
|
373
|
+
// a plan-mode reminder is appended) would keep showing the streaming dot.
|
|
374
|
+
const lastPreserved = lastThreeMessages[lastThreeMessages.length - 1];
|
|
375
|
+
if (lastPreserved && lastPreserved.role === "assistant") {
|
|
376
|
+
this.finalizeStreamingBlock(lastPreserved, "text");
|
|
377
|
+
this.finalizeStreamingBlock(lastPreserved, "reasoning");
|
|
378
|
+
}
|
|
369
379
|
// Create compacted message
|
|
370
380
|
const compactMessage = {
|
|
371
381
|
id: generateMessageId(),
|
|
@@ -526,7 +536,6 @@ export class MessageManager {
|
|
|
526
536
|
const callbackParams = {
|
|
527
537
|
messageId: lastMessage.id,
|
|
528
538
|
chunk: "",
|
|
529
|
-
accumulated: block.content,
|
|
530
539
|
stage: "end",
|
|
531
540
|
};
|
|
532
541
|
if (type === "text") {
|
|
@@ -540,7 +549,7 @@ export class MessageManager {
|
|
|
540
549
|
/**
|
|
541
550
|
* Update the current assistant message content during streaming
|
|
542
551
|
* This method updates the last assistant message's content without creating a new message
|
|
543
|
-
* FR-001: Tracks and provides
|
|
552
|
+
* FR-001: Tracks and provides chunk (new content since last update)
|
|
544
553
|
*/
|
|
545
554
|
updateCurrentMessageContent(newAccumulatedContent) {
|
|
546
555
|
if (this.messages.length === 0)
|
|
@@ -573,11 +582,10 @@ export class MessageManager {
|
|
|
573
582
|
stage: "streaming",
|
|
574
583
|
});
|
|
575
584
|
}
|
|
576
|
-
// FR-001: Trigger callbacks with chunk and
|
|
585
|
+
// FR-001: Trigger callbacks with chunk delta and stage
|
|
577
586
|
this.callbacks.onAssistantContentUpdated?.({
|
|
578
587
|
messageId: lastMessage.id,
|
|
579
588
|
chunk,
|
|
580
|
-
accumulated: newAccumulatedContent,
|
|
581
589
|
stage: "streaming",
|
|
582
590
|
});
|
|
583
591
|
// Note: Subagent-specific callbacks are now handled by SubagentManager
|
|
@@ -620,11 +628,10 @@ export class MessageManager {
|
|
|
620
628
|
startTime: Date.now(),
|
|
621
629
|
});
|
|
622
630
|
}
|
|
623
|
-
// Trigger callbacks with chunk and
|
|
631
|
+
// Trigger callbacks with chunk delta and stage
|
|
624
632
|
this.callbacks.onAssistantReasoningUpdated?.({
|
|
625
633
|
messageId: lastMessage.id,
|
|
626
634
|
chunk,
|
|
627
|
-
accumulated: newAccumulatedReasoning,
|
|
628
635
|
stage: "streaming",
|
|
629
636
|
});
|
|
630
637
|
}
|
|
@@ -14,6 +14,34 @@ export class SlashCommandManager {
|
|
|
14
14
|
}
|
|
15
15
|
initialize() {
|
|
16
16
|
this.loadCustomCommands();
|
|
17
|
+
// Builtin /subtask: manual fork subagent (aligned with Claude Code's
|
|
18
|
+
// /subtask — inherits the parent's full context, runs in the background,
|
|
19
|
+
// result returns to the main conversation via a task notification).
|
|
20
|
+
this.registerCommand({
|
|
21
|
+
id: "subtask",
|
|
22
|
+
name: "subtask",
|
|
23
|
+
description: "Start a fork subagent with the current conversation context in the background",
|
|
24
|
+
handler: async (args, signal) => {
|
|
25
|
+
const taskDescription = args?.trim();
|
|
26
|
+
if (!taskDescription) {
|
|
27
|
+
this.messageManager.addErrorBlock("Usage: /subtask <task description>");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
// The fork snapshots its context synchronously on entry, so the
|
|
32
|
+
// transcript echo below never duplicates the prompt inside the fork.
|
|
33
|
+
await this.aiManager.runForkSubagent(taskDescription, { description: taskDescription }, signal);
|
|
34
|
+
this.messageManager.addUserMessage({
|
|
35
|
+
content: `/subtask ${taskDescription}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
this.aiManager.setIsLoading(false);
|
|
40
|
+
logger?.error("Failed to execute /subtask:", error);
|
|
41
|
+
this.messageManager.addErrorBlock(`Failed to execute /subtask: ${error instanceof Error ? error.message : String(error)}`);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
});
|
|
17
45
|
// Listen for skill refreshes and update skill commands
|
|
18
46
|
const skillManager = this.container.get("SkillManager");
|
|
19
47
|
if (skillManager) {
|
|
@@ -19,7 +19,6 @@ export interface SubagentManagerCallbacks {
|
|
|
19
19
|
subagentId: string;
|
|
20
20
|
messageId: string;
|
|
21
21
|
chunk: string;
|
|
22
|
-
accumulated: string;
|
|
23
22
|
stage: "streaming" | "end";
|
|
24
23
|
}) => void;
|
|
25
24
|
/** Triggered during subagent reasoning streaming updates */
|
|
@@ -27,7 +26,6 @@ export interface SubagentManagerCallbacks {
|
|
|
27
26
|
subagentId: string;
|
|
28
27
|
messageId: string;
|
|
29
28
|
chunk: string;
|
|
30
|
-
accumulated: string;
|
|
31
29
|
stage: "streaming" | "end";
|
|
32
30
|
}) => void;
|
|
33
31
|
/** Triggered when subagent tool block is updated */
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and graceful shutdown for Wave's OpenTelemetry integration.
|
|
6
6
|
*/
|
|
7
7
|
import type { TelemetryConfig } from "../types/telemetry.js";
|
|
8
|
-
import type { SpanExporter, ReadableSpan } from "@opentelemetry/sdk-trace-
|
|
8
|
+
import type { SpanExporter, ReadableSpan } from "@opentelemetry/sdk-trace-base";
|
|
9
9
|
import type { LogRecordExporter, ReadableLogRecord } from "@opentelemetry/sdk-logs";
|
|
10
10
|
/**
|
|
11
11
|
* Resolve the full TelemetryConfig from env vars + settings.json.
|