micro-models-agent 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands.js +46 -0
- package/dist/cli/repl.js +52 -1
- package/dist/core/agent.js +5 -0
- package/dist/i18n/en.json +27 -0
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/ru.json +25 -0
- package/dist/llm/openai-compat.js +26 -0
- package/dist/main.js +474 -39
- package/dist/modules/processes/detect.js +34 -0
- package/dist/modules/processes/index.js +3 -0
- package/dist/modules/processes/registry.js +142 -0
- package/dist/modules/processes/runner.js +109 -0
- package/dist/tools/bash.js +42 -15
- package/dist/tools/executor.js +10 -4
- package/dist/tools/index.js +5 -1
- package/dist/tools/process-kill.js +29 -0
- package/dist/tools/process-list.js +38 -0
- package/dist/tools/process-log.js +39 -0
- package/package.json +1 -1
package/dist/cli/commands.js
CHANGED
|
@@ -78,6 +78,35 @@ export function createProgram() {
|
|
|
78
78
|
.action(async () => {
|
|
79
79
|
const { config } = await bootstrap();
|
|
80
80
|
console.log(t("cli.current_model"), config.model);
|
|
81
|
+
// Fetch available models from provider
|
|
82
|
+
const { OpenAICompatProvider } = await import("../llm/openai-compat");
|
|
83
|
+
const provider = new OpenAICompatProvider({
|
|
84
|
+
model: config.model,
|
|
85
|
+
baseUrl: config.provider.baseUrl,
|
|
86
|
+
apiKey: config.provider.apiKey,
|
|
87
|
+
contextWindow: config.contextWindow,
|
|
88
|
+
});
|
|
89
|
+
const spinner = (await import("../ui/spinner")).Spinner;
|
|
90
|
+
const s = new spinner();
|
|
91
|
+
s.start(t("cli.fetching_models"));
|
|
92
|
+
try {
|
|
93
|
+
const models = await provider.listModels();
|
|
94
|
+
s.stop();
|
|
95
|
+
if (models.length > 0) {
|
|
96
|
+
console.log(t("cli.available_models"));
|
|
97
|
+
for (const m of models) {
|
|
98
|
+
const marker = m === config.model ? "* " : " ";
|
|
99
|
+
console.log(` ${marker}${m}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log(t("cli.no_models_found"));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
s.stop();
|
|
108
|
+
console.log(t("cli.model_fetch_failed", { error: String(err) }));
|
|
109
|
+
}
|
|
81
110
|
console.log(t("cli.model_hint"));
|
|
82
111
|
});
|
|
83
112
|
model
|
|
@@ -91,6 +120,23 @@ export function createProgram() {
|
|
|
91
120
|
saveConfig(config, configPath);
|
|
92
121
|
console.log(t("cli.model_set", { name }));
|
|
93
122
|
});
|
|
123
|
+
// Context window command
|
|
124
|
+
program
|
|
125
|
+
.command("context")
|
|
126
|
+
.description(t("cli.manage_context"))
|
|
127
|
+
.argument("<size>", "Context window size in tokens")
|
|
128
|
+
.action(async (size) => {
|
|
129
|
+
const configPath = join(homedir(), ".mma", "config.json");
|
|
130
|
+
const { config } = await bootstrap();
|
|
131
|
+
const contextWindow = parseInt(size, 10);
|
|
132
|
+
if (isNaN(contextWindow) || contextWindow < 1024) {
|
|
133
|
+
console.log(t("cli.invalid_context_size"));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
config.contextWindow = contextWindow;
|
|
137
|
+
saveConfig(config, configPath);
|
|
138
|
+
console.log(t("cli.context_set", { size: contextWindow }));
|
|
139
|
+
});
|
|
94
140
|
const provider = program
|
|
95
141
|
.command("provider")
|
|
96
142
|
.description(t("cli.manage_providers"));
|
package/dist/cli/repl.js
CHANGED
|
@@ -235,10 +235,40 @@ export class Repl {
|
|
|
235
235
|
name: "model",
|
|
236
236
|
description: t("repl.model_list"),
|
|
237
237
|
usage: t("repl.model_usage"),
|
|
238
|
-
action: (args) => {
|
|
238
|
+
action: async (args) => {
|
|
239
239
|
const subcmd = args[0];
|
|
240
240
|
if (!subcmd || subcmd === "list") {
|
|
241
241
|
console.log(`${t("repl.model_current")} ${this.config.model}`);
|
|
242
|
+
// Fetch available models from provider
|
|
243
|
+
const { OpenAICompatProvider } = await import("../llm/openai-compat");
|
|
244
|
+
const provider = new OpenAICompatProvider({
|
|
245
|
+
model: this.config.model,
|
|
246
|
+
baseUrl: this.config.provider.baseUrl,
|
|
247
|
+
apiKey: this.config.provider.apiKey,
|
|
248
|
+
contextWindow: this.config.contextWindow,
|
|
249
|
+
});
|
|
250
|
+
const { Spinner } = await import("../ui/spinner");
|
|
251
|
+
const s = new Spinner();
|
|
252
|
+
s.start(t("cli.fetching_models"));
|
|
253
|
+
try {
|
|
254
|
+
const models = await provider.listModels();
|
|
255
|
+
s.stop();
|
|
256
|
+
if (models.length > 0) {
|
|
257
|
+
console.log(t("cli.available_models"));
|
|
258
|
+
for (const m of models) {
|
|
259
|
+
const marker = m === this.config.model ? pc.green("* ") : " ";
|
|
260
|
+
console.log(` ${marker}${m}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
console.log(t("cli.no_models_found"));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
s.stop();
|
|
269
|
+
console.log(t("cli.model_fetch_failed", { error: String(err) }));
|
|
270
|
+
}
|
|
271
|
+
console.log(t("cli.model_hint"));
|
|
242
272
|
return;
|
|
243
273
|
}
|
|
244
274
|
if (subcmd === "use") {
|
|
@@ -256,6 +286,27 @@ export class Repl {
|
|
|
256
286
|
console.log(t("repl.model_usage"));
|
|
257
287
|
},
|
|
258
288
|
});
|
|
289
|
+
this.registerCommand({
|
|
290
|
+
name: "context",
|
|
291
|
+
description: t("cli.manage_context"),
|
|
292
|
+
usage: "/context <size>",
|
|
293
|
+
action: (args) => {
|
|
294
|
+
if (args.length === 0) {
|
|
295
|
+
console.log(`/context ${t("repl.context")} ${this.config.contextWindow}`);
|
|
296
|
+
console.log(t("repl.model_usage").replace("/model", "/context"));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const size = parseInt(args[0], 10);
|
|
300
|
+
if (isNaN(size) || size < 1024) {
|
|
301
|
+
console.log(t("cli.invalid_context_size"));
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
this.config.contextWindow = size;
|
|
305
|
+
const configPath = join(homedir(), ".mma", "config.json");
|
|
306
|
+
saveConfig(this.config, configPath);
|
|
307
|
+
console.log(pc.green(t("cli.context_set", { size })));
|
|
308
|
+
},
|
|
309
|
+
});
|
|
259
310
|
}
|
|
260
311
|
registerSessionCommands() {
|
|
261
312
|
if (!this.sessionManager)
|
package/dist/core/agent.js
CHANGED
|
@@ -5,6 +5,7 @@ import { OrchestratorClient } from "../llm/orchestrator";
|
|
|
5
5
|
import { validatePlan, applyAutoFixes, } from "../modules/execution/plan-validator";
|
|
6
6
|
import { MoEExecutor } from "../modules/execution/moe-executor";
|
|
7
7
|
import { StepVerifier } from "../modules/execution/verifier";
|
|
8
|
+
import { processRegistry } from "../modules/processes";
|
|
8
9
|
const TOOL_RESULT_MAX_TOKENS_RATIO = 0.3;
|
|
9
10
|
const TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000;
|
|
10
11
|
export class Agent {
|
|
@@ -618,6 +619,10 @@ export class Agent {
|
|
|
618
619
|
shutdown() {
|
|
619
620
|
const { pluginManager, logger, sessionManager, contextManager } = this.deps;
|
|
620
621
|
contextManager.onCompact = null;
|
|
622
|
+
const killed = processRegistry.killAll();
|
|
623
|
+
if (killed > 0) {
|
|
624
|
+
logger.info(`Killed ${killed} background process(es) on shutdown`);
|
|
625
|
+
}
|
|
621
626
|
pluginManager.runOnSessionEnd({
|
|
622
627
|
logger,
|
|
623
628
|
sessionManager: sessionManager?.getActiveMeta(),
|
package/dist/i18n/en.json
CHANGED
|
@@ -123,6 +123,24 @@
|
|
|
123
123
|
"tool.screenshot_unavailable": "[Screenshot captured \u2014 image not available for text-only model]",
|
|
124
124
|
"tool.timeout": "Tool {name} timed out after {seconds} seconds",
|
|
125
125
|
"tool.interactive_disabled": "Interactive tool is disabled in exit-on-complete mode. Proceed without asking the user.",
|
|
126
|
+
"proc.started": "Started background process {id} (PID {pid}).\nCommand: {command}",
|
|
127
|
+
"proc.detected_hint": "[Long-running command detected — started in background]",
|
|
128
|
+
"proc.manage_hint": "Check output: process_log id={id}. Stop it: process_kill id={id}. List all: process_list.",
|
|
129
|
+
"proc.none": "No background processes running.",
|
|
130
|
+
"proc.not_found": "Process not found: {id}",
|
|
131
|
+
"proc.killed": "Process {id} (PID {pid}) killed.",
|
|
132
|
+
"proc.kill_failed": "Failed to kill process {id}",
|
|
133
|
+
"proc.list_header": "Background processes",
|
|
134
|
+
"proc.log_header": "Process {id} ({status}) output:",
|
|
135
|
+
"proc.log_empty": "(no output yet)",
|
|
136
|
+
"proc.timed_out": "Command timed out after {ms} ms and was killed.",
|
|
137
|
+
"proc.hint": "Manage them with {list}, {log}, {kill}.",
|
|
138
|
+
"proc.status_running": "running",
|
|
139
|
+
"proc.status_exited": "exited",
|
|
140
|
+
"proc.status_killed": "killed",
|
|
141
|
+
"tool.friendly.process_list": "Listing background processes",
|
|
142
|
+
"tool.friendly.process_log": "Process output",
|
|
143
|
+
"tool.friendly.process_kill": "Stopping process",
|
|
126
144
|
"plan.created": "Plan created: {title} ({steps} steps)",
|
|
127
145
|
"plan.step_done": "Step {n}/{total}: {description} \u2713",
|
|
128
146
|
"plan.complete": "Task complete: {summary}",
|
|
@@ -160,6 +178,15 @@
|
|
|
160
178
|
"cli.list_models": "List available models",
|
|
161
179
|
"cli.current_model": "Current model:",
|
|
162
180
|
"cli.model_hint": "(Use /model use <name> to change)",
|
|
181
|
+
"cli.model_fetch_failed": "Failed to fetch models: {error}",
|
|
182
|
+
"cli.available_models": "Available models:",
|
|
183
|
+
"cli.no_models_found": "No models found from provider",
|
|
184
|
+
"cli.fetching_models": "Fetching model list...",
|
|
185
|
+
"cli.manage_context": "Manage context window",
|
|
186
|
+
"cli.invalid_context_size": "Invalid context size. Must be a number >= 1024",
|
|
187
|
+
"cli.context_set": "Context window set to: {size} tokens",
|
|
188
|
+
"cli.manage_context": "Manage context window",
|
|
189
|
+
"cli.invalid_context_size": "Invalid context size. Must be a number >= 1024",
|
|
163
190
|
"cli.set_model": "Set default model",
|
|
164
191
|
"cli.model_set": "Model set to: {name}",
|
|
165
192
|
"cli.manage_providers": "Manage providers",
|
package/dist/i18n/index.js
CHANGED
package/dist/i18n/ru.json
CHANGED
|
@@ -123,6 +123,24 @@
|
|
|
123
123
|
"tool.screenshot_unavailable": "[Скриншот сделан — изображение недоступно для текстовой модели]",
|
|
124
124
|
"tool.timeout": "Инструмент {name} превысил таймаут ({seconds} сек)",
|
|
125
125
|
"tool.interactive_disabled": "Интерактивный инструмент отключён в режиме exit-on-complete. Продолжай без вопроса пользователю.",
|
|
126
|
+
"proc.started": "Фоновый процесс запущен: {id} (PID {pid}).\nКоманда: {command}",
|
|
127
|
+
"proc.detected_hint": "[Обнаружена длительная команда — запущена в фоне]",
|
|
128
|
+
"proc.manage_hint": "Проверить вывод: process_log id={id}. Остановить: process_kill id={id}. Список всех: process_list.",
|
|
129
|
+
"proc.none": "Фоновых процессов нет.",
|
|
130
|
+
"proc.not_found": "Процесс не найден: {id}",
|
|
131
|
+
"proc.killed": "Процесс {id} (PID {pid}) остановлен.",
|
|
132
|
+
"proc.kill_failed": "Не удалось остановить процесс {id}",
|
|
133
|
+
"proc.list_header": "Фоновые процессы",
|
|
134
|
+
"proc.log_header": "Вывод процесса {id} ({status}):",
|
|
135
|
+
"proc.log_empty": "(вывода пока нет)",
|
|
136
|
+
"proc.timed_out": "Команда превысила таймаут {ms} мс и была остановлена.",
|
|
137
|
+
"proc.hint": "Управление: {list}, {log}, {kill}.",
|
|
138
|
+
"proc.status_running": "работает",
|
|
139
|
+
"proc.status_exited": "завершён",
|
|
140
|
+
"proc.status_killed": "остановлен",
|
|
141
|
+
"tool.friendly.process_list": "Список фоновых процессов",
|
|
142
|
+
"tool.friendly.process_log": "Вывод процесса",
|
|
143
|
+
"tool.friendly.process_kill": "Остановка процесса",
|
|
126
144
|
"plan.created": "План создан: {title} ({steps} шагов)",
|
|
127
145
|
"plan.step_done": "Шаг {n}/{total}: {description} ✓",
|
|
128
146
|
"plan.complete": "Задача выполнена: {summary}",
|
|
@@ -160,6 +178,13 @@
|
|
|
160
178
|
"cli.list_models": "Список доступных моделей",
|
|
161
179
|
"cli.current_model": "Текущая модель:",
|
|
162
180
|
"cli.model_hint": "(Используйте /model use <имя> для смены)",
|
|
181
|
+
"cli.model_fetch_failed": "Не удалось получить модели: {error}",
|
|
182
|
+
"cli.available_models": "Доступные модели:",
|
|
183
|
+
"cli.no_models_found": "Модели не найдены у провайдера",
|
|
184
|
+
"cli.fetching_models": "Загрузка списка моделей...",
|
|
185
|
+
"cli.manage_context": "Управление контекстным окном",
|
|
186
|
+
"cli.invalid_context_size": "Некорректный размер контекста. Должно быть число >= 1024",
|
|
187
|
+
"cli.context_set": "Контекстное окно установлено: {size} токенов",
|
|
163
188
|
"cli.set_model": "Установить модель по умолчанию",
|
|
164
189
|
"cli.model_set": "Модель установлена: {name}",
|
|
165
190
|
"cli.manage_providers": "Управление провайдерами",
|
|
@@ -244,6 +244,32 @@ export class OpenAICompatProvider {
|
|
|
244
244
|
countTokens(text) {
|
|
245
245
|
return this.tokenCounter.count(text);
|
|
246
246
|
}
|
|
247
|
+
async listModels() {
|
|
248
|
+
try {
|
|
249
|
+
const url = `${this.config.baseUrl.replace(/\/+$/, "")}/models`;
|
|
250
|
+
const headers = {
|
|
251
|
+
"Content-Type": "application/json",
|
|
252
|
+
};
|
|
253
|
+
if (this.config.apiKey && this.config.apiKey !== "not-needed") {
|
|
254
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
255
|
+
}
|
|
256
|
+
const response = await fetch(url, {
|
|
257
|
+
method: "GET",
|
|
258
|
+
headers,
|
|
259
|
+
});
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
const data = (await response.json());
|
|
264
|
+
const models = (data.data || data || [])
|
|
265
|
+
.map((m) => m.id || m.name || m.model || "")
|
|
266
|
+
.filter(Boolean);
|
|
267
|
+
return models;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
247
273
|
async fetchWithRetry(url, init) {
|
|
248
274
|
const { maxRetries, baseDelay, maxDelay } = this.retryConfig;
|
|
249
275
|
let lastError = null;
|