micro-models-agent 0.41.2 → 0.43.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/bin/mma.mjs +41 -41
- package/dist/cli/commands.js +19 -9
- package/dist/cli/completer.js +37 -36
- package/dist/cli/index.js +2 -2
- package/dist/cli/main.js +23 -48
- package/dist/cli/repl-commands.js +12 -40
- package/dist/cli/repl.js +87 -217
- package/dist/cli/security-commands.js +7 -5
- package/dist/cli/setup.js +26 -8
- package/dist/config/config.js +5 -52
- package/dist/config/defaults.js +5 -29
- package/dist/config/experts.js +1 -1
- package/dist/config/index.js +3 -3
- package/dist/config/security.js +10 -3
- package/dist/core/agent-moe.js +10 -2
- package/dist/core/agent.js +82 -273
- package/dist/core/bootstrap.js +13 -80
- package/dist/core/index.js +2 -2
- package/dist/core/prompt-builder.js +2 -23
- package/dist/core/session-logger.js +4 -46
- package/dist/i18n/en.json +2 -75
- package/dist/i18n/ru.json +1 -74
- package/dist/index.js +1 -1
- package/dist/llm/image-utils.js +5 -4
- package/dist/llm/index.js +4 -4
- package/dist/llm/model-loader.js +6 -6
- package/dist/llm/openai-compat.js +34 -40
- package/dist/llm/orchestrator.js +29 -33
- package/dist/llm/response.js +9 -9
- package/dist/logger/app-logger.js +1 -1
- package/dist/logger/index.js +1 -1
- package/dist/main.js +648 -183
- package/dist/migration/backup.js +13 -13
- package/dist/migration/detect.js +11 -11
- package/dist/migration/index.js +2 -2
- package/dist/modules/browser/actions.js +4 -34
- package/dist/modules/browser/bridge-server.mjs +202 -202
- package/dist/modules/browser/cookie-store.js +6 -6
- package/dist/modules/browser/index.js +5 -7
- package/dist/modules/browser/module.js +7 -8
- package/dist/modules/browser/session.js +84 -87
- package/dist/modules/browser/snapshot.js +58 -92
- package/dist/modules/browser/types.js +1 -4
- package/dist/modules/certification/cli.js +4 -2
- package/dist/modules/certification/fact-checker.js +3 -1
- package/dist/modules/certification/loader.js +9 -3
- package/dist/modules/certification/runner.js +4 -1
- package/dist/modules/context/index.js +1 -1
- package/dist/modules/context/manager.js +86 -160
- package/dist/modules/execution/auditor.js +25 -177
- package/dist/modules/execution/module.js +544 -201
- package/dist/modules/execution/moe-executor.js +0 -25
- package/dist/modules/execution/plan-store.js +3 -1
- package/dist/modules/execution/plan-validator.js +10 -10
- package/dist/modules/execution/planner.js +1 -6
- package/dist/modules/execution/stuck-detector.js +10 -173
- package/dist/modules/execution/verifier.js +42 -86
- package/dist/modules/hallucination/confidence.js +1 -8
- package/dist/modules/hallucination/detector.js +5 -2
- package/dist/modules/hallucination/factual.js +64 -3
- package/dist/modules/hallucination/index.js +1 -1
- package/dist/modules/hallucination/js-identifiers.js +0 -190
- package/dist/modules/hallucination/llm-judge.js +3 -1
- package/dist/modules/indexer/cache.js +7 -9
- package/dist/modules/indexer/index.js +3 -3
- package/dist/modules/indexer/module.js +42 -95
- package/dist/modules/indexer/walker.js +17 -17
- package/dist/modules/lsp/client.js +31 -74
- package/dist/modules/lsp/config.js +33 -87
- package/dist/modules/lsp/index.js +3 -3
- package/dist/modules/lsp/module.js +21 -185
- package/dist/modules/mcp/module.js +6 -2
- package/dist/modules/memory/index.js +1 -1
- package/dist/modules/memory/module.js +23 -71
- package/dist/modules/memory/search.js +9 -11
- package/dist/modules/memory/store.js +13 -13
- package/dist/modules/pipelines/engine.js +10 -10
- package/dist/modules/pipelines/index.js +3 -3
- package/dist/modules/pipelines/parser.js +14 -17
- package/dist/modules/pipelines/template.js +1 -1
- package/dist/modules/plugins/builtin/lint-on-write.js +16 -21
- package/dist/modules/plugins/builtin/notify.js +2 -3
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/loader.js +17 -59
- package/dist/modules/plugins/manager.js +17 -73
- package/dist/modules/processes/index.js +1 -1
- package/dist/modules/processes/registry.js +46 -135
- package/dist/modules/registry.js +2 -4
- package/dist/modules/security/audit-notifier.js +39 -39
- package/dist/modules/security/command-validator.js +8 -2
- package/dist/modules/security/data-sanitizer.js +9 -1
- package/dist/modules/security/encryption.js +56 -58
- package/dist/modules/security/network-validator.js +9 -1
- package/dist/modules/security/path-validator.js +3 -1
- package/dist/modules/security/security-policies.js +19 -3
- package/dist/modules/security/session-encryption.js +1 -1
- package/dist/modules/security/session-isolation.js +8 -8
- package/dist/modules/session/index.js +3 -3
- package/dist/modules/session/module.js +5 -5
- package/dist/modules/session/store.js +9 -3
- package/dist/modules/skills/module.js +2 -1
- package/dist/modules/updater/checker.js +6 -70
- package/dist/modules/updater/index.js +1 -2
- package/dist/modules/user-profile/compressor.js +2 -2
- package/dist/modules/user-profile/index.js +1 -1
- package/dist/modules/user-profile/profile.js +9 -9
- package/dist/tools/attach-image.js +1 -1
- package/dist/tools/bash.js +19 -178
- package/dist/tools/browser.js +29 -46
- package/dist/tools/executor.js +5 -4
- package/dist/tools/file-info.js +12 -13
- package/dist/tools/filter-tools.js +2 -9
- package/dist/tools/glob-tool.js +11 -11
- package/dist/tools/grep-tool.js +3 -1
- package/dist/tools/index.js +2 -13
- package/dist/tools/list-dir.js +17 -18
- package/dist/tools/load-skill.js +3 -1
- package/dist/tools/path-utils.js +4 -4
- package/dist/tools/pipeline-run.js +25 -25
- package/dist/tools/process-kill.js +11 -11
- package/dist/tools/process-list.js +22 -20
- package/dist/tools/process-log.js +18 -22
- package/dist/tools/question.js +3 -1
- package/dist/tools/read-file.js +2 -10
- package/dist/tools/recall.js +37 -44
- package/dist/tools/registry.js +4 -15
- package/dist/tools/remember.js +29 -29
- package/dist/tools/scope-check.js +9 -9
- package/dist/tools/subagent.js +9 -54
- package/dist/tools/user-input.js +1 -1
- package/dist/tools/web-browse.js +3 -3
- package/dist/tools/web-fetch.js +3 -3
- package/dist/tools/web-search.js +3 -3
- package/dist/tools/write-file.js +3 -1
- package/dist/ui/box.js +5 -1
- package/dist/ui/index.js +6 -6
- package/dist/ui/md-formatter.js +33 -33
- package/dist/ui/output.js +5 -5
- package/dist/ui/renderer.js +10 -15
- package/dist/ui/table.js +1 -1
- package/package.json +48 -48
- package/dist/cli/plugin-commands.js +0 -36
- package/dist/cli/run-result.js +0 -22
- package/dist/core/version.js +0 -24
- package/dist/modules/artifacts/store.js +0 -61
- package/dist/modules/browser/bridge-client.js +0 -199
- package/dist/modules/browser/bridge-path.js +0 -10
- package/dist/modules/browser/driver.js +0 -136
- package/dist/modules/context/chunk-query.js +0 -100
- package/dist/modules/context/fact-extractor.js +0 -162
- package/dist/modules/context/history.js +0 -15
- package/dist/modules/execution/audit-runners.js +0 -152
- package/dist/modules/execution/execution-plugin.js +0 -272
- package/dist/modules/execution/plan-tool.js +0 -508
- package/dist/modules/execution/windows-commands.js +0 -41
- package/dist/modules/indexer/project-profile.js +0 -183
- package/dist/modules/lsp/check-tool.js +0 -58
- package/dist/modules/lsp/command.js +0 -60
- package/dist/modules/lsp/probe.js +0 -76
- package/dist/modules/lsp/project-root.js +0 -32
- package/dist/modules/lsp/startup-check.js +0 -141
- package/dist/modules/processes/detect.js +0 -34
- package/dist/modules/skills/matcher.js +0 -27
- package/dist/modules/updater/module.js +0 -116
- package/dist/tools/chunk-query.js +0 -99
- package/dist/tools/download-file.js +0 -116
- package/dist/tools/enable-tools.js +0 -58
- package/dist/tools/hidden-tools-block.js +0 -37
- package/dist/ui/line-editor.js +0 -703
- package/dist/ui/line-math.js +0 -69
- package/dist/ui/plan-view.js +0 -103
|
@@ -1,44 +1,14 @@
|
|
|
1
|
-
import { semverGt } from "../updater/checker";
|
|
2
1
|
export class PluginManager {
|
|
3
2
|
plugins = [];
|
|
4
|
-
|
|
5
|
-
* Register a plugin. Plugins with the same `name` are deduplicated — the
|
|
6
|
-
* higher `version` wins (unversioned plugins count as "0.0.0"). The loser is
|
|
7
|
-
* discarded; `skipped_older` lets the caller warn about the dropped copy.
|
|
8
|
-
*/
|
|
9
|
-
register(plugin, source) {
|
|
10
|
-
if (source)
|
|
11
|
-
plugin.source = source;
|
|
12
|
-
const existing = this.plugins.find((p) => p.name === plugin.name);
|
|
13
|
-
if (existing) {
|
|
14
|
-
if (semverGt(plugin.version || "0.0.0", existing.version || "0.0.0")) {
|
|
15
|
-
this.plugins = this.plugins.filter((p) => p.name !== plugin.name);
|
|
16
|
-
this.plugins.push(plugin);
|
|
17
|
-
this.sort();
|
|
18
|
-
return { status: "registered" };
|
|
19
|
-
}
|
|
20
|
-
return { status: "skipped_older", existing };
|
|
21
|
-
}
|
|
3
|
+
register(plugin) {
|
|
22
4
|
this.plugins.push(plugin);
|
|
23
|
-
this.sort();
|
|
24
|
-
return { status: "registered" };
|
|
25
|
-
}
|
|
26
|
-
sort() {
|
|
27
5
|
this.plugins.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
28
6
|
}
|
|
29
7
|
getAllPlugins() {
|
|
30
8
|
return [...this.plugins];
|
|
31
9
|
}
|
|
32
|
-
/** Detailed plugin list (name, version, source) for REPL / CLI display. */
|
|
33
|
-
getPluginInfos() {
|
|
34
|
-
return [...this.plugins].map((plugin) => ({ plugin, source: plugin.source }));
|
|
35
|
-
}
|
|
36
|
-
/** Find a registered plugin by name, or null. */
|
|
37
|
-
getPlugin(name) {
|
|
38
|
-
return this.plugins.find((p) => p.name === name) ?? null;
|
|
39
|
-
}
|
|
40
10
|
runOnBuildPrompt() {
|
|
41
|
-
return this.plugins.map(
|
|
11
|
+
return this.plugins.map(p => {
|
|
42
12
|
try {
|
|
43
13
|
return p.onBuildPrompt?.() ?? null;
|
|
44
14
|
}
|
|
@@ -55,9 +25,7 @@ export class PluginManager {
|
|
|
55
25
|
if (result === false)
|
|
56
26
|
return false;
|
|
57
27
|
}
|
|
58
|
-
catch {
|
|
59
|
-
/* error isolation */
|
|
60
|
-
}
|
|
28
|
+
catch { /* error isolation */ }
|
|
61
29
|
}
|
|
62
30
|
}
|
|
63
31
|
return true;
|
|
@@ -68,9 +36,7 @@ export class PluginManager {
|
|
|
68
36
|
try {
|
|
69
37
|
await plugin.onAfterTool(ctx, call, result);
|
|
70
38
|
}
|
|
71
|
-
catch {
|
|
72
|
-
/* error isolation */
|
|
73
|
-
}
|
|
39
|
+
catch { /* error isolation */ }
|
|
74
40
|
}
|
|
75
41
|
}
|
|
76
42
|
}
|
|
@@ -80,9 +46,7 @@ export class PluginManager {
|
|
|
80
46
|
try {
|
|
81
47
|
await plugin.onError(ctx, error);
|
|
82
48
|
}
|
|
83
|
-
catch {
|
|
84
|
-
/* error isolation */
|
|
85
|
-
}
|
|
49
|
+
catch { /* error isolation */ }
|
|
86
50
|
}
|
|
87
51
|
}
|
|
88
52
|
}
|
|
@@ -92,9 +56,7 @@ export class PluginManager {
|
|
|
92
56
|
try {
|
|
93
57
|
plugin.onSessionStart(ctx);
|
|
94
58
|
}
|
|
95
|
-
catch {
|
|
96
|
-
/* error isolation */
|
|
97
|
-
}
|
|
59
|
+
catch { /* error isolation */ }
|
|
98
60
|
}
|
|
99
61
|
}
|
|
100
62
|
}
|
|
@@ -104,9 +66,7 @@ export class PluginManager {
|
|
|
104
66
|
try {
|
|
105
67
|
plugin.onBeforeThink(ctx);
|
|
106
68
|
}
|
|
107
|
-
catch {
|
|
108
|
-
/* error isolation */
|
|
109
|
-
}
|
|
69
|
+
catch { /* error isolation */ }
|
|
110
70
|
}
|
|
111
71
|
}
|
|
112
72
|
}
|
|
@@ -116,9 +76,7 @@ export class PluginManager {
|
|
|
116
76
|
try {
|
|
117
77
|
plugin.onAfterThink(ctx, response);
|
|
118
78
|
}
|
|
119
|
-
catch {
|
|
120
|
-
/* error isolation */
|
|
121
|
-
}
|
|
79
|
+
catch { /* error isolation */ }
|
|
122
80
|
}
|
|
123
81
|
}
|
|
124
82
|
}
|
|
@@ -128,9 +86,7 @@ export class PluginManager {
|
|
|
128
86
|
try {
|
|
129
87
|
plugin.onSessionEnd(ctx);
|
|
130
88
|
}
|
|
131
|
-
catch {
|
|
132
|
-
/* error isolation */
|
|
133
|
-
}
|
|
89
|
+
catch { /* error isolation */ }
|
|
134
90
|
}
|
|
135
91
|
}
|
|
136
92
|
}
|
|
@@ -140,9 +96,7 @@ export class PluginManager {
|
|
|
140
96
|
try {
|
|
141
97
|
plugin.onToolCall(ctx);
|
|
142
98
|
}
|
|
143
|
-
catch {
|
|
144
|
-
/* error isolation */
|
|
145
|
-
}
|
|
99
|
+
catch { /* error isolation */ }
|
|
146
100
|
}
|
|
147
101
|
}
|
|
148
102
|
}
|
|
@@ -152,9 +106,7 @@ export class PluginManager {
|
|
|
152
106
|
try {
|
|
153
107
|
plugin.onPhase(ctx, phase);
|
|
154
108
|
}
|
|
155
|
-
catch {
|
|
156
|
-
/* error isolation */
|
|
157
|
-
}
|
|
109
|
+
catch { /* error isolation */ }
|
|
158
110
|
}
|
|
159
111
|
}
|
|
160
112
|
}
|
|
@@ -164,9 +116,7 @@ export class PluginManager {
|
|
|
164
116
|
try {
|
|
165
117
|
plugin.onToolStart(ctx, call);
|
|
166
118
|
}
|
|
167
|
-
catch {
|
|
168
|
-
/* error isolation */
|
|
169
|
-
}
|
|
119
|
+
catch { /* error isolation */ }
|
|
170
120
|
}
|
|
171
121
|
}
|
|
172
122
|
}
|
|
@@ -176,9 +126,7 @@ export class PluginManager {
|
|
|
176
126
|
try {
|
|
177
127
|
plugin.onToolEnd(ctx, call, result, durationMs);
|
|
178
128
|
}
|
|
179
|
-
catch {
|
|
180
|
-
/* error isolation */
|
|
181
|
-
}
|
|
129
|
+
catch { /* error isolation */ }
|
|
182
130
|
}
|
|
183
131
|
}
|
|
184
132
|
}
|
|
@@ -188,12 +136,10 @@ export class PluginManager {
|
|
|
188
136
|
if (plugin.onText) {
|
|
189
137
|
try {
|
|
190
138
|
const out = plugin.onText(ctx, chunk);
|
|
191
|
-
if (typeof out ===
|
|
139
|
+
if (typeof out === 'string')
|
|
192
140
|
chunk = out;
|
|
193
141
|
}
|
|
194
|
-
catch {
|
|
195
|
-
/* error isolation */
|
|
196
|
-
}
|
|
142
|
+
catch { /* error isolation */ }
|
|
197
143
|
}
|
|
198
144
|
}
|
|
199
145
|
return chunk;
|
|
@@ -204,12 +150,10 @@ export class PluginManager {
|
|
|
204
150
|
if (plugin.onMeta) {
|
|
205
151
|
try {
|
|
206
152
|
const out = plugin.onMeta(ctx, chunk);
|
|
207
|
-
if (typeof out ===
|
|
153
|
+
if (typeof out === 'string')
|
|
208
154
|
chunk = out;
|
|
209
155
|
}
|
|
210
|
-
catch {
|
|
211
|
-
/* error isolation */
|
|
212
|
-
}
|
|
156
|
+
catch { /* error isolation */ }
|
|
213
157
|
}
|
|
214
158
|
}
|
|
215
159
|
return chunk;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { registerKillable, unregisterKillable, killByCallId } from "./runner";
|
|
1
|
+
export { registerKillable, unregisterKillable, killByCallId, } from "./runner";
|
|
2
2
|
export { processRegistry } from "./registry";
|
|
@@ -3,107 +3,21 @@ import { platform } from "os";
|
|
|
3
3
|
const MAX_LOG_LINES = 2000;
|
|
4
4
|
const MAX_KEPT_PROCESSES = 20;
|
|
5
5
|
let seq = 0;
|
|
6
|
-
|
|
7
|
-
let oemDecoder;
|
|
8
|
-
function getOemDecoder() {
|
|
9
|
-
if (oemDecoder !== undefined)
|
|
10
|
-
return oemDecoder;
|
|
11
|
-
oemDecoder = null;
|
|
12
|
-
try {
|
|
13
|
-
const cpOut = spawnSync("chcp.com", {
|
|
14
|
-
encoding: "buffer",
|
|
15
|
-
timeout: 2000,
|
|
16
|
-
windowsHide: true,
|
|
17
|
-
}).stdout;
|
|
18
|
-
const m = cpOut ? cpOut.toString("latin1").match(/(\d+)/) : null;
|
|
19
|
-
if (m) {
|
|
20
|
-
try {
|
|
21
|
-
oemDecoder = new TextDecoder("ibm" + m[1]);
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
oemDecoder = null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
/* ignore */
|
|
30
|
-
}
|
|
31
|
-
if (!oemDecoder) {
|
|
32
|
-
try {
|
|
33
|
-
oemDecoder = new TextDecoder("ibm866");
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
oemDecoder = null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return oemDecoder;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Decode one complete line of output to UTF-8.
|
|
43
|
-
*
|
|
44
|
-
* Modern tools (Bun, Node, npm, git) write UTF-8 into the pipe regardless of
|
|
45
|
-
* the console code page, while built-in cmd.exe commands (echo, type, dir)
|
|
46
|
-
* emit the OEM code page (CP866 on Russian Windows). Try strict UTF-8 first;
|
|
47
|
-
* fall back to the OEM decoder when the bytes are not valid UTF-8.
|
|
48
|
-
*/
|
|
49
|
-
function decodeLineToUtf8(buf) {
|
|
50
|
-
try {
|
|
51
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(buf);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
/* not valid UTF-8 → OEM code page output */
|
|
55
|
-
}
|
|
56
|
-
const oem = getOemDecoder();
|
|
57
|
-
if (oem) {
|
|
58
|
-
try {
|
|
59
|
-
return oem.decode(buf);
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
/* unknown encoding — latin1 fallback */
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return buf.toString("latin1");
|
|
66
|
-
}
|
|
67
|
-
/** Synchronous pause without blocking the event loop (via Atomics.wait). */
|
|
68
|
-
function sleepSync(ms) {
|
|
69
|
-
try {
|
|
70
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
/* ignore */
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function taskkillPid(pid) {
|
|
77
|
-
try {
|
|
78
|
-
spawnSync("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
79
|
-
windowsHide: true,
|
|
80
|
-
stdio: "ignore",
|
|
81
|
-
timeout: 5000,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
/* already dead */
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Kill a spawned tree on Windows.
|
|
90
|
-
*
|
|
91
|
-
* `taskkill /T` has a race: when cmd.exe is killed, a child (node) that is
|
|
92
|
-
* still starting up can survive as an orphan because it was never attached to
|
|
93
|
-
* the tree at kill time. A single pass misses it. Running taskkill twice with
|
|
94
|
-
* a settle gap catches those late starters on the second pass — the second
|
|
95
|
-
* tree walk sees the child that was still being created during the first one.
|
|
96
|
-
* This is much cheaper than enumerating processes (wmic/PowerShell), which
|
|
97
|
-
* slows every kill down to seconds under parallel load.
|
|
98
|
-
*/
|
|
99
|
-
export function killTree(child) {
|
|
6
|
+
function killTree(child) {
|
|
100
7
|
const pid = child.pid;
|
|
101
8
|
if (!pid)
|
|
102
9
|
return;
|
|
103
10
|
if (platform() === "win32") {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
11
|
+
try {
|
|
12
|
+
spawnSync("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
13
|
+
windowsHide: true,
|
|
14
|
+
stdio: "ignore",
|
|
15
|
+
timeout: 3000,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
/* already dead */
|
|
20
|
+
}
|
|
107
21
|
return;
|
|
108
22
|
}
|
|
109
23
|
try {
|
|
@@ -159,10 +73,31 @@ class ProcessRegistry {
|
|
|
159
73
|
}
|
|
160
74
|
this.children.set(id, child);
|
|
161
75
|
entry.pid = child.pid ?? 0;
|
|
76
|
+
// On Windows, children spawned via cmd.exe output in the OEM code page,
|
|
77
|
+
// NOT UTF-8. Detect the active code page via chcp.com; fall back to
|
|
78
|
+
// ibm866 (Russian CP866) when detection fails — still correct for the
|
|
79
|
+
// majority of users, and always better than the UTF-8 default.
|
|
80
|
+
const decoder = platform() === "win32"
|
|
81
|
+
? (() => {
|
|
82
|
+
try {
|
|
83
|
+
const cpOut = require("child_process")
|
|
84
|
+
.execSync("chcp.com", {
|
|
85
|
+
encoding: "buffer",
|
|
86
|
+
timeout: 2000,
|
|
87
|
+
windowsHide: true,
|
|
88
|
+
})
|
|
89
|
+
.toString("latin1");
|
|
90
|
+
const m = cpOut.match(/(\d+)/);
|
|
91
|
+
if (m)
|
|
92
|
+
return new TextDecoder("ibm" + m[1]);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
/* ignore */
|
|
96
|
+
}
|
|
97
|
+
return new TextDecoder("ibm866");
|
|
98
|
+
})()
|
|
99
|
+
: new TextDecoder("utf-8");
|
|
162
100
|
let buf = "";
|
|
163
|
-
let byteBuf = Buffer.alloc(0);
|
|
164
|
-
const isWin = platform() === "win32";
|
|
165
|
-
const unixDecoder = new TextDecoder("utf-8");
|
|
166
101
|
const pushLine = (line) => {
|
|
167
102
|
if (entry.log.length >= MAX_LOG_LINES) {
|
|
168
103
|
entry.log.shift();
|
|
@@ -170,40 +105,19 @@ class ProcessRegistry {
|
|
|
170
105
|
entry.log.push(line);
|
|
171
106
|
};
|
|
172
107
|
const append = (chunk) => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
pushLine(line);
|
|
180
|
-
}
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
// Windows: buffer raw bytes and decode per line. Modern tools (Bun, Node,
|
|
184
|
-
// npm, git) write UTF-8 into the pipe regardless of the console code
|
|
185
|
-
// page; cmd.exe builtins (echo, type, dir) emit the OEM code page. Pick
|
|
186
|
-
// the right decoding per line so Russian text survives either way.
|
|
187
|
-
byteBuf = byteBuf.length ? Buffer.concat([byteBuf, chunk]) : Buffer.from(chunk);
|
|
188
|
-
let nl;
|
|
189
|
-
while ((nl = byteBuf.indexOf(0x0a)) !== -1) {
|
|
190
|
-
const raw = byteBuf.subarray(0, nl);
|
|
191
|
-
byteBuf = byteBuf.subarray(nl + 1);
|
|
192
|
-
pushLine(decodeLineToUtf8(raw).replace(/\r$/, ""));
|
|
108
|
+
const decoded = decoder.decode(chunk, { stream: true });
|
|
109
|
+
buf += decoded;
|
|
110
|
+
const lines = buf.split(/\r?\n/);
|
|
111
|
+
buf = lines.pop() ?? "";
|
|
112
|
+
for (const line of lines) {
|
|
113
|
+
pushLine(line);
|
|
193
114
|
}
|
|
194
115
|
};
|
|
195
116
|
const flush = () => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
buf = "";
|
|
201
|
-
}
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
if (byteBuf.length) {
|
|
205
|
-
pushLine(decodeLineToUtf8(byteBuf));
|
|
206
|
-
byteBuf = Buffer.alloc(0);
|
|
117
|
+
buf += decoder.decode();
|
|
118
|
+
if (buf) {
|
|
119
|
+
pushLine(buf);
|
|
120
|
+
buf = "";
|
|
207
121
|
}
|
|
208
122
|
};
|
|
209
123
|
const resolveExit = () => {
|
|
@@ -216,10 +130,7 @@ class ProcessRegistry {
|
|
|
216
130
|
entry.status = "killed";
|
|
217
131
|
}
|
|
218
132
|
entry.spawnError = err.message;
|
|
219
|
-
|
|
220
|
-
// never emits `close`, so the buffered line would never be flushed and
|
|
221
|
-
// the log would be empty (observed: "records a spawn error" test).
|
|
222
|
-
pushLine(`[process error] ${err.message}`);
|
|
133
|
+
append(Buffer.from(`[process error] ${err.message}`));
|
|
223
134
|
resolveExit();
|
|
224
135
|
});
|
|
225
136
|
child.on("close", (code) => {
|
package/dist/modules/registry.js
CHANGED
|
@@ -9,11 +9,9 @@ export class ModuleRegistry {
|
|
|
9
9
|
listModules() {
|
|
10
10
|
return Array.from(this.modules.keys()).sort();
|
|
11
11
|
}
|
|
12
|
-
collectPromptBlocks(
|
|
12
|
+
collectPromptBlocks() {
|
|
13
13
|
const blocks = [];
|
|
14
|
-
for (const
|
|
15
|
-
if (exclude.includes(name))
|
|
16
|
-
continue;
|
|
14
|
+
for (const mod of this.modules.values()) {
|
|
17
15
|
if (mod.getSystemPromptBlock) {
|
|
18
16
|
const block = mod.getSystemPromptBlock();
|
|
19
17
|
if (block)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { writeFileSync, appendFileSync, existsSync, mkdirSync } from
|
|
2
|
-
import { join, dirname } from
|
|
3
|
-
import { homedir } from
|
|
1
|
+
import { writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
4
|
/**
|
|
5
5
|
* Severity weights for comparison
|
|
6
6
|
*/
|
|
@@ -15,16 +15,16 @@ const SEVERITY_WEIGHTS = {
|
|
|
15
15
|
*/
|
|
16
16
|
export const DEFAULT_AUDIT_NOTIFIER_CONFIG = {
|
|
17
17
|
enabled: false,
|
|
18
|
-
filePath: join(homedir(),
|
|
18
|
+
filePath: join(homedir(), '.mma', 'logs', 'audit-notifications.jsonl'),
|
|
19
19
|
webhookTimeout: 5000,
|
|
20
|
-
minSeverity:
|
|
20
|
+
minSeverity: 'medium',
|
|
21
21
|
eventTypes: [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
'security_block',
|
|
23
|
+
'bash_command',
|
|
24
|
+
'file_operation',
|
|
25
|
+
'network_request',
|
|
26
|
+
'authentication',
|
|
27
|
+
'configuration_change',
|
|
28
28
|
],
|
|
29
29
|
maxRetries: 3,
|
|
30
30
|
};
|
|
@@ -85,7 +85,7 @@ export class AuditNotifier {
|
|
|
85
85
|
* Check if a severity level should be notified
|
|
86
86
|
*/
|
|
87
87
|
shouldNotifySeverity(severity) {
|
|
88
|
-
const minSeverity = this.config.minSeverity ??
|
|
88
|
+
const minSeverity = this.config.minSeverity ?? 'medium';
|
|
89
89
|
const minWeight = SEVERITY_WEIGHTS[minSeverity];
|
|
90
90
|
const eventWeight = SEVERITY_WEIGHTS[severity];
|
|
91
91
|
return eventWeight >= minWeight;
|
|
@@ -123,12 +123,12 @@ export class AuditNotifier {
|
|
|
123
123
|
*/
|
|
124
124
|
mapEventType(action) {
|
|
125
125
|
const typeMap = {
|
|
126
|
-
tool_call:
|
|
127
|
-
bash_command:
|
|
128
|
-
file_write:
|
|
129
|
-
file_delete:
|
|
130
|
-
network_request:
|
|
131
|
-
security_block:
|
|
126
|
+
'tool_call': 'tool_call',
|
|
127
|
+
'bash_command': 'bash_command',
|
|
128
|
+
'file_write': 'file_operation',
|
|
129
|
+
'file_delete': 'file_operation',
|
|
130
|
+
'network_request': 'network_request',
|
|
131
|
+
'security_block': 'security_block',
|
|
132
132
|
};
|
|
133
133
|
return typeMap[action] || null;
|
|
134
134
|
}
|
|
@@ -138,16 +138,16 @@ export class AuditNotifier {
|
|
|
138
138
|
determineSeverity(entry) {
|
|
139
139
|
// Default severity based on action type
|
|
140
140
|
const typeSeverity = {
|
|
141
|
-
security_block:
|
|
142
|
-
bash_command:
|
|
143
|
-
file_delete:
|
|
144
|
-
file_write:
|
|
145
|
-
network_request:
|
|
146
|
-
tool_call:
|
|
147
|
-
session_start:
|
|
148
|
-
session_end:
|
|
141
|
+
'security_block': 'high',
|
|
142
|
+
'bash_command': 'high',
|
|
143
|
+
'file_delete': 'medium',
|
|
144
|
+
'file_write': 'medium',
|
|
145
|
+
'network_request': 'medium',
|
|
146
|
+
'tool_call': 'low',
|
|
147
|
+
'session_start': 'low',
|
|
148
|
+
'session_end': 'low',
|
|
149
149
|
};
|
|
150
|
-
return typeSeverity[entry.action] ||
|
|
150
|
+
return typeSeverity[entry.action] || 'low';
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
153
|
* Write notification to file
|
|
@@ -157,10 +157,10 @@ export class AuditNotifier {
|
|
|
157
157
|
return;
|
|
158
158
|
try {
|
|
159
159
|
const line = JSON.stringify(notification);
|
|
160
|
-
appendFileSync(this.config.filePath, line +
|
|
160
|
+
appendFileSync(this.config.filePath, line + '\n', 'utf8');
|
|
161
161
|
}
|
|
162
162
|
catch (error) {
|
|
163
|
-
console.error(
|
|
163
|
+
console.error('[AuditNotifier] Failed to write to file:', error);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
@@ -173,8 +173,8 @@ export class AuditNotifier {
|
|
|
173
173
|
const controller = new AbortController();
|
|
174
174
|
const timeoutId = setTimeout(() => controller.abort(), this.config.webhookTimeout);
|
|
175
175
|
const response = await fetch(this.config.webhookUrl, {
|
|
176
|
-
method:
|
|
177
|
-
headers: {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
headers: { 'Content-Type': 'application/json' },
|
|
178
178
|
body: JSON.stringify(notification),
|
|
179
179
|
signal: controller.signal,
|
|
180
180
|
});
|
|
@@ -184,7 +184,7 @@ export class AuditNotifier {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
catch (error) {
|
|
187
|
-
console.error(
|
|
187
|
+
console.error('[AuditNotifier] Webhook failed, adding to retry queue:', error);
|
|
188
188
|
this.addToRetryQueue(notification);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
@@ -207,7 +207,7 @@ export class AuditNotifier {
|
|
|
207
207
|
while (this.retryQueue.length > 0) {
|
|
208
208
|
const item = this.retryQueue[0];
|
|
209
209
|
if (item.retries >= (this.config.maxRetries ?? 3)) {
|
|
210
|
-
console.error(
|
|
210
|
+
console.error('[AuditNotifier] Max retries exceeded for notification:', item.entry);
|
|
211
211
|
this.retryQueue.shift();
|
|
212
212
|
continue;
|
|
213
213
|
}
|
|
@@ -219,7 +219,7 @@ export class AuditNotifier {
|
|
|
219
219
|
item.retries++;
|
|
220
220
|
// Exponential backoff
|
|
221
221
|
const delay = Math.pow(2, item.retries) * 1000;
|
|
222
|
-
await new Promise(
|
|
222
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
this.isProcessing = false;
|
|
@@ -232,9 +232,9 @@ export class AuditNotifier {
|
|
|
232
232
|
return [];
|
|
233
233
|
}
|
|
234
234
|
try {
|
|
235
|
-
const content = readFileSync(this.config.filePath,
|
|
236
|
-
const lines = content.split(
|
|
237
|
-
return lines.slice(-limit).map(
|
|
235
|
+
const content = readFileSync(this.config.filePath, 'utf8');
|
|
236
|
+
const lines = content.split('\n').filter(Boolean);
|
|
237
|
+
return lines.slice(-limit).map(line => JSON.parse(line));
|
|
238
238
|
}
|
|
239
239
|
catch {
|
|
240
240
|
return [];
|
|
@@ -245,7 +245,7 @@ export class AuditNotifier {
|
|
|
245
245
|
*/
|
|
246
246
|
clearNotifications() {
|
|
247
247
|
if (this.config.filePath) {
|
|
248
|
-
writeFileSync(this.config.filePath,
|
|
248
|
+
writeFileSync(this.config.filePath, '', 'utf8');
|
|
249
249
|
}
|
|
250
250
|
this.retryQueue = [];
|
|
251
251
|
}
|
|
@@ -289,4 +289,4 @@ export function createAuditNotifier(config) {
|
|
|
289
289
|
return new AuditNotifier(config);
|
|
290
290
|
}
|
|
291
291
|
// Import readFileSync for readNotifications
|
|
292
|
-
import { readFileSync } from
|
|
292
|
+
import { readFileSync } from 'fs';
|
|
@@ -46,7 +46,11 @@ function extractBaseCommand(trimmed) {
|
|
|
46
46
|
i++;
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
49
|
-
if (tok === "sudo" ||
|
|
49
|
+
if (tok === "sudo" ||
|
|
50
|
+
tok === "env" ||
|
|
51
|
+
tok === "command" ||
|
|
52
|
+
tok === "exec" ||
|
|
53
|
+
tok === "nohup") {
|
|
50
54
|
i++;
|
|
51
55
|
continue;
|
|
52
56
|
}
|
|
@@ -146,7 +150,9 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
146
150
|
};
|
|
147
151
|
}
|
|
148
152
|
// Also check the raw first token in case it's a simple name
|
|
149
|
-
const rawFirst = trimmedCommand
|
|
153
|
+
const rawFirst = trimmedCommand
|
|
154
|
+
.split(/\s+/)[0]
|
|
155
|
+
.replace(/\.(exe|cmd|bat)$/i, "");
|
|
150
156
|
if (rawFirst !== baseCommand && config.blacklist.includes(rawFirst)) {
|
|
151
157
|
return {
|
|
152
158
|
allowed: false,
|
|
@@ -73,7 +73,15 @@ export function sanitizeCommand(command) {
|
|
|
73
73
|
export function sanitizeUrl(url) {
|
|
74
74
|
try {
|
|
75
75
|
const urlObj = new URL(url);
|
|
76
|
-
const sensitiveKeys = [
|
|
76
|
+
const sensitiveKeys = [
|
|
77
|
+
"api_key",
|
|
78
|
+
"token",
|
|
79
|
+
"password",
|
|
80
|
+
"secret",
|
|
81
|
+
"access_token",
|
|
82
|
+
"auth",
|
|
83
|
+
"key",
|
|
84
|
+
];
|
|
77
85
|
const sanitizedParams = new URLSearchParams(urlObj.search);
|
|
78
86
|
for (const key of sensitiveKeys) {
|
|
79
87
|
if (sanitizedParams.has(key)) {
|