micro-models-agent 0.2.44 → 0.2.45
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/context.d.ts +39 -0
- package/dist/agent/context.d.ts.map +1 -1
- package/dist/agent/context.js +52 -10
- package/dist/agent/context.js.map +1 -1
- package/dist/agent/loop.d.ts +27 -0
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +79 -16
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/memory.d.ts +46 -0
- package/dist/agent/memory.d.ts.map +1 -1
- package/dist/agent/memory.js +67 -3
- package/dist/agent/memory.js.map +1 -1
- package/dist/agent/system-prompt.d.ts +20 -0
- package/dist/agent/system-prompt.d.ts.map +1 -1
- package/dist/agent/system-prompt.js +50 -0
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/user-profile.d.ts +21 -0
- package/dist/agent/user-profile.d.ts.map +1 -1
- package/dist/agent/user-profile.js +43 -1
- package/dist/agent/user-profile.js.map +1 -1
- package/dist/cli/config.d.ts +10 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +10 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/model.d.ts +10 -0
- package/dist/cli/model.d.ts.map +1 -1
- package/dist/cli/model.js +10 -0
- package/dist/cli/model.js.map +1 -1
- package/dist/cli/provider.d.ts +9 -0
- package/dist/cli/provider.d.ts.map +1 -1
- package/dist/cli/provider.js +9 -0
- package/dist/cli/provider.js.map +1 -1
- package/dist/cli/update.d.ts +9 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +9 -0
- package/dist/cli/update.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/config.d.ts +6 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +22 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +18 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +11 -0
- package/dist/config/types.js.map +1 -1
- package/dist/file-loader.d.ts +8 -0
- package/dist/file-loader.d.ts.map +1 -1
- package/dist/file-loader.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/indexer/cache.d.ts.map +1 -1
- package/dist/indexer/cache.js +12 -0
- package/dist/indexer/cache.js.map +1 -1
- package/dist/indexer/code-index.d.ts.map +1 -1
- package/dist/indexer/code-index.js +17 -0
- package/dist/indexer/code-index.js.map +1 -1
- package/dist/indexer/project-map.d.ts.map +1 -1
- package/dist/indexer/project-map.js +17 -0
- package/dist/indexer/project-map.js.map +1 -1
- package/dist/llm/openai-compat.d.ts.map +1 -1
- package/dist/llm/openai-compat.js.map +1 -1
- package/dist/llm/provider.d.ts +15 -0
- package/dist/llm/provider.d.ts.map +1 -1
- package/dist/llm/provider.js +11 -0
- package/dist/llm/provider.js.map +1 -1
- package/dist/llm/token-counter.d.ts +8 -0
- package/dist/llm/token-counter.d.ts.map +1 -1
- package/dist/llm/token-counter.js +8 -0
- package/dist/llm/token-counter.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +12 -0
- package/dist/logger.js.map +1 -1
- package/dist/repl/commands.d.ts +18 -0
- package/dist/repl/commands.d.ts.map +1 -1
- package/dist/repl/commands.js +2 -0
- package/dist/repl/commands.js.map +1 -1
- package/dist/repl/history.d.ts +9 -0
- package/dist/repl/history.d.ts.map +1 -1
- package/dist/repl/history.js +9 -0
- package/dist/repl/history.js.map +1 -1
- package/dist/repl/index.d.ts +4 -0
- package/dist/repl/index.d.ts.map +1 -1
- package/dist/repl/index.js +17 -0
- package/dist/repl/index.js.map +1 -1
- package/dist/session/store.d.ts +12 -0
- package/dist/session/store.d.ts.map +1 -1
- package/dist/session/store.js +30 -0
- package/dist/session/store.js.map +1 -1
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +13 -0
- package/dist/skills/loader.js.map +1 -1
- package/dist/styling/engine.d.ts +1 -0
- package/dist/styling/engine.d.ts.map +1 -1
- package/dist/styling/engine.js +10 -0
- package/dist/styling/engine.js.map +1 -1
- package/dist/styling/index.d.ts +5 -0
- package/dist/styling/index.d.ts.map +1 -1
- package/dist/styling/index.js +16 -0
- package/dist/styling/index.js.map +1 -1
- package/dist/styling/loader.d.ts.map +1 -1
- package/dist/styling/loader.js +11 -0
- package/dist/styling/loader.js.map +1 -1
- package/dist/styling/types.d.ts +14 -0
- package/dist/styling/types.d.ts.map +1 -1
- package/dist/styling/types.js +10 -0
- package/dist/styling/types.js.map +1 -1
- package/dist/tools/approve.d.ts +14 -0
- package/dist/tools/approve.d.ts.map +1 -1
- package/dist/tools/approve.js +13 -0
- package/dist/tools/approve.js.map +1 -1
- package/dist/tools/bash.d.ts +13 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +3 -1
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/fs/copy-file.d.ts +6 -0
- package/dist/tools/fs/copy-file.d.ts.map +1 -1
- package/dist/tools/fs/copy-file.js +6 -0
- package/dist/tools/fs/copy-file.js.map +1 -1
- package/dist/tools/fs/create-dir.d.ts +6 -0
- package/dist/tools/fs/create-dir.d.ts.map +1 -1
- package/dist/tools/fs/create-dir.js +6 -0
- package/dist/tools/fs/create-dir.js.map +1 -1
- package/dist/tools/fs/delete-file.d.ts +6 -0
- package/dist/tools/fs/delete-file.d.ts.map +1 -1
- package/dist/tools/fs/delete-file.js +6 -0
- package/dist/tools/fs/delete-file.js.map +1 -1
- package/dist/tools/fs/edit-file.d.ts +8 -0
- package/dist/tools/fs/edit-file.d.ts.map +1 -1
- package/dist/tools/fs/edit-file.js +8 -0
- package/dist/tools/fs/edit-file.js.map +1 -1
- package/dist/tools/fs/file-info.d.ts +6 -0
- package/dist/tools/fs/file-info.d.ts.map +1 -1
- package/dist/tools/fs/file-info.js +6 -0
- package/dist/tools/fs/file-info.js.map +1 -1
- package/dist/tools/fs/glob-search.d.ts +6 -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 +7 -0
- package/dist/tools/fs/grep-search.d.ts.map +1 -1
- package/dist/tools/fs/grep-search.js +8 -0
- package/dist/tools/fs/grep-search.js.map +1 -1
- package/dist/tools/fs/list-dir.d.ts +6 -0
- package/dist/tools/fs/list-dir.d.ts.map +1 -1
- package/dist/tools/fs/list-dir.js +6 -0
- package/dist/tools/fs/list-dir.js.map +1 -1
- package/dist/tools/fs/move-file.d.ts +6 -0
- package/dist/tools/fs/move-file.d.ts.map +1 -1
- package/dist/tools/fs/move-file.js +6 -0
- package/dist/tools/fs/move-file.js.map +1 -1
- package/dist/tools/fs/path-utils.d.ts +21 -0
- package/dist/tools/fs/path-utils.d.ts.map +1 -1
- package/dist/tools/fs/path-utils.js +22 -0
- package/dist/tools/fs/path-utils.js.map +1 -1
- package/dist/tools/fs/read-file.d.ts +6 -0
- package/dist/tools/fs/read-file.d.ts.map +1 -1
- package/dist/tools/fs/read-file.js +6 -0
- package/dist/tools/fs/read-file.js.map +1 -1
- package/dist/tools/fs/tree.d.ts +6 -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/types.d.ts +7 -0
- package/dist/tools/fs/types.d.ts.map +1 -1
- package/dist/tools/fs/types.js +4 -2
- package/dist/tools/fs/types.js.map +1 -1
- package/dist/tools/fs/write-file.d.ts +7 -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/hooks.d.ts +27 -0
- package/dist/tools/hooks.d.ts.map +1 -1
- package/dist/tools/hooks.js +6 -0
- package/dist/tools/hooks.js.map +1 -1
- package/dist/tools/mcp.d.ts +14 -0
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mcp.js +3 -0
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/plan.d.ts +7 -0
- package/dist/tools/plan.d.ts.map +1 -1
- package/dist/tools/plan.js +19 -0
- package/dist/tools/plan.js.map +1 -1
- package/dist/tools/question.d.ts +16 -0
- package/dist/tools/question.d.ts.map +1 -1
- package/dist/tools/question.js +8 -1
- package/dist/tools/question.js.map +1 -1
- package/dist/tools/registry.d.ts +9 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +26 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/sanitizer.d.ts +23 -0
- package/dist/tools/sanitizer.d.ts.map +1 -1
- package/dist/tools/sanitizer.js +27 -0
- package/dist/tools/sanitizer.js.map +1 -1
- package/dist/tools/subagent.d.ts +59 -0
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +46 -2
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/syntax-check.d.ts +16 -0
- package/dist/tools/syntax-check.d.ts.map +1 -1
- package/dist/tools/syntax-check.js.map +1 -1
- package/dist/tools/todo.d.ts +7 -0
- package/dist/tools/todo.d.ts.map +1 -1
- package/dist/tools/todo.js +17 -0
- package/dist/tools/todo.js.map +1 -1
- package/dist/tools/web-fetch.d.ts +8 -0
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +8 -0
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-search.d.ts +8 -0
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +8 -0
- package/dist/tools/web-search.js.map +1 -1
- package/dist/ui/components/status-bar.d.ts +8 -0
- package/dist/ui/components/status-bar.d.ts.map +1 -1
- package/dist/ui/components/status-bar.js +8 -0
- package/dist/ui/components/status-bar.js.map +1 -1
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/ink/components/App.d.ts +12 -0
- package/dist/ui/ink/components/App.d.ts.map +1 -1
- package/dist/ui/ink/components/App.js.map +1 -1
- package/dist/ui/ink/components/MessageBlock.d.ts +12 -0
- package/dist/ui/ink/components/MessageBlock.d.ts.map +1 -1
- package/dist/ui/ink/components/MessageBlock.js.map +1 -1
- package/dist/ui/ink/components/PromptInput.d.ts +11 -0
- package/dist/ui/ink/components/PromptInput.d.ts.map +1 -1
- package/dist/ui/ink/components/PromptInput.js +1 -0
- package/dist/ui/ink/components/PromptInput.js.map +1 -1
- package/dist/ui/ink/components/ToolCallBlock.d.ts +12 -0
- package/dist/ui/ink/components/ToolCallBlock.d.ts.map +1 -1
- package/dist/ui/ink/components/ToolCallBlock.js.map +1 -1
- package/dist/ui/ink/renderer.d.ts +12 -0
- package/dist/ui/ink/renderer.d.ts.map +1 -1
- package/dist/ui/ink/renderer.js +1 -0
- package/dist/ui/ink/renderer.js.map +1 -1
- package/dist/ui/ink/theme.d.ts +8 -0
- package/dist/ui/ink/theme.d.ts.map +1 -1
- package/dist/ui/ink/theme.js.map +1 -1
- package/dist/ui/readline-renderer.d.ts.map +1 -1
- package/dist/ui/readline-renderer.js +11 -0
- package/dist/ui/readline-renderer.js.map +1 -1
- package/dist/ui/renderer.d.ts +16 -0
- package/dist/ui/renderer.d.ts.map +1 -1
- package/dist/ui/renderer.js +14 -0
- package/dist/ui/renderer.js.map +1 -1
- package/dist/updater.d.ts.map +1 -1
- package/dist/updater.js +9 -1
- package/dist/updater.js.map +1 -1
- package/package.json +1 -1
package/dist/agent/context.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ChatMessage } from '../llm/provider.js';
|
|
2
|
+
/**
|
|
3
|
+
* Менеджер контекста — управляет историей сообщений и компактификацией.
|
|
4
|
+
* Создаётся один раз на сессию агента.
|
|
5
|
+
*/
|
|
2
6
|
export declare class ContextManager {
|
|
3
7
|
private contextWindow;
|
|
4
8
|
private compactThreshold;
|
|
@@ -6,13 +10,48 @@ export declare class ContextManager {
|
|
|
6
10
|
private compacted;
|
|
7
11
|
private _totalTokens;
|
|
8
12
|
private _history;
|
|
13
|
+
/**
|
|
14
|
+
* @param contextWindow - размер контекстного окна модели (в токенах)
|
|
15
|
+
* @param compactThreshold - порог компактификации (по умолчанию 85%)
|
|
16
|
+
*/
|
|
9
17
|
constructor(contextWindow: number, compactThreshold?: number);
|
|
10
18
|
get totalTokens(): number;
|
|
11
19
|
get usagePercent(): number;
|
|
12
20
|
get needsCompact(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Оценивает важность сообщения для компактификации.
|
|
23
|
+
*
|
|
24
|
+
* Алгоритм:
|
|
25
|
+
* - System: 100 (всегда сохраняется отдельно)
|
|
26
|
+
* - Tool с ошибкой: 90 (важно для диагностики)
|
|
27
|
+
* - User с "важно"/"critical": 85
|
|
28
|
+
* - Tool с успехом: 70
|
|
29
|
+
* - Assistant с ошибкой/решением: 70-75
|
|
30
|
+
* - User-вопрос: 65
|
|
31
|
+
* - Обычный user/assistant: 40-60
|
|
32
|
+
* + Recency bonus: (index/total) * 20
|
|
33
|
+
*/
|
|
13
34
|
private scoreMessage;
|
|
35
|
+
/**
|
|
36
|
+
* Обновляет подсчёт токенов для всех сообщений.
|
|
37
|
+
* Использует WeakMap для кэширования — повторный подсчёт не выполняется.
|
|
38
|
+
*/
|
|
14
39
|
updateTokens(messages: ChatMessage[]): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Компактификация истории сообщений.
|
|
42
|
+
*
|
|
43
|
+
* Стратегия:
|
|
44
|
+
* 1. Сохраняем system prompt (всегда первый)
|
|
45
|
+
* 2. Сохраняем первое user-сообщение (контекст задачи)
|
|
46
|
+
* 3. Остальные сообщения оцениваем по score
|
|
47
|
+
* 4. Сохраняем топ-6 по score + последние 6 по позиции
|
|
48
|
+
* 5. Остальные суммируем в "[Ранее в разговоре (сжато):]"
|
|
49
|
+
*
|
|
50
|
+
* @param messages - полная история сообщений
|
|
51
|
+
* @returns Сжатая история
|
|
52
|
+
*/
|
|
15
53
|
compact(messages: ChatMessage[]): ChatMessage[];
|
|
54
|
+
/** Форматирует строку использования контекста для отображения в UI */
|
|
16
55
|
formatUsage(): string;
|
|
17
56
|
}
|
|
18
57
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/agent/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/agent/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA6BtD;;;GAGG;AACH,qBAAa,cAAc;IAWvB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,gBAAgB;IAX1B,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAA0C;IAE1D;;;OAGG;gBAEO,aAAa,EAAE,MAAM,EACrB,gBAAgB,GAAE,MAAa;IAGzC,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,YAAY;IA+BpB;;;OAGG;IACG,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB1D;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE;IAiF/C,sEAAsE;IACtE,WAAW,IAAI,MAAM;CAiBtB"}
|
package/dist/agent/context.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { countTokens } from '../llm/token-counter.js';
|
|
2
2
|
import { getTheme } from '../styling/index.js';
|
|
3
3
|
import { logInfo, logDebug } from '../logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Менеджер контекста — управляет историей сообщений и компактификацией.
|
|
6
|
+
* Создаётся один раз на сессию агента.
|
|
7
|
+
*/
|
|
4
8
|
export class ContextManager {
|
|
5
9
|
contextWindow;
|
|
6
10
|
compactThreshold;
|
|
7
|
-
tokenCache = new WeakMap();
|
|
8
|
-
compacted = 0;
|
|
9
|
-
_totalTokens = 0;
|
|
10
|
-
_history = [];
|
|
11
|
+
tokenCache = new WeakMap(); // кэш подсчёта токенов
|
|
12
|
+
compacted = 0; // количество компактификаций за сессию
|
|
13
|
+
_totalTokens = 0; // общее количество токенов в истории
|
|
14
|
+
_history = []; // история по ролям
|
|
15
|
+
/**
|
|
16
|
+
* @param contextWindow - размер контекстного окна модели (в токенах)
|
|
17
|
+
* @param compactThreshold - порог компактификации (по умолчанию 85%)
|
|
18
|
+
*/
|
|
11
19
|
constructor(contextWindow, compactThreshold = 0.85) {
|
|
12
20
|
this.contextWindow = contextWindow;
|
|
13
21
|
this.compactThreshold = compactThreshold;
|
|
@@ -21,9 +29,22 @@ export class ContextManager {
|
|
|
21
29
|
get needsCompact() {
|
|
22
30
|
return this.usagePercent >= this.compactThreshold * 100;
|
|
23
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Оценивает важность сообщения для компактификации.
|
|
34
|
+
*
|
|
35
|
+
* Алгоритм:
|
|
36
|
+
* - System: 100 (всегда сохраняется отдельно)
|
|
37
|
+
* - Tool с ошибкой: 90 (важно для диагностики)
|
|
38
|
+
* - User с "важно"/"critical": 85
|
|
39
|
+
* - Tool с успехом: 70
|
|
40
|
+
* - Assistant с ошибкой/решением: 70-75
|
|
41
|
+
* - User-вопрос: 65
|
|
42
|
+
* - Обычный user/assistant: 40-60
|
|
43
|
+
* + Recency bonus: (index/total) * 20
|
|
44
|
+
*/
|
|
24
45
|
scoreMessage(msg, index, total) {
|
|
25
|
-
let score = 50; //
|
|
26
|
-
//
|
|
46
|
+
let score = 50; // базовая оценка
|
|
47
|
+
// Оценка по роли сообщения
|
|
27
48
|
if (msg.role === 'system')
|
|
28
49
|
score = 100;
|
|
29
50
|
else if (msg.role === 'tool') {
|
|
@@ -53,11 +74,15 @@ export class ContextManager {
|
|
|
53
74
|
else
|
|
54
75
|
score = 40;
|
|
55
76
|
}
|
|
56
|
-
//
|
|
77
|
+
// Бонус за свежесть: чем позже сообщение, тем выше бонус
|
|
57
78
|
const recencyBonus = (index / total) * 20;
|
|
58
79
|
score += recencyBonus;
|
|
59
80
|
return score;
|
|
60
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Обновляет подсчёт токенов для всех сообщений.
|
|
84
|
+
* Использует WeakMap для кэширования — повторный подсчёт не выполняется.
|
|
85
|
+
*/
|
|
61
86
|
async updateTokens(messages) {
|
|
62
87
|
const start = performance.now();
|
|
63
88
|
let total = 0;
|
|
@@ -75,11 +100,25 @@ export class ContextManager {
|
|
|
75
100
|
const elapsed = (performance.now() - start).toFixed(1);
|
|
76
101
|
logDebug('CONTEXT', `updateTokens: ${messages.length} msgs, ${total} tokens, ${elapsed}ms`);
|
|
77
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Компактификация истории сообщений.
|
|
105
|
+
*
|
|
106
|
+
* Стратегия:
|
|
107
|
+
* 1. Сохраняем system prompt (всегда первый)
|
|
108
|
+
* 2. Сохраняем первое user-сообщение (контекст задачи)
|
|
109
|
+
* 3. Остальные сообщения оцениваем по score
|
|
110
|
+
* 4. Сохраняем топ-6 по score + последние 6 по позиции
|
|
111
|
+
* 5. Остальные суммируем в "[Ранее в разговоре (сжато):]"
|
|
112
|
+
*
|
|
113
|
+
* @param messages - полная история сообщений
|
|
114
|
+
* @returns Сжатая история
|
|
115
|
+
*/
|
|
78
116
|
compact(messages) {
|
|
79
117
|
const start = performance.now();
|
|
80
118
|
logInfo('CONTEXT', `compact: ${messages.length} messages before`);
|
|
81
119
|
if (messages.length <= 4)
|
|
82
120
|
return messages;
|
|
121
|
+
// Извлекаем system prompt и первое user-сообщение
|
|
83
122
|
const systemIdx = messages.findIndex(m => m.role === 'system');
|
|
84
123
|
const system = systemIdx >= 0 ? [messages[systemIdx]] : [];
|
|
85
124
|
const keepFirstUser = [];
|
|
@@ -94,24 +133,26 @@ export class ContextManager {
|
|
|
94
133
|
remaining.push(messages[i]);
|
|
95
134
|
}
|
|
96
135
|
}
|
|
97
|
-
//
|
|
136
|
+
// Оцениваем все оставшиеся сообщения
|
|
98
137
|
const scored = remaining.map((msg, i) => ({
|
|
99
138
|
msg,
|
|
100
139
|
score: this.scoreMessage(msg, i, remaining.length),
|
|
101
140
|
tokens: this.tokenCache.get(msg) || 0,
|
|
102
141
|
}));
|
|
103
|
-
//
|
|
142
|
+
// Сортируем по убыванию score
|
|
104
143
|
scored.sort((a, b) => b.score - a.score);
|
|
105
|
-
//
|
|
144
|
+
// Сохраняем топ-6 по score + последние 6 по позиции
|
|
106
145
|
const recentMsgs = remaining.slice(-6);
|
|
107
146
|
const recentIds = new Set(recentMsgs.map(m => m));
|
|
108
147
|
const importantByScore = scored
|
|
109
148
|
.filter(s => !recentIds.has(s.msg))
|
|
110
149
|
.slice(0, 6)
|
|
111
150
|
.map(s => s.msg);
|
|
151
|
+
// Сообщения, которые будут сжаты в summary
|
|
112
152
|
const toCompact = remaining.filter(m => !recentMsgs.includes(m) && !importantByScore.includes(m));
|
|
113
153
|
if (toCompact.length === 0)
|
|
114
154
|
return messages;
|
|
155
|
+
// Формируем краткую сводку из сжатых сообщений
|
|
115
156
|
const compactedPairs = [];
|
|
116
157
|
let i = 0;
|
|
117
158
|
while (i < toCompact.length) {
|
|
@@ -144,6 +185,7 @@ export class ContextManager {
|
|
|
144
185
|
logInfo('CONTEXT', `compact: ${messages.length} → ${result.length} msgs in ${elapsed}ms`);
|
|
145
186
|
return result;
|
|
146
187
|
}
|
|
188
|
+
/** Форматирует строку использования контекста для отображения в UI */
|
|
147
189
|
formatUsage() {
|
|
148
190
|
const theme = getTheme();
|
|
149
191
|
const pct = this.usagePercent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/agent/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/agent/context.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/loop.d.ts
CHANGED
|
@@ -3,6 +3,11 @@ import { ToolRegistry } from '../tools/registry.js';
|
|
|
3
3
|
import { ContextManager } from './context.js';
|
|
4
4
|
import type { OutputRenderer } from '../ui/renderer.js';
|
|
5
5
|
import type { AgentMode } from '../config/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Результат выполнения цикла агента.
|
|
8
|
+
* Содержит всю историю сообщений и опциональный pending-вопрос
|
|
9
|
+
* (если агент вызвал инструмент question и ждёт ответа пользователя).
|
|
10
|
+
*/
|
|
6
11
|
export interface AgentLoopResult {
|
|
7
12
|
messages: ChatMessage[];
|
|
8
13
|
questionPending?: {
|
|
@@ -10,6 +15,28 @@ export interface AgentLoopResult {
|
|
|
10
15
|
choices?: string[];
|
|
11
16
|
};
|
|
12
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Запускает основной цикл ReAct-агента.
|
|
20
|
+
*
|
|
21
|
+
* Цикл работает до тех пор, пока:
|
|
22
|
+
* - Агент не перестанет вызывать инструменты (финальный текстовый ответ)
|
|
23
|
+
* - Не будет достигнут лимит ходов (maxSteps из конфига)
|
|
24
|
+
* - Пользователь не прервёт выполнение (abort signal)
|
|
25
|
+
*
|
|
26
|
+
* Стратегия обработки ошибок:
|
|
27
|
+
* - Пустые ответы: retry до 3 раз с подсказкой "Continue."
|
|
28
|
+
* - Невалидные tool calls: отправка обратной связи модели
|
|
29
|
+
* - Пустые ответы подряд >3: остановка цикла
|
|
30
|
+
*
|
|
31
|
+
* @param provider - провайдер LLM (OpenAI-совместимый)
|
|
32
|
+
* @param registry - реестр доступных инструментов
|
|
33
|
+
* @param prompt - начальный промпт пользователя
|
|
34
|
+
* @param contextManager - менеджер контекста (опциональный, создаётся новый если не передан)
|
|
35
|
+
* @param previousMessages - предыдущие сообщения для восстановления сессии
|
|
36
|
+
* @param renderer - рендерер вывода (Ink или readline)
|
|
37
|
+
* @param providerName - имя провайдера для отображения
|
|
38
|
+
* @param options - дополнительные опции (quiet, showThinking, mode)
|
|
39
|
+
*/
|
|
13
40
|
export declare function runAgentLoop(provider: LLMProvider, registry: ToolRegistry, prompt: string, contextManager?: ContextManager, previousMessages?: ChatMessage[], renderer?: OutputRenderer, providerName?: string, options?: {
|
|
14
41
|
quiet?: boolean;
|
|
15
42
|
showThinking?: boolean;
|
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;AAEpD,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;
|
|
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;AAEpD,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;AA+CpD;;;;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,CAq1B1B"}
|
package/dist/agent/loop.js
CHANGED
|
@@ -19,7 +19,31 @@ import { loadConfig, saveConfig } from '../config/config.js';
|
|
|
19
19
|
import { buildSystemPrompt } from './system-prompt.js';
|
|
20
20
|
import { logInfo, logWarn, logError, logDebug, logToolCall, setStatusWriter, writeStatus } from '../logger.js';
|
|
21
21
|
import { buildSubagentContext, runIsolatedLoop, extractAllToolCalls, validateToolCall, doChat } from '../tools/subagent.js';
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Основной цикл ReAct-агента.
|
|
24
|
+
*
|
|
25
|
+
* Архитектура цикла:
|
|
26
|
+
* 1. Сборка system prompt (кэшируется на время сессии)
|
|
27
|
+
* 2. Отправка запроса к LLM с историей сообщений
|
|
28
|
+
* 3. Парсинг tool calls из ответа модели (формат <tool_call>)
|
|
29
|
+
* 4. Валидация и исполнение каждого tool call
|
|
30
|
+
* 5. Добавление результата в историю
|
|
31
|
+
* 6. Повтор до завершения (нет tool calls) или лимита ходов
|
|
32
|
+
*
|
|
33
|
+
* Особенности:
|
|
34
|
+
* - Fallback-парсинг при невалидном JSON от маленьких моделей
|
|
35
|
+
* - Автоматическое компактирование контекста при приближении к лимиту
|
|
36
|
+
* - Retry с увеличением temperature при пустых ответах
|
|
37
|
+
* - Автодобавление скилов при обнаружении релевантных паттернов
|
|
38
|
+
*
|
|
39
|
+
* @module agent/loop
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Fallback-извлечение bash-команды из ответа модели.
|
|
43
|
+
* Используется когда основной парсинг tool calls не сработал
|
|
44
|
+
* (маленькие модели часто генерируют невалидный JSON).
|
|
45
|
+
* Применяет три regex-паттерна для поиска команды bash.
|
|
46
|
+
*/
|
|
23
47
|
function extractToolCallFallback(response) {
|
|
24
48
|
const patterns = [
|
|
25
49
|
/"name"\s*:\s*"bash"\s*,\s*"arguments"\s*:\s*\{\s*"cmd"\s*:\s*"((?:[^"\\]|\\.)*)"/,
|
|
@@ -36,12 +60,36 @@ function extractToolCallFallback(response) {
|
|
|
36
60
|
}
|
|
37
61
|
return null;
|
|
38
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Запускает основной цикл ReAct-агента.
|
|
65
|
+
*
|
|
66
|
+
* Цикл работает до тех пор, пока:
|
|
67
|
+
* - Агент не перестанет вызывать инструменты (финальный текстовый ответ)
|
|
68
|
+
* - Не будет достигнут лимит ходов (maxSteps из конфига)
|
|
69
|
+
* - Пользователь не прервёт выполнение (abort signal)
|
|
70
|
+
*
|
|
71
|
+
* Стратегия обработки ошибок:
|
|
72
|
+
* - Пустые ответы: retry до 3 раз с подсказкой "Continue."
|
|
73
|
+
* - Невалидные tool calls: отправка обратной связи модели
|
|
74
|
+
* - Пустые ответы подряд >3: остановка цикла
|
|
75
|
+
*
|
|
76
|
+
* @param provider - провайдер LLM (OpenAI-совместимый)
|
|
77
|
+
* @param registry - реестр доступных инструментов
|
|
78
|
+
* @param prompt - начальный промпт пользователя
|
|
79
|
+
* @param contextManager - менеджер контекста (опциональный, создаётся новый если не передан)
|
|
80
|
+
* @param previousMessages - предыдущие сообщения для восстановления сессии
|
|
81
|
+
* @param renderer - рендерер вывода (Ink или readline)
|
|
82
|
+
* @param providerName - имя провайдера для отображения
|
|
83
|
+
* @param options - дополнительные опции (quiet, showThinking, mode)
|
|
84
|
+
*/
|
|
39
85
|
export async function runAgentLoop(provider, registry, prompt, contextManager, previousMessages, renderer, providerName, options) {
|
|
40
86
|
logInfo('LOOP', `runAgentLoop started: "${prompt.slice(0, 50)}"`);
|
|
41
87
|
renderer?.resetAbort?.();
|
|
88
|
+
// Парсинг ссылок на файлы в промпте (формат @path/to/file)
|
|
42
89
|
const { prompt: enrichedPrompt, files } = await parseFileRefs(prompt);
|
|
43
90
|
logDebug('LOOP', `parseFileRefs: ${files.length} files, prompt len: ${enrichedPrompt.length}`);
|
|
44
91
|
const quiet = options?.quiet ?? false;
|
|
92
|
+
// Автозагрузка скилов из промпта (если пользователь указал @skill-name)
|
|
45
93
|
const loadedSkills = loadSkillsFromRefs(prompt, process.cwd());
|
|
46
94
|
const loadedSkillNames = new Set(loadedSkills.map(s => s.name));
|
|
47
95
|
if (loadedSkills.length > 0) {
|
|
@@ -70,11 +118,13 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
70
118
|
}
|
|
71
119
|
setStatusWriter(renderer ? (msg) => renderer.writeDim(` ${msg}`) : null);
|
|
72
120
|
writeStatus('Initializing...');
|
|
121
|
+
// Сборка system prompt (включает инструкции, информацию о проекте, скилы, память)
|
|
73
122
|
const sysStart = performance.now();
|
|
74
123
|
const systemPrompt = buildSystemPrompt(loadedSkills.length > 0 ? loadedSkills : undefined, options?.mode);
|
|
75
124
|
const sysElapsed = ((performance.now() - sysStart) / 1000).toFixed(1);
|
|
76
125
|
writeStatus(`Done (${sysElapsed}s)`);
|
|
77
126
|
setStatusWriter(null);
|
|
127
|
+
// Начальное состояние истории: system prompt + предыдущие сообщения + текущий промпт
|
|
78
128
|
const messages = [
|
|
79
129
|
{ role: 'system', content: systemPrompt },
|
|
80
130
|
...(previousMessages || []),
|
|
@@ -83,11 +133,11 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
83
133
|
const ctx = contextManager ?? new ContextManager(provider.contextWindow);
|
|
84
134
|
const config = loadConfig();
|
|
85
135
|
const maxSteps = config.agent.maxSteps ?? 100;
|
|
86
|
-
let consecutiveEmpty = 0;
|
|
136
|
+
let consecutiveEmpty = 0; // счётчик пустых ответов подряд
|
|
87
137
|
let pendingSyntaxError = '';
|
|
88
138
|
let aborted = false;
|
|
89
|
-
let continuationNudges = 0;
|
|
90
|
-
let directWriteStreak = 0; //
|
|
139
|
+
let continuationNudges = 0; // счётчик подсказок "продолжай работать"
|
|
140
|
+
let directWriteStreak = 0; // количество прямых write_file без subagent
|
|
91
141
|
// Reset abort signal for this run
|
|
92
142
|
if (renderer?.abortSignal === undefined) {
|
|
93
143
|
// Renderer doesn't support abort — fine
|
|
@@ -99,14 +149,14 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
99
149
|
step: 0,
|
|
100
150
|
maxSteps,
|
|
101
151
|
});
|
|
152
|
+
// ─── Основной цикл ReAct ────────────────────────────────
|
|
102
153
|
for (let step = 0; step < maxSteps; step++) {
|
|
103
154
|
if (renderer?.abortSignal?.aborted) {
|
|
104
155
|
aborted = true;
|
|
105
156
|
break;
|
|
106
157
|
}
|
|
107
158
|
let skillsAutoLoaded = false;
|
|
108
|
-
|
|
109
|
-
const createdFiles = [];
|
|
159
|
+
const createdFiles = []; // отслеживание созданных/изменённых файлов
|
|
110
160
|
logDebug('LOOP', `step ${step + 1}/${maxSteps} starting, ${messages.length} messages`);
|
|
111
161
|
await ctx.updateTokens(messages);
|
|
112
162
|
const skillNames = loadedSkills.map(s => s.name).join(', ');
|
|
@@ -120,6 +170,8 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
120
170
|
step: step + 1,
|
|
121
171
|
maxSteps,
|
|
122
172
|
});
|
|
173
|
+
// ─── Compaction контекста ─────────────────────────────
|
|
174
|
+
// Если использование контекста превышает порог (85%), компактируем историю
|
|
123
175
|
if (ctx.needsCompact && messages.length > 6) {
|
|
124
176
|
const before = messages.length;
|
|
125
177
|
messages.splice(0, messages.length, ...ctx.compact(messages));
|
|
@@ -128,15 +180,16 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
128
180
|
}
|
|
129
181
|
await ctx.updateTokens(messages);
|
|
130
182
|
}
|
|
183
|
+
// ─── Запрос к LLM ────────────────────────────────────
|
|
131
184
|
logInfo('LOOP', `calling doChat, messages: ${messages.length}, options.showThinking: ${options?.showThinking}`);
|
|
132
185
|
const { response } = await doChat(provider, messages, renderer, renderer?.abortSignal, 1, options?.showThinking);
|
|
133
|
-
//
|
|
186
|
+
// Проверка прерывания во время генерации
|
|
134
187
|
if (renderer?.abortSignal?.aborted) {
|
|
135
188
|
aborted = true;
|
|
136
189
|
break;
|
|
137
190
|
}
|
|
138
|
-
//
|
|
139
|
-
//
|
|
191
|
+
// ─── Автозагрузка скилов по @упоминаниям ─────────────
|
|
192
|
+
// Если ответ содержит @skill-name, загружаем скил и пересобираем system prompt
|
|
140
193
|
if (options?.mode !== 'fast' && !skillsAutoLoaded) {
|
|
141
194
|
const suggestedSkillRefs = response.matchAll(/@(\w[\w-]*)/g);
|
|
142
195
|
const newSkillNames = [];
|
|
@@ -161,6 +214,8 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
161
214
|
continue;
|
|
162
215
|
}
|
|
163
216
|
} // end fast-mode skill block
|
|
217
|
+
// ─── Обработка пустых ответов ────────────────────────
|
|
218
|
+
// Маленькие модели иногда возвращают пустой ответ — повторяем с подсказкой
|
|
164
219
|
if (!response.trim()) {
|
|
165
220
|
logWarn('LOOP', `empty response #${consecutiveEmpty + 1}`);
|
|
166
221
|
consecutiveEmpty++;
|
|
@@ -179,6 +234,8 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
179
234
|
}
|
|
180
235
|
consecutiveEmpty = 0;
|
|
181
236
|
messages.push({ role: 'assistant', content: response });
|
|
237
|
+
// ─── Парсинг tool calls ─────────────────────────────
|
|
238
|
+
// Извлекаем JSON из тегов <tool_call>...</tool_call> и валидируем
|
|
182
239
|
const allJsonRaw = extractAllToolCalls(response);
|
|
183
240
|
const validCalls = [];
|
|
184
241
|
const invalidCalls = [];
|
|
@@ -203,6 +260,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
203
260
|
}
|
|
204
261
|
}
|
|
205
262
|
}
|
|
263
|
+
// ─── Fallback: regex-извлечение при невалидном JSON ───
|
|
206
264
|
if (validCalls.length === 0) {
|
|
207
265
|
const fallback = extractToolCallFallback(response);
|
|
208
266
|
if (fallback) {
|
|
@@ -261,9 +319,10 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
261
319
|
}
|
|
262
320
|
continue;
|
|
263
321
|
}
|
|
264
|
-
//
|
|
322
|
+
// ─── Enforce: лимиты на запись файлов ────────────────
|
|
323
|
+
// Отслеживание повторных чтений одного файла для консолидации
|
|
265
324
|
let lastReadFile = null;
|
|
266
|
-
//
|
|
325
|
+
// Блокировка прямых write_file когда есть план с 3+ шагами —.force subagent
|
|
267
326
|
const planSteps = showPlan();
|
|
268
327
|
const hasMultiStepPlan = !planSteps.startsWith('No active plan') && (planSteps.match(/\n/g) || []).length >= 3;
|
|
269
328
|
if (hasMultiStepPlan) {
|
|
@@ -323,7 +382,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
323
382
|
}
|
|
324
383
|
}
|
|
325
384
|
}
|
|
326
|
-
//
|
|
385
|
+
// Блокировка нескольких записей файлов за один ответ — максимум одна
|
|
327
386
|
const writeCalls = validCalls.filter(c => c.parsed.name === 'write_file' || c.parsed.name === 'edit_file');
|
|
328
387
|
if (writeCalls.length > 1) {
|
|
329
388
|
// Keep only the first write call, block the rest
|
|
@@ -341,6 +400,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
341
400
|
renderer?.writeWarning(` [blocked ${blocked.length} extra file writes — one at a time]`);
|
|
342
401
|
}
|
|
343
402
|
}
|
|
403
|
+
// ─── Исполнение tool calls ──────────────────────────
|
|
344
404
|
for (const call of validCalls) {
|
|
345
405
|
const { parsed, cmd } = call;
|
|
346
406
|
const isBash = parsed.name === 'bash';
|
|
@@ -380,7 +440,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
380
440
|
let toolOutput = '';
|
|
381
441
|
let toolExitCode = 0;
|
|
382
442
|
const toolStart = performance.now();
|
|
383
|
-
//
|
|
443
|
+
// ─── Нативные инструменты (plan/todo/question/approve) ──
|
|
384
444
|
if (parsed.name === 'plan') {
|
|
385
445
|
const planName = String(parsed.arguments.TaskName || parsed.arguments.name || parsed.arguments.plan || 'Untitled');
|
|
386
446
|
const stepsRaw = parsed.arguments.Steps || parsed.arguments.steps || [];
|
|
@@ -566,7 +626,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
566
626
|
}
|
|
567
627
|
}
|
|
568
628
|
else if (parsed.name === 'subagent') {
|
|
569
|
-
//
|
|
629
|
+
// ─── Субагент: изолированный контекст для задачи ──
|
|
570
630
|
const task = String(parsed.arguments.task || '');
|
|
571
631
|
if (!task) {
|
|
572
632
|
toolOutput = 'Error: task is required';
|
|
@@ -681,6 +741,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
681
741
|
messages,
|
|
682
742
|
questionPending: { question: `Approve: ${action}?`, choices: ['Yes', 'No'] },
|
|
683
743
|
};
|
|
744
|
+
// ─── MCP-инструменты ────────────────────────────────
|
|
684
745
|
}
|
|
685
746
|
else if (isBash && isMCPCommand(cmd)) {
|
|
686
747
|
const mcpCmd = parseMCPCommand(cmd);
|
|
@@ -694,11 +755,13 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
694
755
|
toolOutput = 'Invalid MCP command. Format: mcp <server> <tool> [args_json]';
|
|
695
756
|
toolExitCode = 1;
|
|
696
757
|
}
|
|
758
|
+
// ─── Файловые инструменты (не bash) ─────────────────
|
|
697
759
|
}
|
|
698
760
|
else if (!isBash) {
|
|
699
761
|
const result = await registry.execute({ id: toolId, name: parsed.name, arguments: parsed.arguments });
|
|
700
762
|
toolOutput = result.stdout || result.stderr || '(no output)';
|
|
701
763
|
toolExitCode = result.exitCode;
|
|
764
|
+
// ─── Bash: выполнение shell-команд ──────────────────
|
|
702
765
|
}
|
|
703
766
|
else {
|
|
704
767
|
const result = await registry.execute({ id: toolId, name: 'bash', arguments: { cmd, timeout: parsed.arguments?.timeout ?? 120 } });
|
|
@@ -751,7 +814,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
751
814
|
else if (!isBash && parsed.name !== 'read_file') {
|
|
752
815
|
lastReadFile = null;
|
|
753
816
|
}
|
|
754
|
-
//
|
|
817
|
+
// ─── Проверка синтаксиса после записи файлов ────────
|
|
755
818
|
let hasSyntaxError = false;
|
|
756
819
|
if (toolExitCode === 0) {
|
|
757
820
|
const isWriteOp = isBash
|
|
@@ -814,7 +877,7 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
|
|
|
814
877
|
pendingSyntaxError = '';
|
|
815
878
|
}
|
|
816
879
|
}
|
|
817
|
-
//
|
|
880
|
+
// Автодетекция стиля кодирования из содержимого файла
|
|
818
881
|
if (toolExitCode === 0 && filePath) {
|
|
819
882
|
try {
|
|
820
883
|
const fullContent = readFileSync(join(process.cwd(), filePath), 'utf-8');
|