micro-models-agent 0.2.50 → 0.2.52
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/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +87 -303
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/modes/applier.d.ts +43 -0
- package/dist/agent/modes/applier.d.ts.map +1 -0
- package/dist/agent/modes/applier.js +30 -0
- package/dist/agent/modes/applier.js.map +1 -0
- package/dist/agent/modes/registry.d.ts +35 -0
- package/dist/agent/modes/registry.d.ts.map +1 -0
- package/dist/agent/modes/registry.js +169 -0
- package/dist/agent/modes/registry.js.map +1 -0
- package/dist/agent/modes/types.d.ts +54 -0
- package/dist/agent/modes/types.d.ts.map +1 -0
- package/dist/agent/modes/types.js +13 -0
- package/dist/agent/modes/types.js.map +1 -0
- package/dist/agent/react-loop.d.ts +61 -0
- package/dist/agent/react-loop.d.ts.map +1 -0
- package/dist/agent/react-loop.js +119 -0
- package/dist/agent/react-loop.js.map +1 -0
- package/dist/agent/tool-executor.d.ts +44 -0
- package/dist/agent/tool-executor.d.ts.map +1 -0
- package/dist/agent/tool-executor.js +186 -0
- package/dist/agent/tool-executor.js.map +1 -0
- package/dist/config/types.d.ts +5 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/plugins/adapters/hooks-adapter.d.ts +12 -0
- package/dist/plugins/adapters/hooks-adapter.d.ts.map +1 -0
- package/dist/plugins/adapters/hooks-adapter.js +23 -0
- package/dist/plugins/adapters/hooks-adapter.js.map +1 -0
- package/dist/plugins/loader.d.ts +13 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +145 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/manager.d.ts +52 -0
- package/dist/plugins/manager.d.ts.map +1 -0
- package/dist/plugins/manager.js +183 -0
- package/dist/plugins/manager.js.map +1 -0
- package/dist/plugins/types.d.ts +73 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +8 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/repl/commands.d.ts +1 -0
- package/dist/repl/commands.d.ts.map +1 -1
- package/dist/repl/commands.js.map +1 -1
- package/dist/repl/index.d.ts.map +1 -1
- package/dist/repl/index.js +3 -2
- package/dist/repl/index.js.map +1 -1
- package/dist/session/store.d.ts +4 -2
- package/dist/session/store.d.ts.map +1 -1
- package/dist/session/store.js +4 -2
- package/dist/session/store.js.map +1 -1
- package/dist/tools/bash.d.ts +4 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +24 -0
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/fs/copy-file.d.ts +2 -0
- package/dist/tools/fs/copy-file.d.ts.map +1 -1
- package/dist/tools/fs/copy-file.js +7 -0
- package/dist/tools/fs/copy-file.js.map +1 -1
- package/dist/tools/fs/create-dir.d.ts +2 -0
- package/dist/tools/fs/create-dir.d.ts.map +1 -1
- package/dist/tools/fs/create-dir.js +7 -0
- package/dist/tools/fs/create-dir.js.map +1 -1
- package/dist/tools/fs/delete-file.d.ts +2 -0
- package/dist/tools/fs/delete-file.d.ts.map +1 -1
- package/dist/tools/fs/delete-file.js +7 -0
- package/dist/tools/fs/delete-file.js.map +1 -1
- package/dist/tools/fs/edit-file.d.ts +2 -0
- package/dist/tools/fs/edit-file.d.ts.map +1 -1
- package/dist/tools/fs/edit-file.js +7 -0
- package/dist/tools/fs/edit-file.js.map +1 -1
- package/dist/tools/fs/file-info.d.ts +2 -0
- package/dist/tools/fs/file-info.d.ts.map +1 -1
- package/dist/tools/fs/file-info.js +7 -0
- package/dist/tools/fs/file-info.js.map +1 -1
- package/dist/tools/fs/glob-search.d.ts +2 -0
- package/dist/tools/fs/glob-search.d.ts.map +1 -1
- package/dist/tools/fs/glob-search.js +7 -0
- package/dist/tools/fs/glob-search.js.map +1 -1
- package/dist/tools/fs/grep-search.d.ts +2 -0
- package/dist/tools/fs/grep-search.d.ts.map +1 -1
- package/dist/tools/fs/grep-search.js +7 -0
- package/dist/tools/fs/grep-search.js.map +1 -1
- package/dist/tools/fs/list-dir.d.ts +2 -0
- package/dist/tools/fs/list-dir.d.ts.map +1 -1
- package/dist/tools/fs/list-dir.js +7 -0
- package/dist/tools/fs/list-dir.js.map +1 -1
- package/dist/tools/fs/move-file.d.ts +2 -0
- package/dist/tools/fs/move-file.d.ts.map +1 -1
- package/dist/tools/fs/move-file.js +7 -0
- package/dist/tools/fs/move-file.js.map +1 -1
- package/dist/tools/fs/read-file.d.ts +2 -0
- package/dist/tools/fs/read-file.d.ts.map +1 -1
- package/dist/tools/fs/read-file.js +7 -0
- package/dist/tools/fs/read-file.js.map +1 -1
- package/dist/tools/fs/tree.d.ts +2 -0
- package/dist/tools/fs/tree.d.ts.map +1 -1
- package/dist/tools/fs/tree.js +7 -0
- package/dist/tools/fs/tree.js.map +1 -1
- package/dist/tools/fs/write-file.d.ts +2 -0
- package/dist/tools/fs/write-file.d.ts.map +1 -1
- package/dist/tools/fs/write-file.js +7 -0
- package/dist/tools/fs/write-file.js.map +1 -1
- package/dist/tools/native/approve.d.ts +11 -0
- package/dist/tools/native/approve.d.ts.map +1 -0
- package/dist/tools/native/approve.js +39 -0
- package/dist/tools/native/approve.js.map +1 -0
- package/dist/tools/native/plan.d.ts +10 -0
- package/dist/tools/native/plan.d.ts.map +1 -0
- package/dist/tools/native/plan.js +46 -0
- package/dist/tools/native/plan.js.map +1 -0
- package/dist/tools/native/question.d.ts +11 -0
- package/dist/tools/native/question.d.ts.map +1 -0
- package/dist/tools/native/question.js +41 -0
- package/dist/tools/native/question.js.map +1 -0
- package/dist/tools/native/theme.d.ts +10 -0
- package/dist/tools/native/theme.d.ts.map +1 -0
- package/dist/tools/native/theme.js +106 -0
- package/dist/tools/native/theme.js.map +1 -0
- package/dist/tools/native/todo.d.ts +10 -0
- package/dist/tools/native/todo.d.ts.map +1 -0
- package/dist/tools/native/todo.js +58 -0
- package/dist/tools/native/todo.js.map +1 -0
- package/dist/tools/native/user-profile.d.ts +10 -0
- package/dist/tools/native/user-profile.d.ts.map +1 -0
- package/dist/tools/native/user-profile.js +58 -0
- package/dist/tools/native/user-profile.js.map +1 -0
- package/dist/tools/registry.d.ts +11 -10
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +40 -147
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/subagent.d.ts +2 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +7 -4
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/types.d.ts +59 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +2 -0
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +7 -0
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-search.d.ts +2 -0
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +7 -0
- package/dist/tools/web-search.js.map +1 -1
- package/dist/updater.js +1 -1
- package/dist/updater.js.map +1 -1
- package/package.json +1 -1
package/dist/agent/loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAc9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAyGpD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,cAAc,EAC/B,gBAAgB,CAAC,EAAE,WAAW,EAAE,EAChC,QAAQ,CAAC,EAAE,cAAc,EACzB,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,GACtE,OAAO,CAAC,eAAe,CAAC,CAysB1B"}
|
package/dist/agent/loop.js
CHANGED
|
@@ -4,22 +4,42 @@ import { jsonrepair } from 'jsonrepair';
|
|
|
4
4
|
import { parseFileRefs } from '../file-loader.js';
|
|
5
5
|
import { ContextManager } from './context.js';
|
|
6
6
|
import { autoLearnFromError } from './memory.js';
|
|
7
|
-
import {
|
|
7
|
+
import { collectCodingStyle } from './user-profile.js';
|
|
8
|
+
import { ModeRegistry } from './modes/registry.js';
|
|
9
|
+
import { applyMode } from './modes/applier.js';
|
|
8
10
|
import { loadSkill, loadSkillsFromRefs, enforceSkillBudget } from '../skills/loader.js';
|
|
9
11
|
import { runPreToolUseHooks, runPostToolUseHooks } from '../tools/hooks.js';
|
|
10
12
|
import { isMCPCommand, parseMCPCommand, callMCPTool } from '../tools/mcp.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { parseQuestionCommand } from '../tools/question.js';
|
|
14
|
-
import { parseApproveCommand } from '../tools/approve.js';
|
|
13
|
+
import { showPlan, getPlanPendingCount } from '../tools/plan.js';
|
|
14
|
+
import { listTodos } from '../tools/todo.js';
|
|
15
15
|
import { checkFileSyntax } from '../tools/syntax-check.js';
|
|
16
|
-
import {
|
|
17
|
-
import { loadThemeConfig, installTheme, removeTheme } from '../styling/loader.js';
|
|
18
|
-
import { loadConfig, saveConfig } from '../config/config.js';
|
|
16
|
+
import { loadConfig } from '../config/config.js';
|
|
19
17
|
import { buildSystemPrompt } from './system-prompt.js';
|
|
20
18
|
import { logInfo, logWarn, logError, logDebug, logToolCall, setStatusWriter, writeStatus } from '../logger.js';
|
|
21
19
|
import { buildSubagentContext, runIsolatedLoop, extractAllToolCalls, validateToolCall, doChat } from '../tools/subagent.js';
|
|
22
20
|
import { SubagentRenderer } from '../ui/subagent-renderer.js';
|
|
21
|
+
import { ToolExecutor } from './tool-executor.js';
|
|
22
|
+
import { planHandler } from '../tools/native/plan.js';
|
|
23
|
+
import { todoHandler } from '../tools/native/todo.js';
|
|
24
|
+
import { questionHandler } from '../tools/native/question.js';
|
|
25
|
+
import { approveHandler } from '../tools/native/approve.js';
|
|
26
|
+
import { userProfileHandler } from '../tools/native/user-profile.js';
|
|
27
|
+
import { themeHandler } from '../tools/native/theme.js';
|
|
28
|
+
import { bashHandler } from '../tools/bash.js';
|
|
29
|
+
import { readFileHandler } from '../tools/fs/read-file.js';
|
|
30
|
+
import { writeFileHandler } from '../tools/fs/write-file.js';
|
|
31
|
+
import { editFileHandler } from '../tools/fs/edit-file.js';
|
|
32
|
+
import { listDirHandler } from '../tools/fs/list-dir.js';
|
|
33
|
+
import { createDirHandler } from '../tools/fs/create-dir.js';
|
|
34
|
+
import { deleteFileHandler } from '../tools/fs/delete-file.js';
|
|
35
|
+
import { copyFileHandler } from '../tools/fs/copy-file.js';
|
|
36
|
+
import { moveFileHandler } from '../tools/fs/move-file.js';
|
|
37
|
+
import { fileInfoHandler } from '../tools/fs/file-info.js';
|
|
38
|
+
import { globSearchHandler } from '../tools/fs/glob-search.js';
|
|
39
|
+
import { grepSearchHandler } from '../tools/fs/grep-search.js';
|
|
40
|
+
import { treeHandler } from '../tools/fs/tree.js';
|
|
41
|
+
import { webSearchHandler } from '../tools/web-search.js';
|
|
42
|
+
import { webFetchHandler } from '../tools/web-fetch.js';
|
|
23
43
|
/**
|
|
24
44
|
* Умная обрезка tool-результатов для экономии токенов в контексте.
|
|
25
45
|
*
|
|
@@ -123,6 +143,10 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
123
143
|
const { prompt: enrichedPrompt, files } = await parseFileRefs(prompt);
|
|
124
144
|
logDebug('LOOP', `parseFileRefs: ${files.length} files, prompt len: ${enrichedPrompt.length}`);
|
|
125
145
|
const quiet = options?.quiet ?? false;
|
|
146
|
+
// ─── ModeRegistry: конфигурация режима ──
|
|
147
|
+
const modeRegistry = new ModeRegistry();
|
|
148
|
+
const modeConfig = modeRegistry.getOrFallback(options?.mode || 'base');
|
|
149
|
+
const applied = applyMode(modeConfig, { quiet, showThinking: options?.showThinking });
|
|
126
150
|
// Автозагрузка скилов из промпта (если пользователь указал @skill-name)
|
|
127
151
|
const loadedSkills = loadSkillsFromRefs(prompt, process.cwd());
|
|
128
152
|
const loadedSkillNames = new Set(loadedSkills.map(s => s.name));
|
|
@@ -130,17 +154,20 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
130
154
|
logInfo('LOOP', `loaded ${loadedSkills.length} skills: ${loadedSkills.map(s => s.name).join(', ')}`);
|
|
131
155
|
renderer?.writeInfo(` Loaded ${loadedSkills.length} skill(s): ${loadedSkills.map(s => s.name).join(', ')}`);
|
|
132
156
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
157
|
+
// Принудительная загрузка скилов из ModeConfig
|
|
158
|
+
for (const skillName of applied.forcedSkills) {
|
|
159
|
+
if (!loadedSkillNames.has(skillName)) {
|
|
160
|
+
const skill = loadSkill(skillName, process.cwd());
|
|
161
|
+
if (skill) {
|
|
162
|
+
loadedSkills.push(skill);
|
|
163
|
+
loadedSkillNames.add(skillName);
|
|
164
|
+
logInfo('LOOP', `auto-loaded ${skillName} skill for ${modeConfig.id} mode`);
|
|
165
|
+
renderer?.writeInfo(` Auto-loaded @${skillName} skill for ${modeConfig.id} mode`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
logWarn('LOOP', `${skillName} skill not found for ${modeConfig.id} mode`);
|
|
169
|
+
renderer?.writeWarning(` Warning: @${skillName} skill not found (required for ${modeConfig.id} mode)`);
|
|
170
|
+
}
|
|
144
171
|
}
|
|
145
172
|
}
|
|
146
173
|
// Ограничение количества скилов в base mode
|
|
@@ -149,10 +176,10 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
149
176
|
? (config.agent.maxSkillsPro ?? 0)
|
|
150
177
|
: (config.agent.maxSkillsBase ?? 2);
|
|
151
178
|
if (maxSkills > 0 && loadedSkills.length > maxSkills) {
|
|
152
|
-
const budget =
|
|
179
|
+
const budget = applied.skillTokenBudget;
|
|
153
180
|
const { allowed, dropped } = enforceSkillBudget(loadedSkills, budget);
|
|
154
181
|
if (dropped.length > 0) {
|
|
155
|
-
renderer?.writeWarning(` Skill limit (${maxSkills} max in ${
|
|
182
|
+
renderer?.writeWarning(` Skill limit (${maxSkills} max in ${modeConfig.id} mode): dropped ${dropped.join(', ')}`);
|
|
156
183
|
logInfo('LOOP', `skill limit: dropped ${dropped.join(', ')} (budget: ${budget} tokens)`);
|
|
157
184
|
loadedSkills.length = 0;
|
|
158
185
|
loadedSkills.push(...allowed);
|
|
@@ -187,6 +214,16 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
187
214
|
let aborted = false;
|
|
188
215
|
let continuationNudges = 0; // счётчик подсказок "продолжай работать"
|
|
189
216
|
let directWriteStreak = 0; // количество прямых write_file без subagent
|
|
217
|
+
// ─── ToolExecutor: единый диспетчер инструментов ──
|
|
218
|
+
const toolExecutor = new ToolExecutor();
|
|
219
|
+
toolExecutor.registerAll([
|
|
220
|
+
planHandler, todoHandler, questionHandler, approveHandler,
|
|
221
|
+
userProfileHandler, themeHandler, bashHandler,
|
|
222
|
+
readFileHandler, writeFileHandler, editFileHandler, listDirHandler,
|
|
223
|
+
createDirHandler, deleteFileHandler, copyFileHandler, moveFileHandler,
|
|
224
|
+
fileInfoHandler, globSearchHandler, grepSearchHandler, treeHandler,
|
|
225
|
+
webSearchHandler, webFetchHandler,
|
|
226
|
+
]);
|
|
190
227
|
// Reset abort signal for this run
|
|
191
228
|
if (renderer?.abortSignal === undefined) {
|
|
192
229
|
// Renderer doesn't support abort — fine
|
|
@@ -539,191 +576,8 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
539
576
|
let toolOutput = '';
|
|
540
577
|
let toolExitCode = 0;
|
|
541
578
|
const toolStart = performance.now();
|
|
542
|
-
// ───
|
|
543
|
-
if (parsed.name === '
|
|
544
|
-
const planName = String(parsed.arguments.TaskName || parsed.arguments.name || parsed.arguments.plan || 'Untitled');
|
|
545
|
-
const stepsRaw = parsed.arguments.Steps || parsed.arguments.steps || [];
|
|
546
|
-
const steps = Array.isArray(stepsRaw) ? stepsRaw : String(stepsRaw).split('\n').filter((s) => s.trim());
|
|
547
|
-
if (!quiet) {
|
|
548
|
-
renderer?.writeBracketTag('plan', 'create');
|
|
549
|
-
}
|
|
550
|
-
toolOutput = createPlan(planName, steps);
|
|
551
|
-
// If plan has 3+ steps, suggest using subagent for isolation
|
|
552
|
-
if (steps.length >= 3) {
|
|
553
|
-
toolOutput += `\n\nIMPORTANT: Plan has ${steps.length} steps. Use subagent tool for EACH step to work in isolation:\n`;
|
|
554
|
-
for (let i = 0; i < steps.length; i++) {
|
|
555
|
-
toolOutput += `subagent("Step ${i + 1}: ${steps[i]}. List existing files and packages.")\n`;
|
|
556
|
-
}
|
|
557
|
-
toolOutput += `This prevents context loss. Do NOT use write_file directly — delegate to subagents.`;
|
|
558
|
-
}
|
|
559
|
-
if (!quiet && toolOutput) {
|
|
560
|
-
renderer?.writeLine(toolOutput);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
else if (parsed.name === 'todo') {
|
|
564
|
-
const action = String(parsed.arguments.action || 'list');
|
|
565
|
-
const text = String(parsed.arguments.text || '');
|
|
566
|
-
if (!quiet) {
|
|
567
|
-
renderer?.writeBracketTag('todo', action);
|
|
568
|
-
}
|
|
569
|
-
switch (action) {
|
|
570
|
-
case 'add':
|
|
571
|
-
toolOutput = addTodo(text);
|
|
572
|
-
break;
|
|
573
|
-
case 'done':
|
|
574
|
-
toolOutput = doneTodo(text);
|
|
575
|
-
break;
|
|
576
|
-
case 'remove':
|
|
577
|
-
case 'rm':
|
|
578
|
-
toolOutput = removeTodo(text);
|
|
579
|
-
break;
|
|
580
|
-
case 'list':
|
|
581
|
-
case 'ls':
|
|
582
|
-
toolOutput = listTodos();
|
|
583
|
-
break;
|
|
584
|
-
default:
|
|
585
|
-
toolOutput = 'Unknown todo action. Use: add, done, remove, list';
|
|
586
|
-
toolExitCode = 1;
|
|
587
|
-
}
|
|
588
|
-
if (!quiet && toolExitCode === 0 && toolOutput) {
|
|
589
|
-
renderer?.writeLine(toolOutput);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
else if (parsed.name === 'question') {
|
|
593
|
-
const question = String(parsed.arguments.question || parsed.arguments.text || '');
|
|
594
|
-
const choicesArr = Array.isArray(parsed.arguments.choices) ? parsed.arguments.choices : [];
|
|
595
|
-
if (!quiet) {
|
|
596
|
-
renderer?.writeBracketTag('question', question);
|
|
597
|
-
}
|
|
598
|
-
messages.push({
|
|
599
|
-
role: 'tool',
|
|
600
|
-
content: `Question asked: ${question}`,
|
|
601
|
-
tool_call_id: toolId,
|
|
602
|
-
});
|
|
603
|
-
return {
|
|
604
|
-
messages,
|
|
605
|
-
questionPending: { question, choices: choicesArr.length > 0 ? choicesArr : undefined },
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
else if (parsed.name === 'approve') {
|
|
609
|
-
const action = String(parsed.arguments.action || parsed.arguments.text || '');
|
|
610
|
-
if (!quiet) {
|
|
611
|
-
renderer?.writeBracketTag('approve', action);
|
|
612
|
-
}
|
|
613
|
-
messages.push({
|
|
614
|
-
role: 'tool',
|
|
615
|
-
content: `Approval requested: ${action}`,
|
|
616
|
-
tool_call_id: toolId,
|
|
617
|
-
});
|
|
618
|
-
return {
|
|
619
|
-
messages,
|
|
620
|
-
questionPending: { question: `Approve: ${action}?`, choices: ['Yes', 'No'] },
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
else if (parsed.name === 'user_profile') {
|
|
624
|
-
const action = String(parsed.arguments.action || 'show');
|
|
625
|
-
if (!quiet) {
|
|
626
|
-
renderer?.writeBracketTag('user_profile', action);
|
|
627
|
-
}
|
|
628
|
-
switch (action) {
|
|
629
|
-
case 'show': {
|
|
630
|
-
const content = loadProfile();
|
|
631
|
-
toolOutput = content || 'User profile is empty.';
|
|
632
|
-
break;
|
|
633
|
-
}
|
|
634
|
-
case 'add_note': {
|
|
635
|
-
const note = String(parsed.arguments.note || parsed.arguments.text || '');
|
|
636
|
-
const section = String(parsed.arguments.section || 'Notes');
|
|
637
|
-
if (!note) {
|
|
638
|
-
toolOutput = 'Usage: user_profile action=add_note section=Notes note=...';
|
|
639
|
-
toolExitCode = 1;
|
|
640
|
-
break;
|
|
641
|
-
}
|
|
642
|
-
appendNote(note, section);
|
|
643
|
-
toolOutput = `Note added to "${section}" section.`;
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
default:
|
|
647
|
-
toolOutput = 'Unknown user_profile action. Use: show, add_note';
|
|
648
|
-
toolExitCode = 1;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
else if (parsed.name === 'theme') {
|
|
652
|
-
const action = String(parsed.arguments.action || 'list');
|
|
653
|
-
const name = String(parsed.arguments.name || '');
|
|
654
|
-
const url = String(parsed.arguments.url || '');
|
|
655
|
-
if (!quiet) {
|
|
656
|
-
renderer?.writeBracketTag('theme', action);
|
|
657
|
-
}
|
|
658
|
-
switch (action) {
|
|
659
|
-
case 'list': {
|
|
660
|
-
const allThemes = listThemes();
|
|
661
|
-
const current = loadThemeConfig().name;
|
|
662
|
-
toolOutput = `Available themes (${allThemes.length}):\n`;
|
|
663
|
-
for (const t of allThemes) {
|
|
664
|
-
toolOutput += ` ${t}${t === current ? ' (active)' : ''}\n`;
|
|
665
|
-
}
|
|
666
|
-
break;
|
|
667
|
-
}
|
|
668
|
-
case 'show': {
|
|
669
|
-
const config = loadThemeConfig();
|
|
670
|
-
toolOutput = `Theme: ${config.name}\nColors: ${JSON.stringify(config.colors, null, 2)}\nIcons: ${JSON.stringify(config.icons, null, 2)}`;
|
|
671
|
-
break;
|
|
672
|
-
}
|
|
673
|
-
case 'switch': {
|
|
674
|
-
const allThemes = listThemes();
|
|
675
|
-
if (!allThemes.includes(name)) {
|
|
676
|
-
toolOutput = `Unknown theme "${name}". Available: ${allThemes.join(', ')}`;
|
|
677
|
-
toolExitCode = 1;
|
|
678
|
-
break;
|
|
679
|
-
}
|
|
680
|
-
const appConfig = loadConfig();
|
|
681
|
-
appConfig.theme = name;
|
|
682
|
-
saveConfig(appConfig);
|
|
683
|
-
switchTheme(name);
|
|
684
|
-
try {
|
|
685
|
-
const { resetInkTheme } = await import('../ui/ink/theme.js');
|
|
686
|
-
resetInkTheme();
|
|
687
|
-
}
|
|
688
|
-
catch { /* not in ink mode */ }
|
|
689
|
-
toolOutput = `Theme switched to "${name}"`;
|
|
690
|
-
break;
|
|
691
|
-
}
|
|
692
|
-
case 'install': {
|
|
693
|
-
if (!name || !url) {
|
|
694
|
-
toolOutput = 'Usage: theme install <name> <url>';
|
|
695
|
-
toolExitCode = 1;
|
|
696
|
-
break;
|
|
697
|
-
}
|
|
698
|
-
try {
|
|
699
|
-
const resp = await fetch(url);
|
|
700
|
-
const text = await resp.text();
|
|
701
|
-
JSON.parse(text);
|
|
702
|
-
installTheme(name, text);
|
|
703
|
-
toolOutput = `Theme "${name}" installed from ${url}`;
|
|
704
|
-
}
|
|
705
|
-
catch (e) {
|
|
706
|
-
toolOutput = `Failed to install theme: ${e.message}`;
|
|
707
|
-
toolExitCode = 1;
|
|
708
|
-
}
|
|
709
|
-
break;
|
|
710
|
-
}
|
|
711
|
-
case 'remove': {
|
|
712
|
-
if (!removeTheme(name)) {
|
|
713
|
-
toolOutput = `Theme "${name}" not found`;
|
|
714
|
-
toolExitCode = 1;
|
|
715
|
-
break;
|
|
716
|
-
}
|
|
717
|
-
toolOutput = `Theme "${name}" removed`;
|
|
718
|
-
break;
|
|
719
|
-
}
|
|
720
|
-
default:
|
|
721
|
-
toolOutput = 'Unknown theme action. Use: list, show, switch, install, remove';
|
|
722
|
-
toolExitCode = 1;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
else if (parsed.name === 'subagent') {
|
|
726
|
-
// ─── Субагент: изолированный контекст для задачи ──
|
|
579
|
+
// ─── Субагент: изолированный контекст для задачи ──
|
|
580
|
+
if (parsed.name === 'subagent') {
|
|
727
581
|
const task = String(parsed.arguments.task || '');
|
|
728
582
|
if (!task) {
|
|
729
583
|
toolOutput = 'Error: task is required';
|
|
@@ -732,10 +586,9 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
732
586
|
else {
|
|
733
587
|
const autoContext = buildSubagentContext(process.cwd());
|
|
734
588
|
logInfo('AGENT', `spawning subagent | task: "${task.slice(0, 150)}"`);
|
|
735
|
-
// Создаём буферизированный рендерер для субагента
|
|
736
589
|
const subRenderer = new SubagentRenderer();
|
|
737
590
|
try {
|
|
738
|
-
const childResult = await runIsolatedLoop(provider, registry, task, autoContext, process.cwd(), quiet, subRenderer);
|
|
591
|
+
const childResult = await runIsolatedLoop(provider, registry, task, autoContext, process.cwd(), quiet, subRenderer, toolExecutor);
|
|
739
592
|
toolOutput = childResult.summary;
|
|
740
593
|
logInfo('AGENT', `subagent returned | files: ${childResult.createdFiles.length} | turns: ${childResult.turns} | errors: ${childResult.errors.length}`);
|
|
741
594
|
if (childResult.createdFiles.length > 0) {
|
|
@@ -744,7 +597,6 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
744
597
|
createdFiles.push(f);
|
|
745
598
|
}
|
|
746
599
|
}
|
|
747
|
-
// Отправляем буфер субагента в UI для отображения в ToolCallBlock
|
|
748
600
|
const buf = subRenderer.getBuffer();
|
|
749
601
|
if (!quiet && renderer?.writeToolBuffer) {
|
|
750
602
|
renderer.writeToolBuffer({ lines: buf.lines, toolCalls: buf.toolCalls });
|
|
@@ -760,92 +612,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
760
612
|
if (!quiet) {
|
|
761
613
|
renderer?.writeToolResult(subTime, toolExitCode, toolOutput.slice(0, 300));
|
|
762
614
|
}
|
|
763
|
-
|
|
764
|
-
else if (isBash && cmd.startsWith('plan ')) {
|
|
765
|
-
const planArgs = cmd.slice(5).trim();
|
|
766
|
-
const { action, params } = parsePlanCommand(planArgs);
|
|
767
|
-
if (!quiet) {
|
|
768
|
-
renderer?.writeBracketTag('plan', action);
|
|
769
|
-
}
|
|
770
|
-
switch (action) {
|
|
771
|
-
case 'create':
|
|
772
|
-
toolOutput = createPlan(params[0] || 'Untitled Plan', params.slice(1));
|
|
773
|
-
break;
|
|
774
|
-
case 'done':
|
|
775
|
-
toolOutput = updatePlanStep(parseInt(params[0]) || 0, 'done');
|
|
776
|
-
break;
|
|
777
|
-
case 'progress':
|
|
778
|
-
toolOutput = updatePlanStep(parseInt(params[0]) || 0, 'in_progress');
|
|
779
|
-
break;
|
|
780
|
-
case 'show':
|
|
781
|
-
toolOutput = showPlan();
|
|
782
|
-
break;
|
|
783
|
-
case 'clear':
|
|
784
|
-
toolOutput = clearPlan();
|
|
785
|
-
break;
|
|
786
|
-
default:
|
|
787
|
-
toolOutput = 'Unknown plan action. Use: create, done, progress, show, clear';
|
|
788
|
-
toolExitCode = 1;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
else if (isBash && cmd.startsWith('todo ')) {
|
|
792
|
-
const todoArgs = cmd.slice(5).trim();
|
|
793
|
-
const { action, params } = parseTodoCommand(todoArgs);
|
|
794
|
-
if (!quiet) {
|
|
795
|
-
renderer?.writeBracketTag('todo', action);
|
|
796
|
-
}
|
|
797
|
-
switch (action) {
|
|
798
|
-
case 'add':
|
|
799
|
-
toolOutput = addTodo(params);
|
|
800
|
-
break;
|
|
801
|
-
case 'done':
|
|
802
|
-
toolOutput = doneTodo(params);
|
|
803
|
-
break;
|
|
804
|
-
case 'remove':
|
|
805
|
-
case 'rm':
|
|
806
|
-
toolOutput = removeTodo(params);
|
|
807
|
-
break;
|
|
808
|
-
case 'list':
|
|
809
|
-
case 'ls':
|
|
810
|
-
toolOutput = listTodos();
|
|
811
|
-
break;
|
|
812
|
-
default:
|
|
813
|
-
toolOutput = 'Unknown todo action. Use: add, done, remove, list';
|
|
814
|
-
toolExitCode = 1;
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
else if (isBash && cmd.startsWith('question ')) {
|
|
818
|
-
const qArgs = cmd.slice(9).trim();
|
|
819
|
-
const { question, choices } = parseQuestionCommand(qArgs);
|
|
820
|
-
if (!quiet) {
|
|
821
|
-
renderer?.writeBracketTag('question', question);
|
|
822
|
-
}
|
|
823
|
-
messages.push({
|
|
824
|
-
role: 'tool',
|
|
825
|
-
content: `Question asked: ${question}`,
|
|
826
|
-
tool_call_id: toolId,
|
|
827
|
-
});
|
|
828
|
-
return {
|
|
829
|
-
messages,
|
|
830
|
-
questionPending: { question, choices: choices.length > 0 ? choices : undefined },
|
|
831
|
-
};
|
|
832
|
-
}
|
|
833
|
-
else if (isBash && cmd.startsWith('approve ')) {
|
|
834
|
-
const approveArgs = cmd.slice(8).trim();
|
|
835
|
-
const action = parseApproveCommand(approveArgs);
|
|
836
|
-
if (!quiet) {
|
|
837
|
-
renderer?.writeBracketTag('approve', action);
|
|
838
|
-
}
|
|
839
|
-
messages.push({
|
|
840
|
-
role: 'tool',
|
|
841
|
-
content: `Approval requested: ${action}`,
|
|
842
|
-
tool_call_id: toolId,
|
|
843
|
-
});
|
|
844
|
-
return {
|
|
845
|
-
messages,
|
|
846
|
-
questionPending: { question: `Approve: ${action}?`, choices: ['Yes', 'No'] },
|
|
847
|
-
};
|
|
848
|
-
// ─── MCP-инструменты ────────────────────────────────
|
|
615
|
+
// ─── MCP: внешние tool-серверы ────────────────────────
|
|
849
616
|
}
|
|
850
617
|
else if (isBash && isMCPCommand(cmd)) {
|
|
851
618
|
const mcpCmd = parseMCPCommand(cmd);
|
|
@@ -859,18 +626,35 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
859
626
|
toolOutput = 'Invalid MCP command. Format: mcp <server> <tool> [args_json]';
|
|
860
627
|
toolExitCode = 1;
|
|
861
628
|
}
|
|
862
|
-
// ───
|
|
863
|
-
}
|
|
864
|
-
else if (!isBash) {
|
|
865
|
-
const result = await registry.execute({ id: toolId, name: parsed.name, arguments: parsed.arguments });
|
|
866
|
-
toolOutput = result.stdout || result.stderr || '(no output)';
|
|
867
|
-
toolExitCode = result.exitCode;
|
|
868
|
-
// ─── Bash: выполнение shell-команд ──────────────────
|
|
629
|
+
// ─── Все инструменты через ToolExecutor ──────────────
|
|
869
630
|
}
|
|
870
631
|
else {
|
|
871
|
-
|
|
632
|
+
// Определяем инструмент и аргументы для executor'а
|
|
633
|
+
let executorName = parsed.name;
|
|
634
|
+
let executorArgs = parsed.arguments;
|
|
635
|
+
// Bash-команды
|
|
636
|
+
if (isBash) {
|
|
637
|
+
executorName = 'bash';
|
|
638
|
+
executorArgs = { cmd, timeout: parsed.arguments?.timeout ?? 120 };
|
|
639
|
+
}
|
|
640
|
+
const hookCmd = isBash ? cmd : `${parsed.name} ${JSON.stringify(parsed.arguments)}`;
|
|
641
|
+
const executorCall = { id: toolId, name: executorName, arguments: executorArgs };
|
|
642
|
+
const toolCtx = { cwd: process.cwd(), renderer, quiet };
|
|
643
|
+
const { result, elapsed } = await toolExecutor.execute(executorCall, toolCtx, hookCmd);
|
|
872
644
|
toolOutput = result.stdout || result.stderr || '(no output)';
|
|
873
645
|
toolExitCode = result.exitCode;
|
|
646
|
+
// Обработка question/approve: приостанавливаем цикл
|
|
647
|
+
if (result.questionPending) {
|
|
648
|
+
messages.push({
|
|
649
|
+
role: 'tool',
|
|
650
|
+
content: toolOutput,
|
|
651
|
+
tool_call_id: toolId,
|
|
652
|
+
});
|
|
653
|
+
return {
|
|
654
|
+
messages,
|
|
655
|
+
questionPending: { question: toolOutput, choices: undefined },
|
|
656
|
+
};
|
|
657
|
+
}
|
|
874
658
|
}
|
|
875
659
|
const toolTime = ((performance.now() - toolStart) / 1000).toFixed(1);
|
|
876
660
|
logToolCall(parsed.name, preview, toolExitCode !== 0 ? `exit:${toolExitCode} ${toolOutput.slice(0, 100)}` : `ok (${toolTime}s)`);
|