wave-agent-sdk 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +9 -6
- package/dist/agent.js +35 -33
- package/dist/builtin/index.d.ts +1 -0
- package/dist/builtin/index.js +20 -0
- package/dist/builtin/plugins.d.ts +1 -0
- package/dist/builtin/plugins.js +225 -0
- package/dist/builtin/skills/artifact.d.ts +1 -0
- package/dist/builtin/skills/artifact.js +18 -0
- package/dist/builtin/skills/code-review.d.ts +1 -0
- package/{builtin/skills/code-review/SKILL.md → dist/builtin/skills/code-review.js} +23 -19
- package/dist/builtin/skills/deep-research.d.ts +1 -0
- package/{builtin/skills/deep-research/SKILL.md → dist/builtin/skills/deep-research.js} +18 -14
- package/dist/builtin/skills/init.d.ts +1 -0
- package/{builtin/skills/init/SKILL.md → dist/builtin/skills/init.js} +6 -3
- package/dist/builtin/skills/loop.d.ts +1 -0
- package/dist/builtin/skills/loop.js +83 -0
- package/dist/builtin/skills/settings.d.ts +1 -0
- package/dist/builtin/skills/settings.js +1249 -0
- package/dist/builtin/skills/simplify.d.ts +1 -0
- package/{builtin/skills/simplify/SKILL.md → dist/builtin/skills/simplify.js} +7 -3
- package/dist/builtin/subagents.d.ts +1 -0
- package/dist/builtin/subagents.js +164 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/managers/aiManager.d.ts +10 -0
- package/dist/managers/aiManager.js +49 -5
- package/dist/managers/mcpManager.js +1 -1
- package/dist/managers/messageManager.d.ts +6 -0
- package/dist/managers/messageManager.js +33 -0
- package/dist/managers/subagentManager.d.ts +9 -0
- package/dist/managers/subagentManager.js +62 -3
- package/dist/services/hook.js +41 -7
- package/dist/services/initializationService.js +0 -21
- package/dist/services/jsonlHandler.d.ts +37 -2
- package/dist/services/jsonlHandler.js +55 -6
- package/dist/services/session.d.ts +35 -4
- package/dist/services/session.js +233 -36
- package/dist/services/worktreeHooks.d.ts +45 -0
- package/dist/services/worktreeHooks.js +133 -0
- package/dist/tools/agentTool.js +36 -1
- package/dist/tools/bashTool.js +120 -57
- package/dist/tools/editTool.js +1 -0
- package/dist/tools/enterWorktreeTool.d.ts +1 -1
- package/dist/tools/enterWorktreeTool.js +44 -31
- package/dist/tools/exitWorktreeTool.js +21 -26
- package/dist/tools/readTool.js +6 -3
- package/dist/tools/types.d.ts +1 -0
- package/dist/tools/writeTool.js +1 -0
- package/dist/types/agent.d.ts +5 -0
- package/dist/types/hooks.d.ts +3 -2
- package/dist/types/messaging.d.ts +1 -0
- package/dist/types/skills.d.ts +0 -1
- package/dist/types/skills.js +0 -1
- package/dist/utils/asyncWorkRegistry.d.ts +32 -0
- package/dist/utils/asyncWorkRegistry.js +81 -0
- package/dist/utils/builtinEmbed.d.ts +21 -0
- package/dist/utils/builtinEmbed.js +53 -0
- package/dist/utils/configPaths.d.ts +0 -1
- package/dist/utils/configPaths.js +5 -26
- package/dist/utils/containerSetup.js +5 -0
- package/dist/utils/convertMessagesForAPI.js +21 -2
- package/dist/utils/messageOperations.d.ts +1 -0
- package/dist/utils/skillParser.js +3 -6
- package/dist/utils/toolImagePersistence.d.ts +25 -0
- package/dist/utils/toolImagePersistence.js +56 -0
- package/dist/utils/windowsPaths.d.ts +28 -0
- package/dist/utils/windowsPaths.js +47 -0
- package/dist/utils/worktreeSession.d.ts +6 -0
- package/dist/utils/worktreeUtils.d.ts +7 -0
- package/dist/utils/worktreeUtils.js +88 -40
- package/package.json +5 -5
- package/builtin/plugins/sdd/.wave-plugin/plugin.json +0 -8
- package/builtin/plugins/sdd/hooks/hooks.json +0 -14
- package/builtin/plugins/sdd/scripts/session-start.js +0 -24
- package/builtin/plugins/sdd/scripts/spec-count.js +0 -77
- package/builtin/plugins/sdd/skills/specify/SKILL.md +0 -47
- package/builtin/plugins/sdd/skills/specify/templates/spec-template.md +0 -47
- package/builtin/skills/artifact/SKILL.md +0 -14
- package/builtin/skills/loop/SKILL.md +0 -79
- package/builtin/skills/settings/ENV.md +0 -78
- package/builtin/skills/settings/HOOKS.md +0 -227
- package/builtin/skills/settings/MCP.md +0 -137
- package/builtin/skills/settings/MEMORY.md +0 -76
- package/builtin/skills/settings/MODELS.md +0 -119
- package/builtin/skills/settings/PERMISSIONS.md +0 -88
- package/builtin/skills/settings/PLUGINS.md +0 -171
- package/builtin/skills/settings/SKILL.md +0 -132
- package/builtin/skills/settings/SKILLS.md +0 -107
- package/builtin/skills/settings/SUBAGENTS.md +0 -77
- package/builtin/subagents/bash.md +0 -19
- package/builtin/subagents/explore.md +0 -43
- package/builtin/subagents/general-purpose.md +0 -20
- package/builtin/subagents/plan.md +0 -56
- package/builtin/subagents/vision.md +0 -18
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const simplifySkill: Record<string, string>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { AGENT_TOOL_NAME } from "../../constants/tools.js";
|
|
2
|
+
export const simplifySkill = {
|
|
3
|
+
"skills/simplify/SKILL.md": `---
|
|
2
4
|
name: simplify
|
|
3
5
|
description: Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.
|
|
4
6
|
disable-model-invocation: true
|
|
@@ -10,11 +12,11 @@ Review all changed files for reuse, quality, and efficiency. Fix any issues foun
|
|
|
10
12
|
|
|
11
13
|
## Phase 1: Identify Changes
|
|
12
14
|
|
|
13
|
-
Run
|
|
15
|
+
Run \`git diff\` (or \`git diff HEAD\` if there are staged changes) to see what changed. If there are no git changes, review the most recently modified files that the user mentioned or that you edited earlier in this conversation.
|
|
14
16
|
|
|
15
17
|
## Phase 2: Launch Three Review Agents in Parallel
|
|
16
18
|
|
|
17
|
-
Use the
|
|
19
|
+
Use the ${AGENT_TOOL_NAME} tool to launch all three agents concurrently in a single message. Pass each agent the full diff so it has the complete context.
|
|
18
20
|
|
|
19
21
|
### Agent 1: Code Reuse Review
|
|
20
22
|
|
|
@@ -53,3 +55,5 @@ Review the same changes for efficiency:
|
|
|
53
55
|
Wait for all three agents to complete. Aggregate their findings and fix each issue directly. If a finding is a false positive or not worth addressing, note it and move on — do not argue with the finding, just skip it.
|
|
54
56
|
|
|
55
57
|
When done, briefly summarize what was fixed (or confirm the code was already clean).
|
|
58
|
+
`,
|
|
59
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const subagents: Record<string, string>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { BASH_TOOL_NAME, EDIT_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME, LSP_TOOL_NAME, READ_TOOL_NAME, WRITE_TOOL_NAME, } from "../constants/tools.js";
|
|
2
|
+
export const subagents = {
|
|
3
|
+
"subagents/bash.md": `---
|
|
4
|
+
name: Bash
|
|
5
|
+
description: Command execution specialist for running bash commands. Use this for git operations, command execution, and other terminal tasks.
|
|
6
|
+
tools: [${BASH_TOOL_NAME}]
|
|
7
|
+
model: inherit
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
You are a command execution specialist. Your role is to execute bash commands efficiently and safely.
|
|
11
|
+
|
|
12
|
+
Guidelines:
|
|
13
|
+
- Execute commands precisely as instructed
|
|
14
|
+
- For git operations, follow git safety protocols
|
|
15
|
+
- Report command output clearly and concisely
|
|
16
|
+
- If a command fails, explain the error and suggest solutions
|
|
17
|
+
- Use command chaining (&&) for dependent operations
|
|
18
|
+
- Quote paths with spaces properly
|
|
19
|
+
- For clear communication, avoid using emojis
|
|
20
|
+
|
|
21
|
+
Complete the requested operations efficiently.
|
|
22
|
+
`,
|
|
23
|
+
"subagents/explore.md": `---
|
|
24
|
+
name: Explore
|
|
25
|
+
description: 'Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions.'
|
|
26
|
+
tools: [${GLOB_TOOL_NAME}, ${GREP_TOOL_NAME}, ${READ_TOOL_NAME}, ${BASH_TOOL_NAME}, ${LSP_TOOL_NAME}]
|
|
27
|
+
model: fastModel
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
|
|
31
|
+
|
|
32
|
+
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
|
33
|
+
This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
|
|
34
|
+
- Creating new files (no ${WRITE_TOOL_NAME}, touch, or file creation of any kind)
|
|
35
|
+
- Modifying existing files (no ${EDIT_TOOL_NAME} operations)
|
|
36
|
+
- Moving or copying files (no mv or cp)
|
|
37
|
+
- Creating temporary files anywhere, including /tmp
|
|
38
|
+
- Using redirect operators (>, >>, |) or heredocs to write to files
|
|
39
|
+
- Running ANY commands that change system state
|
|
40
|
+
|
|
41
|
+
Your role is EXCLUSIVELY to search and analyze existing code. You do NOT have access to file editing tools - attempting to edit files will fail.
|
|
42
|
+
|
|
43
|
+
Your strengths:
|
|
44
|
+
- Rapidly finding files using glob patterns
|
|
45
|
+
- Searching code and text with powerful regex patterns
|
|
46
|
+
- Reading and analyzing file contents
|
|
47
|
+
- Using Language Server Protocol (LSP) for deep code intelligence (definitions, references, etc.)
|
|
48
|
+
|
|
49
|
+
Guidelines:
|
|
50
|
+
- Use ${GLOB_TOOL_NAME} for broad file pattern matching
|
|
51
|
+
- Use ${GREP_TOOL_NAME} for searching file contents with regex
|
|
52
|
+
- Use ${READ_TOOL_NAME} when you know the specific file path you need to read
|
|
53
|
+
- Use ${LSP_TOOL_NAME} for code intelligence features like finding definitions, references, implementations, and symbols. This is especially useful for understanding complex code relationships.
|
|
54
|
+
- Use ${BASH_TOOL_NAME} ONLY for read-only operations (ls, git status, git log, git diff, find, cat, head, tail)
|
|
55
|
+
- NEVER use ${BASH_TOOL_NAME} for: mkdir, touch, rm, cp, mv, git add, git commit, npm install, pip install, or any file creation/modification
|
|
56
|
+
- Adapt your search approach based on the thoroughness level specified by the caller
|
|
57
|
+
- Return file paths as absolute paths in your final response
|
|
58
|
+
- For clear communication, avoid using emojis
|
|
59
|
+
- Communicate your final report directly as a regular message - do NOT attempt to create files
|
|
60
|
+
|
|
61
|
+
NOTE: You are meant to be a fast agent that returns output as quickly as possible. In order to achieve this you must:
|
|
62
|
+
- Make efficient use of the tools that you have at your disposal: be smart about how you search for files and implementations
|
|
63
|
+
- Wherever possible you should try to spawn multiple parallel tool calls for grepping and reading files
|
|
64
|
+
|
|
65
|
+
Complete the user's search request efficiently and report your findings clearly.
|
|
66
|
+
`,
|
|
67
|
+
"subagents/general-purpose.md": `---
|
|
68
|
+
description: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you.
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
You are an agent. Given the user's message, you should use the tools available to complete the task. Do what has been asked; nothing more, nothing less. When you complete the task simply respond with a detailed writeup.
|
|
72
|
+
|
|
73
|
+
Your strengths:
|
|
74
|
+
- Searching for code, configurations, and patterns across large codebases
|
|
75
|
+
- Analyzing multiple files to understand system architecture
|
|
76
|
+
- Investigating complex questions that require exploring many files
|
|
77
|
+
- Performing multi-step research tasks
|
|
78
|
+
|
|
79
|
+
Guidelines:
|
|
80
|
+
- For file searches: Use ${GREP_TOOL_NAME} or ${GLOB_TOOL_NAME} when you need to search broadly. Use ${READ_TOOL_NAME} when you know the specific file path.
|
|
81
|
+
- For analysis: Start broad and narrow down. Use multiple search strategies if the first doesn't yield results.
|
|
82
|
+
- Be thorough: Check multiple locations, consider different naming conventions, look for related files.
|
|
83
|
+
- NEVER create files unless they're absolutely necessary for achieving your goal. ALWAYS prefer editing an existing file to creating a new one.
|
|
84
|
+
- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested.
|
|
85
|
+
- In your final response always share relevant file names and code snippets. Any file paths you return in your response MUST be absolute. Do NOT use relative paths.
|
|
86
|
+
- For clear communication, avoid using emojis.
|
|
87
|
+
`,
|
|
88
|
+
"subagents/plan.md": `---
|
|
89
|
+
name: Plan
|
|
90
|
+
description: Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.
|
|
91
|
+
tools: [${GLOB_TOOL_NAME}, ${GREP_TOOL_NAME}, ${READ_TOOL_NAME}, ${BASH_TOOL_NAME}, ${LSP_TOOL_NAME}]
|
|
92
|
+
model: inherit
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
You are a software architect and planning specialist. Your role is to explore the codebase and design implementation plans.
|
|
96
|
+
|
|
97
|
+
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
|
98
|
+
This is a READ-ONLY planning task. You are STRICTLY PROHIBITED from:
|
|
99
|
+
- Creating new files (no ${WRITE_TOOL_NAME}, touch, or file creation of any kind)
|
|
100
|
+
- Modifying existing files (no ${EDIT_TOOL_NAME} operations)
|
|
101
|
+
- Moving or copying files (no mv or cp)
|
|
102
|
+
- Creating temporary files anywhere, including /tmp
|
|
103
|
+
- Using redirect operators (>, >>, |) or heredocs to write to files
|
|
104
|
+
- Running ANY commands that change system state
|
|
105
|
+
|
|
106
|
+
Your role is EXCLUSIVELY to explore the codebase and design implementation plans. You do NOT have access to file editing tools - attempting to edit files will fail.
|
|
107
|
+
|
|
108
|
+
You will be provided with a set of requirements and optionally a perspective on how to approach the design process.
|
|
109
|
+
|
|
110
|
+
## Your Process
|
|
111
|
+
|
|
112
|
+
1. **Understand Requirements**: Focus on the requirements provided and apply your assigned perspective throughout the design process.
|
|
113
|
+
|
|
114
|
+
2. **Explore Thoroughly**:
|
|
115
|
+
- Read any files provided to you in the initial prompt
|
|
116
|
+
- Find existing patterns and conventions using ${GLOB_TOOL_NAME}, ${GREP_TOOL_NAME}, and ${READ_TOOL_NAME}
|
|
117
|
+
- Understand the current architecture
|
|
118
|
+
- Identify similar features as reference
|
|
119
|
+
- Trace through relevant code paths
|
|
120
|
+
- Use ${BASH_TOOL_NAME} ONLY for read-only operations (ls, git status, git log, git diff, find, cat, head, tail)
|
|
121
|
+
- NEVER use ${BASH_TOOL_NAME} for: mkdir, touch, rm, cp, mv, git add, git commit, npm install, pip install, or any file creation/modification
|
|
122
|
+
|
|
123
|
+
3. **Design Solution**:
|
|
124
|
+
- Create implementation approach based on your assigned perspective
|
|
125
|
+
- Consider trade-offs and architectural decisions
|
|
126
|
+
- Follow existing patterns where appropriate
|
|
127
|
+
|
|
128
|
+
4. **Detail the Plan**:
|
|
129
|
+
- Provide step-by-step implementation strategy
|
|
130
|
+
- Identify dependencies and sequencing
|
|
131
|
+
- Anticipate potential challenges
|
|
132
|
+
|
|
133
|
+
## Required Output
|
|
134
|
+
|
|
135
|
+
End your response with:
|
|
136
|
+
|
|
137
|
+
### Critical Files for Implementation
|
|
138
|
+
List 3-5 files most critical for implementing this plan:
|
|
139
|
+
- path/to/file1.ts - [Brief reason: e.g., "Core logic to modify"]
|
|
140
|
+
- path/to/file2.ts - [Brief reason: e.g., "Interfaces to implement"]
|
|
141
|
+
- path/to/file3.ts - [Brief reason: e.g., "Pattern to follow"]
|
|
142
|
+
|
|
143
|
+
REMEMBER: You can ONLY explore and plan. You CANNOT and MUST NOT write, edit, or modify any files. You do NOT have access to file editing tools.
|
|
144
|
+
`,
|
|
145
|
+
"subagents/vision.md": `---
|
|
146
|
+
name: vision
|
|
147
|
+
description: 'Image recognition specialist that runs on the model specified by the WAVE_VISION_MODEL environment variable. Use this when the current model does not support image recognition but the user has shared an image (identify the image by its "[Image source: <path>]" metadata). Pass the image file path(s) in the prompt; this agent reads the image with the ${READ_TOOL_NAME} tool and returns a detailed text description of its contents.'
|
|
148
|
+
tools: [${READ_TOOL_NAME}]
|
|
149
|
+
model: visionModel
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
You are an image recognition specialist. You run on a vision-capable model and your job is to look at image files and return detailed text descriptions of their contents.
|
|
153
|
+
|
|
154
|
+
When given image file path(s):
|
|
155
|
+
- Use the ${READ_TOOL_NAME} tool on each image path to load it. The ${READ_TOOL_NAME} tool returns the image as base64 image data that you can see directly.
|
|
156
|
+
- Describe the image contents in detail: what is shown, any visible text (transcribe verbatim where relevant), layout, colors, objects, and anything else the caller asked about.
|
|
157
|
+
- If an image cannot be read (file missing, not an image, or too large), report the error clearly and state which path failed.
|
|
158
|
+
- Do not invent or guess content you cannot see — only describe what the image actually shows.
|
|
159
|
+
- Return your description directly as a text message. Do NOT create files.
|
|
160
|
+
- Avoid using emojis in your response.
|
|
161
|
+
|
|
162
|
+
Complete the image recognition task and report your findings clearly.
|
|
163
|
+
`,
|
|
164
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./core/session.js";
|
|
2
2
|
export * from "./services/authService.js";
|
|
3
|
+
export * from "./services/worktreeHooks.js";
|
|
3
4
|
export * from "./constants/tools.js";
|
|
4
5
|
export * from "./agent.js";
|
|
5
6
|
export * from "./core/plugin.js";
|
|
@@ -21,6 +22,7 @@ export * from "./utils/hookMatcher.js";
|
|
|
21
22
|
export * from "./utils/tokenCalculation.js";
|
|
22
23
|
export * from "./utils/gitUtils.js";
|
|
23
24
|
export * from "./utils/nameGenerator.js";
|
|
25
|
+
export * from "./utils/pathEncoder.js";
|
|
24
26
|
export * from "./utils/worktreeSession.js";
|
|
25
27
|
export * from "./utils/worktreeUtils.js";
|
|
26
28
|
export { loadMergedWaveConfig, loadUserConfigEnv, } from "./services/configurationService.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Export all services
|
|
2
2
|
export * from "./core/session.js";
|
|
3
3
|
export * from "./services/authService.js";
|
|
4
|
+
export * from "./services/worktreeHooks.js";
|
|
4
5
|
// Export constants
|
|
5
6
|
export * from "./constants/tools.js";
|
|
6
7
|
// Export main agent
|
|
@@ -25,6 +26,7 @@ export * from "./utils/hookMatcher.js";
|
|
|
25
26
|
export * from "./utils/tokenCalculation.js";
|
|
26
27
|
export * from "./utils/gitUtils.js";
|
|
27
28
|
export * from "./utils/nameGenerator.js";
|
|
29
|
+
export * from "./utils/pathEncoder.js";
|
|
28
30
|
export * from "./utils/worktreeSession.js";
|
|
29
31
|
export * from "./utils/worktreeUtils.js";
|
|
30
32
|
export { loadMergedWaveConfig, loadUserConfigEnv, } from "./services/configurationService.js";
|
|
@@ -12,6 +12,15 @@ interface ForkLoopResult {
|
|
|
12
12
|
}
|
|
13
13
|
export interface AIManagerCallbacks {
|
|
14
14
|
onCompactionStateChange?: (isCompacting: boolean) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Streaming content from the compaction fork (accumulated, same semantics
|
|
17
|
+
* as `onContentUpdate` in CallAgentOptions). Reasoning chunks fall back to
|
|
18
|
+
* this channel when no reasoning callback is provided. Consumed by the CLI
|
|
19
|
+
* to show the compaction loading tail.
|
|
20
|
+
*/
|
|
21
|
+
onCompactionContentUpdate?: (content: string) => void;
|
|
22
|
+
/** Streaming reasoning from the compaction fork (accumulated). */
|
|
23
|
+
onCompactionReasoningUpdate?: (content: string) => void;
|
|
15
24
|
onUsageAdded?: (usage: Usage) => void;
|
|
16
25
|
onCwdChange?: (newCwd: string) => void;
|
|
17
26
|
}
|
|
@@ -59,6 +68,7 @@ export declare class AIManager {
|
|
|
59
68
|
private get memoryService();
|
|
60
69
|
private get taskManager();
|
|
61
70
|
private get backgroundTaskManager();
|
|
71
|
+
private get asyncWorkRegistry();
|
|
62
72
|
private get hookManager();
|
|
63
73
|
private get reversionManager();
|
|
64
74
|
private get permissionManager();
|
|
@@ -1,6 +1,7 @@
|
|
|
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 { persistToolImages } from "../utils/toolImagePersistence.js";
|
|
4
5
|
import { parseTaskNotificationXml, taskNotificationToXml, } from "../utils/notificationXml.js";
|
|
5
6
|
import { calculateComprehensiveTotalTokens } from "../utils/tokenCalculation.js";
|
|
6
7
|
import { estimateTokens } from "../utils/tokenEstimate.js";
|
|
@@ -102,6 +103,9 @@ export class AIManager {
|
|
|
102
103
|
get backgroundTaskManager() {
|
|
103
104
|
return this.container.get("BackgroundTaskManager");
|
|
104
105
|
}
|
|
106
|
+
get asyncWorkRegistry() {
|
|
107
|
+
return this.container.get("AsyncWorkRegistry");
|
|
108
|
+
}
|
|
105
109
|
get hookManager() {
|
|
106
110
|
return this.container.get("HookManager");
|
|
107
111
|
}
|
|
@@ -430,7 +434,7 @@ export class AIManager {
|
|
|
430
434
|
// 4. Fork path: fork the conversation with the same system prompt,
|
|
431
435
|
// tools, model, and generation params as the main loop so the forked
|
|
432
436
|
// request prefix matches exactly and the prompt cache is reused.
|
|
433
|
-
const forkResult = await this.runCompactFork(recentChatMessages, compactPrompt, options.abortSignal);
|
|
437
|
+
const forkResult = await this.runCompactFork(recentChatMessages, compactPrompt, options.abortSignal, this.callbacks?.onCompactionContentUpdate, this.callbacks?.onCompactionReasoningUpdate);
|
|
434
438
|
const summaryContent = forkResult.content;
|
|
435
439
|
const compactTokens = forkResult.usage;
|
|
436
440
|
if (!summaryContent) {
|
|
@@ -452,6 +456,10 @@ export class AIManager {
|
|
|
452
456
|
const enhancedSummary = await this.buildPostCompactContext(formattedSummary);
|
|
453
457
|
// 8. Execute message reconstruction
|
|
454
458
|
await this.messageManager.compactMessagesAndUpdateSession(enhancedSummary, compactUsage);
|
|
459
|
+
// Clear readFileState after compaction (aligned with Claude Code's
|
|
460
|
+
// compact flow): a file_unchanged stub would otherwise reference Read
|
|
461
|
+
// results that were just folded away. Clearing forces a real re-read.
|
|
462
|
+
this.readFileState.clear();
|
|
455
463
|
// Re-add plan mode reminder as persistent meta message after compaction
|
|
456
464
|
const postCompactMode = this.permissionManager?.getCurrentEffectiveMode(this.getModelConfig().permissionMode);
|
|
457
465
|
if (postCompactMode === "plan") {
|
|
@@ -595,6 +603,25 @@ export class AIManager {
|
|
|
595
603
|
// gateway's idle timeout fires (non-streaming waits for the full
|
|
596
604
|
// response, which exceeds the timeout on large contexts).
|
|
597
605
|
stream: true,
|
|
606
|
+
// Surface partial output to the caller (e.g. the compaction loading
|
|
607
|
+
// tail) as it arrives. Reasoning chunks from thinking models go to
|
|
608
|
+
// the reasoning channel when the caller supplies one; otherwise they
|
|
609
|
+
// fall back to the content channel (CLI mixes both).
|
|
610
|
+
onContentUpdate: (content) => {
|
|
611
|
+
if (content.trim()) {
|
|
612
|
+
options.onContentUpdate?.(content);
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
onReasoningUpdate: (reasoning) => {
|
|
616
|
+
if (reasoning.trim()) {
|
|
617
|
+
if (options.onReasoningUpdate) {
|
|
618
|
+
options.onReasoningUpdate(reasoning);
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
options.onContentUpdate?.(reasoning);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
},
|
|
598
625
|
});
|
|
599
626
|
if (result.usage) {
|
|
600
627
|
totalUsage = {
|
|
@@ -647,10 +674,12 @@ export class AIManager {
|
|
|
647
674
|
* Fork-path compaction: deny all tool calls locally (the model is told to
|
|
648
675
|
* summarize, not act) and feed the rejections back for another turn.
|
|
649
676
|
*/
|
|
650
|
-
async runCompactFork(historyMessages, compactPrompt, abortSignal) {
|
|
677
|
+
async runCompactFork(historyMessages, compactPrompt, abortSignal, onContentUpdate, onReasoningUpdate) {
|
|
651
678
|
return this.runForkLoop(historyMessages, compactPrompt, {
|
|
652
679
|
maxTurns: MAX_FORK_TURNS,
|
|
653
680
|
deniedToolMessage: "Tool use is not allowed during compaction",
|
|
681
|
+
onContentUpdate,
|
|
682
|
+
onReasoningUpdate,
|
|
654
683
|
}, abortSignal);
|
|
655
684
|
}
|
|
656
685
|
/**
|
|
@@ -806,8 +835,10 @@ export class AIManager {
|
|
|
806
835
|
}
|
|
807
836
|
};
|
|
808
837
|
// Fire-and-forget: the fork runs to completion in the background; the
|
|
809
|
-
// caller gets the task ID immediately.
|
|
810
|
-
|
|
838
|
+
// caller gets the task ID immediately. Tracked in the async work registry
|
|
839
|
+
// so destroy() drains it before returning (abort via the task's onStop
|
|
840
|
+
// makes the loop settle).
|
|
841
|
+
const forkPromise = (async () => {
|
|
811
842
|
try {
|
|
812
843
|
const result = await this.runForkLoop(historyMessages, prompt, {
|
|
813
844
|
// /subtask aligns with Claude Code's fork subagent
|
|
@@ -854,6 +885,7 @@ export class AIManager {
|
|
|
854
885
|
abortCleanup?.();
|
|
855
886
|
}
|
|
856
887
|
})();
|
|
888
|
+
this.asyncWorkRegistry?.track(forkPromise);
|
|
857
889
|
return taskId;
|
|
858
890
|
}
|
|
859
891
|
/**
|
|
@@ -1904,6 +1936,18 @@ ${question}`;
|
|
|
1904
1936
|
};
|
|
1905
1937
|
// Execute tool
|
|
1906
1938
|
const toolResult = await this.toolManager.execute(functionToolCall.function?.name || "", toolArgs, context);
|
|
1939
|
+
// MCP tools can return images (e.g. Figma screenshots) as in-memory
|
|
1940
|
+
// base64. When this agent's model cannot see them, persist each image to
|
|
1941
|
+
// a temp file so the non-vision path in convertMessagesForAPI can attach
|
|
1942
|
+
// `[Image source: <path>]` metadata and the main model can delegate
|
|
1943
|
+
// recognition to the vision subagent. Vision-capable models keep the
|
|
1944
|
+
// inline base64 — no disk writes.
|
|
1945
|
+
let toolImages = toolResult.images;
|
|
1946
|
+
if (toolImages &&
|
|
1947
|
+
toolImages.length > 0 &&
|
|
1948
|
+
!supportsVision(this.getModelConfig().capabilities)) {
|
|
1949
|
+
toolImages = persistToolImages(toolImages);
|
|
1950
|
+
}
|
|
1907
1951
|
// Build result content, adding truncation warning if JSON was recovered
|
|
1908
1952
|
let toolResultContent = toolResult.content ||
|
|
1909
1953
|
(toolResult.error ? `Error: ${toolResult.error}` : "");
|
|
@@ -1926,7 +1970,7 @@ ${question}`;
|
|
|
1926
1970
|
backgroundedByUser: toolResult.backgroundedByUser,
|
|
1927
1971
|
assistantAutoBackgrounded: toolResult.assistantAutoBackgrounded,
|
|
1928
1972
|
startLineNumber: toolResult.startLineNumber,
|
|
1929
|
-
images:
|
|
1973
|
+
images: toolImages,
|
|
1930
1974
|
timestamp: Date.now(),
|
|
1931
1975
|
});
|
|
1932
1976
|
// Execute PostToolUse hooks after successful tool completion
|
|
@@ -656,7 +656,7 @@ export class McpManager {
|
|
|
656
656
|
else if (c.type === "image" && c.data) {
|
|
657
657
|
images.push({
|
|
658
658
|
data: c.data,
|
|
659
|
-
mediaType: "image/png", //
|
|
659
|
+
mediaType: c.mimeType || "image/png", // MCP standard mimeType, PNG fallback
|
|
660
660
|
});
|
|
661
661
|
}
|
|
662
662
|
else if (c.type === "resource") {
|
|
@@ -222,6 +222,12 @@ export declare class MessageManager {
|
|
|
222
222
|
* Extract file read contents from tool result blocks in a message.
|
|
223
223
|
*/
|
|
224
224
|
private extractFileReadsFromMessage;
|
|
225
|
+
/**
|
|
226
|
+
* Bound recentFileReads growth: evict oldest entries beyond the cache limit
|
|
227
|
+
* and drop content from all but the most recent entries (paths are kept for
|
|
228
|
+
* hasFileBeenRead). Prevents unbounded memory growth in long sessions.
|
|
229
|
+
*/
|
|
230
|
+
private trimRecentFileReads;
|
|
225
231
|
/**
|
|
226
232
|
* Get recent file read contents, sorted by timestamp (newest first).
|
|
227
233
|
* @param maxFiles - Maximum number of files to return
|
|
@@ -5,6 +5,13 @@ import { appendMessages, createSession, generateSessionId, SESSION_DIR, } from "
|
|
|
5
5
|
import { pathEncoder } from "../utils/pathEncoder.js";
|
|
6
6
|
import { estimateTokens } from "../utils/tokenEstimate.js";
|
|
7
7
|
import { READ_TOOL_NAME } from "../constants/tools.js";
|
|
8
|
+
// Cap for recentFileReads: content is only consumed by getRecentFileReads
|
|
9
|
+
// (top 5 for post-compact restoration), so bound both the entry count and the
|
|
10
|
+
// number of entries that keep full content (aligned with Claude Code's
|
|
11
|
+
// FileStateCache 100-entry limit). Paths beyond the content cap are kept so
|
|
12
|
+
// hasFileBeenRead still works for read-before-edit enforcement.
|
|
13
|
+
const RECENT_FILE_READS_CACHE_LIMIT = 100;
|
|
14
|
+
const RECENT_FILE_READS_CONTENT_LIMIT = 10;
|
|
8
15
|
import { logger } from "../utils/globalLogger.js";
|
|
9
16
|
export class MessageManager {
|
|
10
17
|
constructor(container, options) {
|
|
@@ -397,6 +404,8 @@ export class MessageManager {
|
|
|
397
404
|
// Update in-memory state
|
|
398
405
|
this.setMessages(newMessages);
|
|
399
406
|
this.savedMessageCount = newMessages.length;
|
|
407
|
+
// Trim recent file read cache so contents compacted away are released
|
|
408
|
+
this.trimRecentFileReads();
|
|
400
409
|
// Clear and rebuild loaded rule IDs from remaining meta messages
|
|
401
410
|
this.clearLoadedRuleIds();
|
|
402
411
|
this.rebuildLoadedRuleIds();
|
|
@@ -779,10 +788,34 @@ export class MessageManager {
|
|
|
779
788
|
content: block.result,
|
|
780
789
|
timestamp: Date.now(),
|
|
781
790
|
});
|
|
791
|
+
this.trimRecentFileReads();
|
|
782
792
|
}
|
|
783
793
|
}
|
|
784
794
|
}
|
|
785
795
|
}
|
|
796
|
+
/**
|
|
797
|
+
* Bound recentFileReads growth: evict oldest entries beyond the cache limit
|
|
798
|
+
* and drop content from all but the most recent entries (paths are kept for
|
|
799
|
+
* hasFileBeenRead). Prevents unbounded memory growth in long sessions.
|
|
800
|
+
*/
|
|
801
|
+
trimRecentFileReads() {
|
|
802
|
+
if (this.recentFileReads.size > RECENT_FILE_READS_CACHE_LIMIT) {
|
|
803
|
+
const sortedAsc = Array.from(this.recentFileReads.entries()).sort(([, a], [, b]) => a.timestamp - b.timestamp);
|
|
804
|
+
const excess = this.recentFileReads.size - RECENT_FILE_READS_CACHE_LIMIT;
|
|
805
|
+
for (const [path] of sortedAsc.slice(0, excess)) {
|
|
806
|
+
this.recentFileReads.delete(path);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (this.recentFileReads.size > RECENT_FILE_READS_CONTENT_LIMIT) {
|
|
810
|
+
const sortedDesc = Array.from(this.recentFileReads.entries()).sort(([, a], [, b]) => b.timestamp - a.timestamp);
|
|
811
|
+
for (const [path, entry] of sortedDesc.slice(RECENT_FILE_READS_CONTENT_LIMIT)) {
|
|
812
|
+
this.recentFileReads.set(path, {
|
|
813
|
+
content: "",
|
|
814
|
+
timestamp: entry.timestamp,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
786
819
|
/**
|
|
787
820
|
* Get recent file read contents, sorted by timestamp (newest first).
|
|
788
821
|
* @param maxFiles - Maximum number of files to return
|
|
@@ -75,6 +75,7 @@ export declare class SubagentManager {
|
|
|
75
75
|
private stream;
|
|
76
76
|
constructor(container: Container, options: SubagentManagerOptions);
|
|
77
77
|
private get configurationService();
|
|
78
|
+
private get asyncWorkRegistry();
|
|
78
79
|
/**
|
|
79
80
|
* Initialize the SubagentManager by loading and caching configurations
|
|
80
81
|
*/
|
|
@@ -150,6 +151,14 @@ export declare class SubagentManager {
|
|
|
150
151
|
* Clean up completed, errored, or aborted instances
|
|
151
152
|
*/
|
|
152
153
|
cleanupInstance(subagentId: string): void;
|
|
154
|
+
/**
|
|
155
|
+
* Release a subagent instance so its graph (message history, file-read
|
|
156
|
+
* caches, log stream) can be garbage-collected. Besides removing the
|
|
157
|
+
* instance from the map, this drops the background task's onStop closure,
|
|
158
|
+
* which captures the instance and would otherwise keep the whole graph
|
|
159
|
+
* alive for the rest of the session.
|
|
160
|
+
*/
|
|
161
|
+
private releaseInstance;
|
|
153
162
|
/**
|
|
154
163
|
* Get all active instances
|
|
155
164
|
*/
|
|
@@ -23,6 +23,9 @@ export class SubagentManager {
|
|
|
23
23
|
get configurationService() {
|
|
24
24
|
return this.container.get("ConfigurationService");
|
|
25
25
|
}
|
|
26
|
+
get asyncWorkRegistry() {
|
|
27
|
+
return this.container.get("AsyncWorkRegistry");
|
|
28
|
+
}
|
|
26
29
|
/**
|
|
27
30
|
* Initialize the SubagentManager by loading and caching configurations
|
|
28
31
|
*/
|
|
@@ -171,6 +174,16 @@ export class SubagentManager {
|
|
|
171
174
|
// Without this, concurrent background subagents steal sibling completion
|
|
172
175
|
// notifications from the parent queue, causing the main agent to exit early.
|
|
173
176
|
subagentContainer.register("MessageQueue", new MessageQueue());
|
|
177
|
+
// Deliberately shadow the parent ReversionManager with `undefined` so the
|
|
178
|
+
// subagent resolves no reversion manager instead of falling back to the
|
|
179
|
+
// parent's. Subagent Write/Edit must not be recorded into the main agent's
|
|
180
|
+
// file_history buffer — this mirrors Claude Code's explicit isolation for
|
|
181
|
+
// forked agents (`updateFileHistoryState: () => {}` in forkedAgent.ts).
|
|
182
|
+
// Without this, the subagent drains the SHARED parent buffer into its own
|
|
183
|
+
// transient messages (discarded on cleanup), leaving orphaned snapshot
|
|
184
|
+
// files on disk and racing the main agent's own drain. All ReversionManager
|
|
185
|
+
// consumers are truthiness-guarded, so resolving to `undefined` is safe.
|
|
186
|
+
subagentContainer.register("ReversionManager", undefined);
|
|
174
187
|
// Register a modified AgentOptions without onLoadingChange to prevent subagent loading
|
|
175
188
|
// from affecting the parent agent's loading state
|
|
176
189
|
const parentOptions = this.container.get("AgentOptions");
|
|
@@ -321,7 +334,7 @@ export class SubagentManager {
|
|
|
321
334
|
instance.backgroundTaskId = taskId;
|
|
322
335
|
// Execute in background
|
|
323
336
|
// Note: notification enqueueing is handled by internalExecute when instance.backgroundTaskId is set
|
|
324
|
-
(async () => {
|
|
337
|
+
const backgroundPromise = (async () => {
|
|
325
338
|
try {
|
|
326
339
|
const result = await this.internalExecute(instance, prompt, abortSignal);
|
|
327
340
|
const task = backgroundTaskManager?.getTask(taskId);
|
|
@@ -342,13 +355,30 @@ export class SubagentManager {
|
|
|
342
355
|
task.runtime = task.endTime - startTime;
|
|
343
356
|
}
|
|
344
357
|
}
|
|
358
|
+
finally {
|
|
359
|
+
// Free the instance once the background task finishes: without
|
|
360
|
+
// this the instance (message history, caches) would linger in the
|
|
361
|
+
// map for the whole session, and the task's onStop closure would
|
|
362
|
+
// keep it alive even after that.
|
|
363
|
+
this.releaseInstance(instance.subagentId);
|
|
364
|
+
}
|
|
345
365
|
})();
|
|
366
|
+
this.asyncWorkRegistry?.track(backgroundPromise);
|
|
346
367
|
return taskId;
|
|
347
368
|
}
|
|
348
|
-
|
|
369
|
+
const result = await this.internalExecute(instance, prompt, abortSignal);
|
|
370
|
+
// Free the instance once the subagent finishes. Callers that still hold
|
|
371
|
+
// the reference can read its messageManager, but the map entry and the
|
|
372
|
+
// background task's onStop closure are released so the message history
|
|
373
|
+
// and caches can be garbage-collected.
|
|
374
|
+
this.releaseInstance(instance.subagentId);
|
|
375
|
+
return result;
|
|
349
376
|
}
|
|
350
377
|
catch (error) {
|
|
351
378
|
this.updateInstanceStatus(instance.subagentId, "error");
|
|
379
|
+
// Release on failure too so errored instances don't linger for the
|
|
380
|
+
// session (the abort listener already releases on abort).
|
|
381
|
+
this.releaseInstance(instance.subagentId);
|
|
352
382
|
throw error;
|
|
353
383
|
}
|
|
354
384
|
}
|
|
@@ -543,8 +573,37 @@ export class SubagentManager {
|
|
|
543
573
|
(instance.status === "completed" ||
|
|
544
574
|
instance.status === "error" ||
|
|
545
575
|
instance.status === "aborted")) {
|
|
546
|
-
this.
|
|
576
|
+
this.releaseInstance(subagentId);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Release a subagent instance so its graph (message history, file-read
|
|
581
|
+
* caches, log stream) can be garbage-collected. Besides removing the
|
|
582
|
+
* instance from the map, this drops the background task's onStop closure,
|
|
583
|
+
* which captures the instance and would otherwise keep the whole graph
|
|
584
|
+
* alive for the rest of the session.
|
|
585
|
+
*/
|
|
586
|
+
releaseInstance(subagentId) {
|
|
587
|
+
const instance = this.instances.get(subagentId);
|
|
588
|
+
if (!instance) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (instance.backgroundTaskId) {
|
|
592
|
+
const backgroundTaskManager = this.container.has("BackgroundTaskManager")
|
|
593
|
+
? this.container.get("BackgroundTaskManager")
|
|
594
|
+
: undefined;
|
|
595
|
+
const task = backgroundTaskManager?.getTask(instance.backgroundTaskId);
|
|
596
|
+
if (task) {
|
|
597
|
+
task.onStop = undefined;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (instance.logStream && !instance.logStream.writableEnded) {
|
|
601
|
+
// Only force-close a stream that hasn't been ended yet (the completion
|
|
602
|
+
// and error paths already call end(); destroy would drop buffered data).
|
|
603
|
+
instance.logStream.destroy();
|
|
547
604
|
}
|
|
605
|
+
instance.logStream = undefined;
|
|
606
|
+
this.instances.delete(subagentId);
|
|
548
607
|
}
|
|
549
608
|
/**
|
|
550
609
|
* Get all active instances
|