minovative-mind-cli 2.12.0 → 2.13.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/README.md +13 -0
- package/dist/commands/chat.js +3 -1
- package/dist/services/agent/slashCommands.js +4 -2
- package/dist/services/agent-tools.js +4 -3
- package/dist/services/ai.js +4 -0
- package/dist/services/ideOptimization.d.ts +15 -0
- package/dist/services/ideOptimization.js +169 -0
- package/dist/services/orchestration/messageBus.d.ts +81 -41
- package/dist/services/orchestration/messageBus.js +242 -98
- package/dist/services/orchestration/orchestrator.d.ts +6 -6
- package/dist/services/orchestration/orchestrator.js +32 -21
- package/dist/services/orchestration/scopedTools.d.ts +7 -1
- package/dist/services/orchestration/scopedTools.js +45 -9
- package/dist/services/orchestration/subAgent.d.ts +19 -17
- package/dist/services/orchestration/subAgent.js +100 -83
- package/dist/utils/fuzzyMatch.d.ts +51 -21
- package/dist/utils/fuzzyMatch.js +37 -122
- package/dist/utils/projectStorage.js +10 -5
- package/dist/utils/systemPrompts.d.ts +2 -2
- package/dist/utils/systemPrompts.js +4 -4
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -41,7 +41,7 @@ export function getScopedToolDeclarations() {
|
|
|
41
41
|
},
|
|
42
42
|
content: {
|
|
43
43
|
type: 'STRING',
|
|
44
|
-
description: 'The semantic intent or message content',
|
|
44
|
+
description: 'The semantic intent or message content (concise summary, max 500 chars)',
|
|
45
45
|
},
|
|
46
46
|
affectedFiles: {
|
|
47
47
|
type: 'ARRAY',
|
|
@@ -58,10 +58,23 @@ export function getScopedToolDeclarations() {
|
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
name: 'read_messages',
|
|
61
|
-
description: 'Fetch
|
|
61
|
+
description: 'Fetch intelligent updates from other agents on the bus. Can filter by file, agent ID, or signal type.',
|
|
62
62
|
parameters: {
|
|
63
63
|
type: 'OBJECT',
|
|
64
|
-
properties: {
|
|
64
|
+
properties: {
|
|
65
|
+
file: {
|
|
66
|
+
type: 'STRING',
|
|
67
|
+
description: 'Optional: Filter updates touching a specific file path (e.g., "src/services/auth.ts")',
|
|
68
|
+
},
|
|
69
|
+
fromAgent: {
|
|
70
|
+
type: 'STRING',
|
|
71
|
+
description: 'Optional: Filter updates from a specific collaborator agent ID',
|
|
72
|
+
},
|
|
73
|
+
type: {
|
|
74
|
+
type: 'STRING',
|
|
75
|
+
description: 'Optional: Filter by signal type ("completion", "warning", "discovery", "request")',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
65
78
|
},
|
|
66
79
|
},
|
|
67
80
|
];
|
|
@@ -80,8 +93,9 @@ export function getScopedToolDeclarations() {
|
|
|
80
93
|
* @param locks - Shared file lock registry instance
|
|
81
94
|
* @param onProgress - Callback to notify parent of sub-agent progress
|
|
82
95
|
* @param options - Optional sub-path auto-focus and override configuration
|
|
96
|
+
* @param taskScope - Optional task scope metadata (targetFiles, dependsOn)
|
|
83
97
|
*/
|
|
84
|
-
export async function executeScopedTool(name, args, workspaceRoot, agentId, bus, locks, onProgress, options) {
|
|
98
|
+
export async function executeScopedTool(name, args, workspaceRoot, agentId, bus, locks, onProgress, options, taskScope) {
|
|
85
99
|
// Update heartbeat so the orchestrator knows we are making progress
|
|
86
100
|
onProgress();
|
|
87
101
|
const timestamp = Date.now();
|
|
@@ -89,23 +103,27 @@ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus,
|
|
|
89
103
|
let targetDesc = 'workspace';
|
|
90
104
|
let status = 'success';
|
|
91
105
|
let result = null;
|
|
92
|
-
let resultSummary
|
|
106
|
+
let resultSummary;
|
|
93
107
|
try {
|
|
94
108
|
// ─── Orchestration-Specific Tools ────────────────────────────────
|
|
95
109
|
if (name === 'post_message') {
|
|
96
110
|
const type = args.type;
|
|
111
|
+
let content = String(args.content || '');
|
|
112
|
+
if (content.length > 500) {
|
|
113
|
+
content = content.substring(0, 500) + '... (truncated)';
|
|
114
|
+
}
|
|
97
115
|
const signal = {
|
|
98
116
|
type,
|
|
99
117
|
timestamp,
|
|
100
118
|
fromAgent: agentId,
|
|
101
|
-
content
|
|
119
|
+
content,
|
|
102
120
|
};
|
|
103
121
|
if (type === 'discovery')
|
|
104
122
|
signal.affectedFiles = args.affectedFiles || [];
|
|
105
123
|
if (type === 'request')
|
|
106
124
|
signal.toAgent = args.toAgent || 'all';
|
|
107
125
|
if (type === 'completion')
|
|
108
|
-
signal.summary =
|
|
126
|
+
signal.summary = content; // map completion summary
|
|
109
127
|
const accepted = bus.postSignal(signal);
|
|
110
128
|
if (!accepted) {
|
|
111
129
|
return { error: `Message bus rejected signal. You have hit the max signal cap (${MessageBus.MAX_SIGNALS_PER_AGENT}). Stop sending signals.` };
|
|
@@ -113,7 +131,18 @@ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus,
|
|
|
113
131
|
return { success: true, message: 'Message posted to bus.' };
|
|
114
132
|
}
|
|
115
133
|
if (name === 'read_messages') {
|
|
116
|
-
const unread = bus.
|
|
134
|
+
const unread = bus.queryBus({
|
|
135
|
+
agentId,
|
|
136
|
+
file: args.file,
|
|
137
|
+
fromAgent: args.fromAgent,
|
|
138
|
+
type: args.type,
|
|
139
|
+
onlyMutations: true,
|
|
140
|
+
targetFiles: taskScope?.targetFiles,
|
|
141
|
+
dependsOn: taskScope?.dependsOn,
|
|
142
|
+
});
|
|
143
|
+
if (unread.activities.length === 0 && unread.signals.length === 0) {
|
|
144
|
+
return { status: 'No new peer messages or workspace mutations.' };
|
|
145
|
+
}
|
|
117
146
|
return {
|
|
118
147
|
activityLogs: MessageBus.formatActivityEntries(unread.activities),
|
|
119
148
|
semanticSignals: MessageBus.formatSignals(unread.signals),
|
|
@@ -157,9 +186,16 @@ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus,
|
|
|
157
186
|
// If we got a file lock and had context from a previous writer, we should
|
|
158
187
|
// inject that diff info back into the agent's tool return payload so it
|
|
159
188
|
// knows someone else changed the file while it was queued.
|
|
160
|
-
if (diffContext && typeof result === 'object') {
|
|
189
|
+
if (diffContext && typeof result === 'object' && result !== null) {
|
|
161
190
|
result._orchestrationWarning = `NOTICE: Another agent modified this file while you were waiting. Diff context: ${diffContext}`;
|
|
162
191
|
}
|
|
192
|
+
// In-band relevance-gated urgent signals (warnings, targeted requests, or upstream completions)
|
|
193
|
+
if (typeof result === 'object' && result !== null && typeof bus?.peekUrgentSignals === 'function') {
|
|
194
|
+
const urgentSignals = bus.peekUrgentSignals(agentId, taskScope);
|
|
195
|
+
if (urgentSignals && urgentSignals.length > 0) {
|
|
196
|
+
result._orchestrationNotice = MessageBus.formatSignals(urgentSignals);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
163
199
|
}
|
|
164
200
|
finally {
|
|
165
201
|
// Release the lock if we grabbed one
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @
|
|
2
|
+
* @file Sub-Agent Runner for Orchestration.
|
|
3
3
|
*
|
|
4
4
|
* Implements the lifecycle, health monitoring, and tool-loop execution for a single
|
|
5
5
|
* parallelized sub-agent.
|
|
6
6
|
*/
|
|
7
7
|
import { MessageBus } from './messageBus.js';
|
|
8
8
|
import { FileLockRegistry } from './fileLockRegistry.js';
|
|
9
|
+
import { TaskScope } from './scopedTools.js';
|
|
9
10
|
/**
|
|
10
11
|
* Scopes workspace file blocks within a context string by replacing full implementation
|
|
11
12
|
* bodies with compact AST declaration outlines (types, classes, function signatures),
|
|
@@ -53,31 +54,20 @@ export declare class SubAgentRunner {
|
|
|
53
54
|
private readonly globalContext;
|
|
54
55
|
private readonly onProgress?;
|
|
55
56
|
private readonly onTool?;
|
|
57
|
+
private readonly taskScope?;
|
|
58
|
+
/** Max time without a tool call or response before the agent is considered stalled */
|
|
59
|
+
static readonly STALL_TIMEOUT_MS = 300000;
|
|
56
60
|
private chat;
|
|
57
|
-
private lastHeartbeat;
|
|
58
61
|
private creditsUsed;
|
|
59
62
|
private inputTokens;
|
|
63
|
+
private lastHeartbeat;
|
|
60
64
|
private outputTokens;
|
|
61
|
-
/** Max time without a tool call or response before the agent is considered stalled */
|
|
62
|
-
static readonly STALL_TIMEOUT_MS = 300000;
|
|
63
|
-
constructor(taskId: string, intent: string, workspaceRoot: string, bus: MessageBus, locks: FileLockRegistry, globalContext: string, onProgress?: ((msg: string) => void) | undefined, onTool?: ((msg: string) => void) | undefined);
|
|
64
|
-
/**
|
|
65
|
-
* Constructs the base system instruction for this specific agent,
|
|
66
|
-
* injecting scoped AST outlines for referenced workspace files to eliminate
|
|
67
|
-
* multi-megabyte context bloat during parallel sub-agent execution.
|
|
68
|
-
*/
|
|
69
|
-
private buildSystemInstruction;
|
|
70
65
|
/**
|
|
71
66
|
* Updates the heartbeat. Passed to `executeScopedTool` to ensure the agent
|
|
72
67
|
* isn't marked as stalled while performing long-running commands.
|
|
73
68
|
*/
|
|
74
69
|
private pingHeartbeat;
|
|
75
|
-
|
|
76
|
-
* Executes the sub-agent with a health monitor harness.
|
|
77
|
-
* Runs the tool loop until the model stops returning function calls,
|
|
78
|
-
* an error occurs, or the stall timeout is hit.
|
|
79
|
-
*/
|
|
80
|
-
execute(signal: AbortSignal): Promise<SubAgentResult>;
|
|
70
|
+
constructor(taskId: string, intent: string, workspaceRoot: string, bus: MessageBus, locks: FileLockRegistry, globalContext: string, onProgress?: ((msg: string) => void) | undefined, onTool?: ((msg: string) => void) | undefined, taskScope?: TaskScope | undefined);
|
|
81
71
|
/**
|
|
82
72
|
* Compacts older tool responses in the chat session history into concise references
|
|
83
73
|
* (e.g. `[Read X lines from file.ts - tool execution completed]`), drastically reducing
|
|
@@ -87,6 +77,18 @@ export declare class SubAgentRunner {
|
|
|
87
77
|
* @param turnsToKeep Number of recent turns to preserve in full (default: 1).
|
|
88
78
|
*/
|
|
89
79
|
compactOlderToolResponses(turnsToKeep?: number): void;
|
|
80
|
+
/**
|
|
81
|
+
* Executes the sub-agent with a health monitor harness.
|
|
82
|
+
* Runs the tool loop until the model stops returning function calls,
|
|
83
|
+
* an error occurs, or the stall timeout is hit.
|
|
84
|
+
*/
|
|
85
|
+
execute(signal: AbortSignal): Promise<SubAgentResult>;
|
|
86
|
+
/**
|
|
87
|
+
* Constructs the base system instruction for this specific agent,
|
|
88
|
+
* injecting scoped AST outlines for referenced workspace files to eliminate
|
|
89
|
+
* multi-megabyte context bloat during parallel sub-agent execution.
|
|
90
|
+
*/
|
|
91
|
+
private buildSystemInstruction;
|
|
90
92
|
/**
|
|
91
93
|
* Accumulates token usage from the chat session.
|
|
92
94
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @
|
|
2
|
+
* @file Sub-Agent Runner for Orchestration.
|
|
3
3
|
*
|
|
4
4
|
* Implements the lifecycle, health monitoring, and tool-loop execution for a single
|
|
5
5
|
* parallelized sub-agent.
|
|
@@ -64,6 +64,12 @@ function findMatchingFunctionCall(modelEntry, toolName, partIndex) {
|
|
|
64
64
|
* @returns Compacted tool execution reference string.
|
|
65
65
|
*/
|
|
66
66
|
export function createCompactToolReference(toolName, output, args) {
|
|
67
|
+
if (toolName === 'read_messages') {
|
|
68
|
+
return '[read_messages - peer activity reviewed]';
|
|
69
|
+
}
|
|
70
|
+
if (toolName === 'post_message') {
|
|
71
|
+
return '[post_message - coordination signal posted]';
|
|
72
|
+
}
|
|
67
73
|
if (typeof output !== 'string') {
|
|
68
74
|
return `[${toolName} tool execution completed]`;
|
|
69
75
|
}
|
|
@@ -73,8 +79,8 @@ export function createCompactToolReference(toolName, output, args) {
|
|
|
73
79
|
}
|
|
74
80
|
const lineCount = output.split('\n').length;
|
|
75
81
|
if (toolName === 'read_file') {
|
|
76
|
-
|
|
77
|
-
return
|
|
82
|
+
// Preserve full workspace_file content so the sub-agent retains source code in working memory
|
|
83
|
+
return output;
|
|
78
84
|
}
|
|
79
85
|
if (toolName === 'grep_search' || toolName === 'search_codebase') {
|
|
80
86
|
const pattern = args?.pattern ? `for "${args.pattern}"` : '';
|
|
@@ -126,14 +132,25 @@ export class SubAgentRunner {
|
|
|
126
132
|
globalContext;
|
|
127
133
|
onProgress;
|
|
128
134
|
onTool;
|
|
135
|
+
taskScope;
|
|
136
|
+
/** Max time without a tool call or response before the agent is considered stalled */
|
|
137
|
+
static STALL_TIMEOUT_MS = 300_000;
|
|
129
138
|
chat;
|
|
130
|
-
lastHeartbeat = Date.now();
|
|
131
139
|
creditsUsed = 0;
|
|
132
140
|
inputTokens = 0;
|
|
141
|
+
lastHeartbeat = Date.now();
|
|
133
142
|
outputTokens = 0;
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Updates the heartbeat. Passed to `executeScopedTool` to ensure the agent
|
|
145
|
+
* isn't marked as stalled while performing long-running commands.
|
|
146
|
+
*/
|
|
147
|
+
pingHeartbeat = (msg) => {
|
|
148
|
+
this.lastHeartbeat = Date.now();
|
|
149
|
+
if (msg && this.onProgress) {
|
|
150
|
+
this.onProgress(msg);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
constructor(taskId, intent, workspaceRoot, bus, locks, globalContext, onProgress, onTool, taskScope) {
|
|
137
154
|
this.taskId = taskId;
|
|
138
155
|
this.intent = intent;
|
|
139
156
|
this.workspaceRoot = workspaceRoot;
|
|
@@ -142,6 +159,7 @@ export class SubAgentRunner {
|
|
|
142
159
|
this.globalContext = globalContext;
|
|
143
160
|
this.onProgress = onProgress;
|
|
144
161
|
this.onTool = onTool;
|
|
162
|
+
this.taskScope = taskScope;
|
|
145
163
|
let model = getGlobalActiveModel();
|
|
146
164
|
if (model === GEMINI_MODELS.AUTO)
|
|
147
165
|
model = GEMINI_MODELS.FLASH;
|
|
@@ -154,44 +172,56 @@ export class SubAgentRunner {
|
|
|
154
172
|
});
|
|
155
173
|
}
|
|
156
174
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return (`<identity>\n` +
|
|
164
|
-
`You are an autonomous Senior software developer sub-agent executing a specific portion of a larger task.\n` +
|
|
165
|
-
`</identity>\n\n` +
|
|
166
|
-
`<task_info>\n` +
|
|
167
|
-
`Your task ID is: ${this.taskId}\n` +
|
|
168
|
-
`Your objective: ${this.intent}\n` +
|
|
169
|
-
`</task_info>\n\n` +
|
|
170
|
-
`<reference_context>\n` +
|
|
171
|
-
`DO NOT IMPLEMENT THIS FULL REQUEST. THIS IS JUST FOR CONTEXT.\n\n` +
|
|
172
|
-
`${scopedContext}\n` +
|
|
173
|
-
`</reference_context>\n\n` +
|
|
174
|
-
`<critical_guidelines>\n` +
|
|
175
|
-
`1. You are ONE worker in a team. Focus EXCLUSIVELY on your specific objective: "${this.intent}".\n` +
|
|
176
|
-
`2. DO NOT attempt to fulfill the entire original user request in the reference context. Other agents handle other tasks.\n` +
|
|
177
|
-
`3. EXECUTION WORKFLOW:\n` +
|
|
178
|
-
` - Step 1 (Investigate): Use 'grep_search', 'read_file', or 'list_directory' to inspect the codebase as needed.\n` +
|
|
179
|
-
` - Step 2 (Execute): Use 'modify_file' or 'write_file' to implement the required changes.\n` +
|
|
180
|
-
` - Step 3 (Verify): Use 'run_debug_script', 'run_fuzz_probe', 'check_heap_delta', 'check_behavioral_drift', or 'run_command' to test your changes if necessary.\n` +
|
|
181
|
-
` - Step 4 (Conclude): Once your specific objective is fully met, stop calling tools and return a text summary of your changes.\n` +
|
|
182
|
-
`4. Coordinate: Use 'read_messages' to check for updates from other agents. Use 'post_message' if you discover breaking changes affecting others.\n` +
|
|
183
|
-
`</critical_guidelines>`);
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Updates the heartbeat. Passed to `executeScopedTool` to ensure the agent
|
|
187
|
-
* isn't marked as stalled while performing long-running commands.
|
|
175
|
+
* Compacts older tool responses in the chat session history into concise references
|
|
176
|
+
* (e.g. `[Read X lines from file.ts - tool execution completed]`), drastically reducing
|
|
177
|
+
* context window bloat across multi-turn sub-agent executions while preserving
|
|
178
|
+
* Gemini's functionCall <-> functionResponse protocol.
|
|
179
|
+
*
|
|
180
|
+
* @param turnsToKeep Number of recent turns to preserve in full (default: 1).
|
|
188
181
|
*/
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
182
|
+
compactOlderToolResponses(turnsToKeep = 1) {
|
|
183
|
+
const rawHistory = this.chat.getRawHistory();
|
|
184
|
+
// 1 turn = 1 user Content + 1 model Content pair (2 entries)
|
|
185
|
+
const cutoffIndex = Math.max(0, rawHistory.length - turnsToKeep * 2);
|
|
186
|
+
if (cutoffIndex <= 0)
|
|
187
|
+
return;
|
|
188
|
+
for (let i = 0; i < cutoffIndex; i++) {
|
|
189
|
+
const entry = rawHistory[i];
|
|
190
|
+
if (entry && entry.role === 'user' && Array.isArray(entry.parts)) {
|
|
191
|
+
const prevModelEntry = i > 0 ? rawHistory[i - 1] : undefined;
|
|
192
|
+
let funcRespIdx = 0;
|
|
193
|
+
for (const part of entry.parts) {
|
|
194
|
+
if (part && typeof part === 'object' && 'functionResponse' in part && part.functionResponse) {
|
|
195
|
+
const funcResp = part.functionResponse;
|
|
196
|
+
const toolName = funcResp.name || 'tool';
|
|
197
|
+
const matchingCall = findMatchingFunctionCall(prevModelEntry, toolName, funcRespIdx);
|
|
198
|
+
const args = matchingCall?.args;
|
|
199
|
+
if (funcResp.response && typeof funcResp.response === 'object') {
|
|
200
|
+
const respObj = funcResp.response;
|
|
201
|
+
if (toolName === 'read_messages') {
|
|
202
|
+
funcResp.response = { output: '[read_messages - peer activity reviewed]' };
|
|
203
|
+
}
|
|
204
|
+
else if (toolName === 'post_message') {
|
|
205
|
+
funcResp.response = { output: '[post_message - coordination signal posted]' };
|
|
206
|
+
}
|
|
207
|
+
else if (typeof respObj.output === 'string') {
|
|
208
|
+
respObj.output = createCompactToolReference(toolName, respObj.output, args);
|
|
209
|
+
}
|
|
210
|
+
else if (typeof respObj.result === 'string') {
|
|
211
|
+
respObj.result = createCompactToolReference(toolName, respObj.result, args);
|
|
212
|
+
}
|
|
213
|
+
else if (typeof respObj.output === 'object' && respObj.output !== null) {
|
|
214
|
+
respObj.output = createCompactToolReference(toolName, respObj.output, args);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
funcRespIdx++;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
193
221
|
}
|
|
194
|
-
|
|
222
|
+
// Also trigger proxy chat session pruning to collapse any remaining long fields
|
|
223
|
+
this.chat.pruneToolOutputHistory();
|
|
224
|
+
}
|
|
195
225
|
/**
|
|
196
226
|
* Executes the sub-agent with a health monitor harness.
|
|
197
227
|
* Runs the tool loop until the model stops returning function calls,
|
|
@@ -243,7 +273,7 @@ export class SubAgentRunner {
|
|
|
243
273
|
debugLog(`SubAgent [${this.taskId}]: Executing tool ${call.name}`);
|
|
244
274
|
let responseData;
|
|
245
275
|
try {
|
|
246
|
-
responseData = await executeScopedTool(call.name, call.args, this.workspaceRoot, this.taskId, this.bus, this.locks, this.pingHeartbeat);
|
|
276
|
+
responseData = await executeScopedTool(call.name, call.args, this.workspaceRoot, this.taskId, this.bus, this.locks, this.pingHeartbeat, undefined, this.taskScope);
|
|
247
277
|
}
|
|
248
278
|
catch (err) {
|
|
249
279
|
responseData = { error: err.message || String(err) };
|
|
@@ -309,46 +339,33 @@ export class SubAgentRunner {
|
|
|
309
339
|
});
|
|
310
340
|
}
|
|
311
341
|
/**
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* context
|
|
315
|
-
* Gemini's functionCall <-> functionResponse protocol.
|
|
316
|
-
*
|
|
317
|
-
* @param turnsToKeep Number of recent turns to preserve in full (default: 1).
|
|
342
|
+
* Constructs the base system instruction for this specific agent,
|
|
343
|
+
* injecting scoped AST outlines for referenced workspace files to eliminate
|
|
344
|
+
* multi-megabyte context bloat during parallel sub-agent execution.
|
|
318
345
|
*/
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
respObj.result = createCompactToolReference(toolName, respObj.result, args);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
funcRespIdx++;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
// Also trigger proxy chat session pruning to collapse any remaining long fields
|
|
351
|
-
this.chat.pruneToolOutputHistory();
|
|
346
|
+
buildSystemInstruction() {
|
|
347
|
+
const scopedContext = scopeContext(this.globalContext);
|
|
348
|
+
return (`<identity>\n` +
|
|
349
|
+
`You are an autonomous Senior software developer sub-agent executing a specific portion of a larger task.\n` +
|
|
350
|
+
`</identity>\n\n` +
|
|
351
|
+
`<task_info>\n` +
|
|
352
|
+
`Your task ID is: ${this.taskId}\n` +
|
|
353
|
+
`Your objective: ${this.intent}\n` +
|
|
354
|
+
`</task_info>\n\n` +
|
|
355
|
+
`<reference_context>\n` +
|
|
356
|
+
`DO NOT IMPLEMENT THIS FULL REQUEST. THIS IS JUST FOR CONTEXT.\n\n` +
|
|
357
|
+
`${scopedContext}\n` +
|
|
358
|
+
`</reference_context>\n\n` +
|
|
359
|
+
`<critical_guidelines>\n` +
|
|
360
|
+
`1. You are ONE worker in a team. Focus EXCLUSIVELY on your specific objective: "${this.intent}".\n` +
|
|
361
|
+
`2. DO NOT attempt to fulfill the entire original user request in the reference context. Other agents handle other tasks.\n` +
|
|
362
|
+
`3. EXECUTION WORKFLOW:\n` +
|
|
363
|
+
` - Step 1 (Investigate): Use 'grep_search', 'read_file', or 'list_directory' to inspect the codebase as needed.\n` +
|
|
364
|
+
` - Step 2 (Execute): Use 'modify_file' or 'write_file' to implement the required changes.\n` +
|
|
365
|
+
` - Step 3 (Verify): Use 'run_debug_script', 'run_fuzz_probe', 'check_heap_delta', 'check_behavioral_drift', or 'run_command' to test your changes if necessary.\n` +
|
|
366
|
+
` - Step 4 (Conclude): Once your specific objective is fully met, stop calling tools and return a text summary of your changes.\n` +
|
|
367
|
+
`4. Coordinate: Use 'read_messages' to check for updates from other agents. Use 'post_message' if you discover breaking changes affecting others.\n` +
|
|
368
|
+
`</critical_guidelines>`);
|
|
352
369
|
}
|
|
353
370
|
/**
|
|
354
371
|
* Accumulates token usage from the chat session.
|
|
@@ -1,53 +1,83 @@
|
|
|
1
1
|
import { ValidationResult } from './localSyntaxValidator.js';
|
|
2
|
+
/**
|
|
3
|
+
* Represents the result of a successful fuzzy match operation within a source file.
|
|
4
|
+
*/
|
|
2
5
|
export interface MatchResult {
|
|
6
|
+
/** The starting character byte offset of the matched region in the file content. */
|
|
3
7
|
start: number;
|
|
8
|
+
/** The ending character byte offset of the matched region in the file content. */
|
|
4
9
|
end: number;
|
|
10
|
+
/** The specific matching strategy that successfully resolved the snippet (e.g. 'Exact Match', 'Whitespace-Normalized Match', or Levenshtein details). */
|
|
5
11
|
strategy: string;
|
|
6
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Represents a discrete search-and-replace block within a multi-block patch operation.
|
|
15
|
+
*/
|
|
7
16
|
export interface PatchBlock {
|
|
17
|
+
/** The target search snippet to locate within the source file. */
|
|
8
18
|
search: string;
|
|
19
|
+
/** The replacement content to substitute into the matched region. */
|
|
9
20
|
replace: string;
|
|
10
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Represents the comprehensive result of applying one or more patch blocks to a file.
|
|
24
|
+
*/
|
|
11
25
|
export interface PatchApplicationResult {
|
|
26
|
+
/** Indicates whether all patch blocks were successfully applied and optional syntax validation passed. */
|
|
12
27
|
success: boolean;
|
|
28
|
+
/** The final transformed file content after patch application. */
|
|
13
29
|
content: string;
|
|
30
|
+
/** The count of successfully applied patch blocks. */
|
|
14
31
|
appliedBlocks: number;
|
|
32
|
+
/** The count of patch blocks that failed matching or application. */
|
|
15
33
|
failedBlocks: number;
|
|
34
|
+
/** A collection of error or diagnostic messages encountered during patching. */
|
|
16
35
|
errors: string[];
|
|
36
|
+
/** Optional syntax validation results from {@link localValidate} when syntax validation is enabled. */
|
|
17
37
|
syntaxValidation?: ValidationResult;
|
|
18
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Configuration options governing patch application behavior.
|
|
41
|
+
*/
|
|
19
42
|
export interface ApplyPatchOptions {
|
|
43
|
+
/** Whether to perform local AST-aware syntax validation on the resulting content. Defaults to false. */
|
|
20
44
|
validateSyntax?: boolean;
|
|
21
45
|
}
|
|
22
46
|
/**
|
|
23
|
-
*
|
|
24
|
-
* and
|
|
47
|
+
* Normalizes whitespace in a text snippet by trimming each line, collapsing internal
|
|
48
|
+
* runs of spaces and tabs into a single space, and standardizing line endings.
|
|
49
|
+
*
|
|
50
|
+
* @param text - The raw input text string to normalize.
|
|
51
|
+
* @returns The whitespace-normalized string.
|
|
25
52
|
*/
|
|
26
53
|
export declare function normalizeWhitespace(text: string): string;
|
|
27
54
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
55
|
+
* Calculates the normalized Levenshtein similarity ratio between two strings on a scale from 0.0 to 1.0.
|
|
56
|
+
*
|
|
57
|
+
* @param a - The first string to compare.
|
|
58
|
+
* @param b - The second string to compare.
|
|
59
|
+
* @returns A similarity score between 0.0 (completely dissimilar) and 1.0 (identical).
|
|
30
60
|
*/
|
|
31
61
|
export declare function levenshteinSimilarity(a: string, b: string): number;
|
|
32
62
|
/**
|
|
33
|
-
* Finds the best match for `searchContent` inside `fileContent
|
|
34
|
-
*
|
|
35
|
-
*
|
|
63
|
+
* Finds the best match for a `searchContent` snippet inside `fileContent` using a robust 3-tier
|
|
64
|
+
* deterministic strategy pipeline:
|
|
65
|
+
*
|
|
66
|
+
* 1. **Exact Match**: Checks for literal substring occurrence. Enforces strict uniqueness (fails if ambiguous).
|
|
67
|
+
* 2. **Whitespace-Normalized Match**: Compares line-by-line after stripping leading/trailing whitespace and collapsing interior spaces. Enforces uniqueness.
|
|
68
|
+
* 3. **Levenshtein Similarity Matching**: Evaluates sliding window similarity across file regions with adaptive confidence thresholds (0.92 for short snippets, 0.88 for longer content) and distinct region grouping to disambiguate repetitive code blocks.
|
|
69
|
+
*
|
|
70
|
+
* @param fileContent - The full content of the source file being searched.
|
|
71
|
+
* @param searchContent - The search snippet to locate within the file.
|
|
72
|
+
* @returns A {@link MatchResult} containing start/end offsets and strategy metadata, or `null` if no unique or confident match could be determined.
|
|
36
73
|
*/
|
|
37
74
|
export declare function findBestMatch(fileContent: string, searchContent: string): MatchResult | null;
|
|
38
|
-
export declare function applyMatch(fileContent: string, match: MatchResult, replaceContent: string): string;
|
|
39
75
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* >>>>> REPLACE
|
|
76
|
+
* Applies a specific match result substitution to source file content.
|
|
77
|
+
*
|
|
78
|
+
* @param fileContent - The original source file content.
|
|
79
|
+
* @param match - The {@link MatchResult} specifying character offsets where the replacement occurs.
|
|
80
|
+
* @param replaceContent - The replacement string to substitute into the matched region.
|
|
81
|
+
* @returns The transformed file content string.
|
|
47
82
|
*/
|
|
48
|
-
export declare function
|
|
49
|
-
/**
|
|
50
|
-
* Applies a sequence of search/replace patch blocks to file content
|
|
51
|
-
* using fuzzy matching, and validates local syntax on the result.
|
|
52
|
-
*/
|
|
53
|
-
export declare function applyPatch(fileContent: string, patchTextOrBlocks: string | PatchBlock[], filePath?: string, options?: ApplyPatchOptions): PatchApplicationResult;
|
|
83
|
+
export declare function applyMatch(fileContent: string, match: MatchResult, replaceContent: string): string;
|