micro-models-agent 0.8.0 → 0.10.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/config/config.js +66 -3
- package/dist/config/security.js +1 -1
- package/dist/core/agent.js +5 -0
- package/dist/core/bootstrap.js +10 -0
- package/dist/i18n/en.json +18 -0
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/ru.json +18 -0
- package/dist/main.js +16339 -0
- 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/modules/security/audit-log.js +8 -0
- package/dist/modules/security/command-validator.js +22 -9
- package/dist/modules/security/encryption.js +22 -2
- package/dist/tools/bash.js +49 -19
- package/dist/tools/executor.js +10 -4
- package/dist/tools/grep-tool.js +24 -18
- package/dist/tools/index.js +5 -1
- package/dist/tools/pipeline-run.js +114 -9
- 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/dist/tools/read-file.js +6 -1
- package/dist/tools/subagent.js +12 -0
- package/package.json +1 -1
package/dist/config/config.js
CHANGED
|
@@ -1,11 +1,41 @@
|
|
|
1
1
|
import { existsSync, readFileSync, unlinkSync, writeFileSync, mkdirSync } from 'fs';
|
|
2
2
|
import { join, dirname } from 'path';
|
|
3
3
|
import { DEFAULTS } from './defaults';
|
|
4
|
+
import { DEFAULT_SECURITY_CONFIG } from './security';
|
|
4
5
|
import { t } from '../i18n/index';
|
|
5
6
|
import { MigrationDetector } from '../migration/detect';
|
|
6
7
|
import { BackupManager } from '../migration/backup';
|
|
7
8
|
import { validateExpertConfig } from './experts';
|
|
8
9
|
import { ConfigEncryptor } from '../modules/security/encryption';
|
|
10
|
+
/**
|
|
11
|
+
* Restore RegExp instances in dangerousPatterns that were serialized as {}
|
|
12
|
+
* (pre-0.8.0 configs) or as {__regex, source, flags} (new format).
|
|
13
|
+
* Falls back to default patterns for any entry that is not a real RegExp.
|
|
14
|
+
*/
|
|
15
|
+
function restoreDangerousPatterns(patterns, defaults) {
|
|
16
|
+
const fallback = (Array.isArray(defaults) ? defaults : []);
|
|
17
|
+
if (!Array.isArray(patterns) || patterns.length === 0) {
|
|
18
|
+
return fallback;
|
|
19
|
+
}
|
|
20
|
+
return patterns.map((p, i) => {
|
|
21
|
+
if (p instanceof RegExp)
|
|
22
|
+
return p;
|
|
23
|
+
// If the pattern was serialized as {__regex, source, flags}, revive it
|
|
24
|
+
if (p && typeof p === "object") {
|
|
25
|
+
const { source, flags } = p;
|
|
26
|
+
if (source && typeof source === "string") {
|
|
27
|
+
try {
|
|
28
|
+
return new RegExp(source, flags || "");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// fall through
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Corrupted entry (e.g. serialized as {} pre-0.8.0): use default by index
|
|
36
|
+
return fallback[i] || fallback[0] || p;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
9
39
|
function deepMerge(target, source) {
|
|
10
40
|
const result = { ...target };
|
|
11
41
|
for (const key of Object.keys(source)) {
|
|
@@ -22,10 +52,37 @@ function deepMerge(target, source) {
|
|
|
22
52
|
}
|
|
23
53
|
return result;
|
|
24
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* JSON replacer that serializes RegExp objects as {__regex, source, flags}
|
|
57
|
+
* so they survive JSON.stringify/parse round-trips.
|
|
58
|
+
*/
|
|
59
|
+
function regexReplacer(_key, value) {
|
|
60
|
+
if (value instanceof RegExp) {
|
|
61
|
+
return { __regex: true, source: value.source, flags: value.flags };
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* JSON reviver that restores RegExp objects serialized by regexReplacer.
|
|
67
|
+
*/
|
|
68
|
+
function regexReviver(_key, value) {
|
|
69
|
+
if (value &&
|
|
70
|
+
typeof value === "object" &&
|
|
71
|
+
value.__regex === true) {
|
|
72
|
+
const { source, flags } = value;
|
|
73
|
+
try {
|
|
74
|
+
return new RegExp(source, flags);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
25
82
|
function loadJSON(path) {
|
|
26
83
|
try {
|
|
27
84
|
if (existsSync(path)) {
|
|
28
|
-
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
85
|
+
return JSON.parse(readFileSync(path, 'utf-8'), regexReviver);
|
|
29
86
|
}
|
|
30
87
|
}
|
|
31
88
|
catch { /* ignore malformed files */ }
|
|
@@ -84,6 +141,11 @@ export function loadConfig(options) {
|
|
|
84
141
|
if (projectData) {
|
|
85
142
|
config = deepMerge(config, projectData);
|
|
86
143
|
}
|
|
144
|
+
// Restore RegExp patterns in contentScan that may have been serialized
|
|
145
|
+
// as {} in pre-0.8.0 config files, or merged from user config.
|
|
146
|
+
if (config.security?.contentScan?.dangerousPatterns) {
|
|
147
|
+
config.security.contentScan.dangerousPatterns = restoreDangerousPatterns(config.security.contentScan.dangerousPatterns, DEFAULT_SECURITY_CONFIG.contentScan.dangerousPatterns);
|
|
148
|
+
}
|
|
87
149
|
config = applyEnvVars(config);
|
|
88
150
|
// Decrypt sensitive fields in the loaded config
|
|
89
151
|
try {
|
|
@@ -98,6 +160,7 @@ export function loadConfig(options) {
|
|
|
98
160
|
// If decryption fails, log a warning but continue with the config
|
|
99
161
|
console.warn(t('config.decryption_warning', { error: e.message }));
|
|
100
162
|
}
|
|
163
|
+
// Update global audit notifier with config (done in bootstrap.ts)
|
|
101
164
|
return config;
|
|
102
165
|
}
|
|
103
166
|
export function validateConfig(config, allToolTags) {
|
|
@@ -113,11 +176,11 @@ export function saveConfig(config, configPath) {
|
|
|
113
176
|
try {
|
|
114
177
|
const encryptor = new ConfigEncryptor();
|
|
115
178
|
const encryptedConfig = encryptor.encrypt({ ...config });
|
|
116
|
-
writeFileSync(configPath, JSON.stringify(encryptedConfig,
|
|
179
|
+
writeFileSync(configPath, JSON.stringify(encryptedConfig, regexReplacer, 2), 'utf-8');
|
|
117
180
|
}
|
|
118
181
|
catch (e) {
|
|
119
182
|
// If encryption fails, save without encryption
|
|
120
183
|
console.warn(t('config.encryption_warning', { error: e.message }));
|
|
121
|
-
writeFileSync(configPath, JSON.stringify(config,
|
|
184
|
+
writeFileSync(configPath, JSON.stringify(config, regexReplacer, 2), 'utf-8');
|
|
122
185
|
}
|
|
123
186
|
}
|
package/dist/config/security.js
CHANGED
|
@@ -90,7 +90,7 @@ export const DEFAULT_SECURITY_CONFIG = {
|
|
|
90
90
|
},
|
|
91
91
|
network: {
|
|
92
92
|
// Domains that are always denied
|
|
93
|
-
deniedDomains: [],
|
|
93
|
+
deniedDomains: ["localhost", "127.0.0.1", "::1"],
|
|
94
94
|
// If allowedDomains is non-empty, only these domains are allowed
|
|
95
95
|
allowedDomains: [],
|
|
96
96
|
// Timeout for network requests (ms)
|
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/core/bootstrap.js
CHANGED
|
@@ -64,6 +64,16 @@ export async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplet
|
|
|
64
64
|
: join(process.cwd(), ".mmrc");
|
|
65
65
|
const config = loadConfig({ configDir: dir, projectConfigPath });
|
|
66
66
|
setLocale(config.locale);
|
|
67
|
+
// Update global audit notifier with config
|
|
68
|
+
try {
|
|
69
|
+
const { globalAuditNotifier } = await import("../modules/security/audit-notifier");
|
|
70
|
+
if (config.security?.auditNotifier) {
|
|
71
|
+
globalAuditNotifier.updateConfig(config.security.auditNotifier);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Ignore if audit notifier is not available
|
|
76
|
+
}
|
|
67
77
|
const logger = new Logger(config.logLevel);
|
|
68
78
|
logger.setLogDir(join(dir, "logs"));
|
|
69
79
|
logger.debug("MMA bootstrap", {
|
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}",
|
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}",
|