micro-models-agent 0.28.9 → 0.29.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.
Files changed (167) hide show
  1. package/dist/cli/commands.js +220 -0
  2. package/dist/cli/completer.js +168 -0
  3. package/dist/cli/index.js +2 -0
  4. package/dist/cli/main.js +113 -0
  5. package/dist/cli/repl.js +987 -0
  6. package/dist/cli/security-commands.js +166 -0
  7. package/dist/cli/setup.js +229 -0
  8. package/dist/config/config.js +186 -0
  9. package/dist/config/defaults.js +91 -0
  10. package/dist/config/experts.js +15 -0
  11. package/dist/config/index.js +3 -0
  12. package/dist/config/security.js +193 -0
  13. package/dist/config/types.js +1 -0
  14. package/dist/core/agent-moe.js +98 -0
  15. package/dist/core/agent.js +461 -0
  16. package/dist/core/bootstrap.js +321 -0
  17. package/dist/core/index.js +2 -0
  18. package/dist/core/prompt-builder.js +55 -0
  19. package/dist/core/session-logger.js +122 -0
  20. package/dist/core/types.js +1 -0
  21. package/dist/i18n/en.json +461 -0
  22. package/dist/i18n/index.js +43 -0
  23. package/dist/i18n/ru.json +461 -0
  24. package/dist/index.js +22 -0
  25. package/dist/llm/image-utils.js +144 -0
  26. package/dist/llm/index.js +4 -0
  27. package/dist/llm/model-loader.js +78 -0
  28. package/dist/llm/openai-compat.js +324 -0
  29. package/dist/llm/orchestrator.js +194 -0
  30. package/dist/llm/provider.js +10 -0
  31. package/dist/llm/response.js +39 -0
  32. package/dist/llm/token-counter.js +39 -0
  33. package/dist/llm/types.js +1 -0
  34. package/dist/logger/app-logger.js +76 -0
  35. package/dist/logger/index.js +1 -0
  36. package/dist/main.js +2251 -724
  37. package/dist/migration/backup.js +45 -0
  38. package/dist/migration/detect.js +50 -0
  39. package/dist/migration/index.js +2 -0
  40. package/dist/modules/browser/actions.js +46 -0
  41. package/dist/modules/browser/cookie-store.js +24 -0
  42. package/dist/modules/browser/index.js +5 -0
  43. package/dist/modules/browser/module.js +28 -0
  44. package/dist/modules/browser/session.js +287 -0
  45. package/dist/modules/browser/snapshot.js +114 -0
  46. package/dist/modules/browser/types.js +9 -0
  47. package/dist/modules/context/history.js +15 -0
  48. package/dist/modules/context/index.js +1 -0
  49. package/dist/modules/context/manager.js +240 -0
  50. package/dist/modules/execution/auditor.js +72 -0
  51. package/dist/modules/execution/index.js +6 -0
  52. package/dist/modules/execution/module.js +337 -0
  53. package/dist/modules/execution/moe-executor.js +209 -0
  54. package/dist/modules/execution/plan-validator.js +153 -0
  55. package/dist/modules/execution/planner.js +35 -0
  56. package/dist/modules/execution/stuck-detector.js +134 -0
  57. package/dist/modules/execution/tracker.js +53 -0
  58. package/dist/modules/execution/types.js +1 -0
  59. package/dist/modules/execution/verifier.js +149 -0
  60. package/dist/modules/hallucination/confidence.js +54 -0
  61. package/dist/modules/hallucination/consistency.js +60 -0
  62. package/dist/modules/hallucination/detector.js +41 -0
  63. package/dist/modules/hallucination/factual.js +170 -0
  64. package/dist/modules/hallucination/index.js +4 -0
  65. package/dist/modules/index.js +5 -0
  66. package/dist/modules/indexer/cache.js +38 -0
  67. package/dist/modules/indexer/index.js +3 -0
  68. package/dist/modules/indexer/module.js +192 -0
  69. package/dist/modules/indexer/walker.js +101 -0
  70. package/dist/modules/mcp/client.js +393 -0
  71. package/dist/modules/mcp/index.js +3 -0
  72. package/dist/modules/mcp/module.js +146 -0
  73. package/dist/modules/mcp/registry.js +15 -0
  74. package/dist/modules/memory/index.js +1 -0
  75. package/dist/modules/memory/module.js +48 -0
  76. package/dist/modules/memory/search.js +40 -0
  77. package/dist/modules/memory/store.js +65 -0
  78. package/dist/modules/pipelines/engine.js +60 -0
  79. package/dist/modules/pipelines/index.js +3 -0
  80. package/dist/modules/pipelines/parser.js +53 -0
  81. package/dist/modules/pipelines/template.js +14 -0
  82. package/dist/modules/plugins/builtin/lint-on-write.js +121 -0
  83. package/dist/modules/plugins/builtin/notify.js +8 -0
  84. package/dist/modules/plugins/index.js +1 -0
  85. package/dist/modules/plugins/loader.js +28 -0
  86. package/dist/modules/plugins/manager.js +161 -0
  87. package/dist/modules/plugins/types.js +1 -0
  88. package/dist/modules/processes/detect.js +34 -0
  89. package/dist/modules/processes/index.js +3 -0
  90. package/dist/modules/processes/registry.js +148 -0
  91. package/dist/modules/processes/runner.js +124 -0
  92. package/dist/modules/registry.js +45 -0
  93. package/dist/modules/security/audit-log.js +116 -0
  94. package/dist/modules/security/audit-notifier.js +292 -0
  95. package/dist/modules/security/command-validator.js +185 -0
  96. package/dist/modules/security/content-scanner.js +52 -0
  97. package/dist/modules/security/data-sanitizer.js +97 -0
  98. package/dist/modules/security/encryption.js +240 -0
  99. package/dist/modules/security/index.js +14 -0
  100. package/dist/modules/security/network-validator.js +79 -0
  101. package/dist/modules/security/path-validator.js +155 -0
  102. package/dist/modules/security/rate-limiter.js +119 -0
  103. package/dist/modules/security/security-policies.js +393 -0
  104. package/dist/modules/security/session-encryption.js +193 -0
  105. package/dist/modules/security/session-isolation.js +95 -0
  106. package/dist/modules/session/index.js +3 -0
  107. package/dist/modules/session/manager.js +167 -0
  108. package/dist/modules/session/module.js +24 -0
  109. package/dist/modules/session/store.js +174 -0
  110. package/dist/modules/session/types.js +1 -0
  111. package/dist/modules/skills/index.js +3 -0
  112. package/dist/modules/skills/loader.js +72 -0
  113. package/dist/modules/skills/matcher.js +27 -0
  114. package/dist/modules/skills/module.js +143 -0
  115. package/dist/modules/types.js +1 -0
  116. package/dist/modules/updater/checker.js +32 -0
  117. package/dist/modules/updater/index.js +1 -0
  118. package/dist/modules/user-profile/compressor.js +16 -0
  119. package/dist/modules/user-profile/index.js +1 -0
  120. package/dist/modules/user-profile/profile.js +68 -0
  121. package/dist/tools/approve.js +32 -0
  122. package/dist/tools/attach-image.js +89 -0
  123. package/dist/tools/bash.js +140 -0
  124. package/dist/tools/browser.js +97 -0
  125. package/dist/tools/create-dir.js +56 -0
  126. package/dist/tools/delete-file.js +63 -0
  127. package/dist/tools/edit-file.js +77 -0
  128. package/dist/tools/executor.js +95 -0
  129. package/dist/tools/file-info.js +45 -0
  130. package/dist/tools/filter-tools.js +10 -0
  131. package/dist/tools/glob-tool.js +26 -0
  132. package/dist/tools/grep-tool.js +64 -0
  133. package/dist/tools/index.js +52 -0
  134. package/dist/tools/list-dir.js +47 -0
  135. package/dist/tools/load-skill.js +48 -0
  136. package/dist/tools/mcp-call.js +68 -0
  137. package/dist/tools/move-file.js +84 -0
  138. package/dist/tools/path-utils.js +51 -0
  139. package/dist/tools/pipeline-run.js +144 -0
  140. package/dist/tools/preview.js +2 -0
  141. package/dist/tools/process-kill.js +29 -0
  142. package/dist/tools/process-list.js +38 -0
  143. package/dist/tools/process-log.js +41 -0
  144. package/dist/tools/question.js +142 -0
  145. package/dist/tools/read-file.js +73 -0
  146. package/dist/tools/recall.js +110 -0
  147. package/dist/tools/registry.js +36 -0
  148. package/dist/tools/remember.js +67 -0
  149. package/dist/tools/scope-check.js +30 -0
  150. package/dist/tools/search-history.js +64 -0
  151. package/dist/tools/subagent.js +142 -0
  152. package/dist/tools/types.js +1 -0
  153. package/dist/tools/user-input.js +123 -0
  154. package/dist/tools/web-browse.js +57 -0
  155. package/dist/tools/web-fetch.js +72 -0
  156. package/dist/tools/web-search.js +59 -0
  157. package/dist/tools/write-file.js +80 -0
  158. package/dist/ui/box.js +81 -0
  159. package/dist/ui/colors.js +4 -0
  160. package/dist/ui/diff.js +185 -0
  161. package/dist/ui/index.js +6 -0
  162. package/dist/ui/md-formatter.js +212 -0
  163. package/dist/ui/output.js +13 -0
  164. package/dist/ui/renderer.js +141 -0
  165. package/dist/ui/spinner.js +70 -0
  166. package/dist/ui/table.js +144 -0
  167. package/package.json +4 -4
@@ -0,0 +1,161 @@
1
+ export class PluginManager {
2
+ plugins = [];
3
+ register(plugin) {
4
+ this.plugins.push(plugin);
5
+ this.plugins.sort((a, b) => (b.priority || 0) - (a.priority || 0));
6
+ }
7
+ getAllPlugins() {
8
+ return [...this.plugins];
9
+ }
10
+ runOnBuildPrompt() {
11
+ return this.plugins.map(p => {
12
+ try {
13
+ return p.onBuildPrompt?.() ?? null;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ });
19
+ }
20
+ async runOnBeforeTool(ctx, call) {
21
+ for (const plugin of this.plugins) {
22
+ if (plugin.onBeforeTool) {
23
+ try {
24
+ const result = await plugin.onBeforeTool(ctx, call);
25
+ if (result === false)
26
+ return false;
27
+ }
28
+ catch { /* error isolation */ }
29
+ }
30
+ }
31
+ return true;
32
+ }
33
+ async runOnAfterTool(ctx, call, result) {
34
+ for (const plugin of this.plugins) {
35
+ if (plugin.onAfterTool) {
36
+ try {
37
+ await plugin.onAfterTool(ctx, call, result);
38
+ }
39
+ catch { /* error isolation */ }
40
+ }
41
+ }
42
+ }
43
+ async runOnError(ctx, error) {
44
+ for (const plugin of this.plugins) {
45
+ if (plugin.onError) {
46
+ try {
47
+ await plugin.onError(ctx, error);
48
+ }
49
+ catch { /* error isolation */ }
50
+ }
51
+ }
52
+ }
53
+ runOnSessionStart(ctx) {
54
+ for (const plugin of this.plugins) {
55
+ if (plugin.onSessionStart) {
56
+ try {
57
+ plugin.onSessionStart(ctx);
58
+ }
59
+ catch { /* error isolation */ }
60
+ }
61
+ }
62
+ }
63
+ runOnBeforeThink(ctx) {
64
+ for (const plugin of this.plugins) {
65
+ if (plugin.onBeforeThink) {
66
+ try {
67
+ plugin.onBeforeThink(ctx);
68
+ }
69
+ catch { /* error isolation */ }
70
+ }
71
+ }
72
+ }
73
+ runOnAfterThink(ctx, response) {
74
+ for (const plugin of this.plugins) {
75
+ if (plugin.onAfterThink) {
76
+ try {
77
+ plugin.onAfterThink(ctx, response);
78
+ }
79
+ catch { /* error isolation */ }
80
+ }
81
+ }
82
+ }
83
+ runOnSessionEnd(ctx) {
84
+ for (const plugin of this.plugins) {
85
+ if (plugin.onSessionEnd) {
86
+ try {
87
+ plugin.onSessionEnd(ctx);
88
+ }
89
+ catch { /* error isolation */ }
90
+ }
91
+ }
92
+ }
93
+ runOnToolCall(ctx) {
94
+ for (const plugin of this.plugins) {
95
+ if (plugin.onToolCall) {
96
+ try {
97
+ plugin.onToolCall(ctx);
98
+ }
99
+ catch { /* error isolation */ }
100
+ }
101
+ }
102
+ }
103
+ runOnPhase(ctx, phase) {
104
+ for (const plugin of this.plugins) {
105
+ if (plugin.onPhase) {
106
+ try {
107
+ plugin.onPhase(ctx, phase);
108
+ }
109
+ catch { /* error isolation */ }
110
+ }
111
+ }
112
+ }
113
+ runOnToolStart(ctx, call) {
114
+ for (const plugin of this.plugins) {
115
+ if (plugin.onToolStart) {
116
+ try {
117
+ plugin.onToolStart(ctx, call);
118
+ }
119
+ catch { /* error isolation */ }
120
+ }
121
+ }
122
+ }
123
+ runOnToolEnd(ctx, call, result, durationMs) {
124
+ for (const plugin of this.plugins) {
125
+ if (plugin.onToolEnd) {
126
+ try {
127
+ plugin.onToolEnd(ctx, call, result, durationMs);
128
+ }
129
+ catch { /* error isolation */ }
130
+ }
131
+ }
132
+ }
133
+ /** Pipe a text chunk through plugin transforms; returns the (possibly replaced) chunk. */
134
+ runOnText(ctx, chunk) {
135
+ for (const plugin of this.plugins) {
136
+ if (plugin.onText) {
137
+ try {
138
+ const out = plugin.onText(ctx, chunk);
139
+ if (typeof out === 'string')
140
+ chunk = out;
141
+ }
142
+ catch { /* error isolation */ }
143
+ }
144
+ }
145
+ return chunk;
146
+ }
147
+ /** Pipe a meta chunk through plugin transforms; returns the (possibly replaced) chunk. */
148
+ runOnMeta(ctx, chunk) {
149
+ for (const plugin of this.plugins) {
150
+ if (plugin.onMeta) {
151
+ try {
152
+ const out = plugin.onMeta(ctx, chunk);
153
+ if (typeof out === 'string')
154
+ chunk = out;
155
+ }
156
+ catch { /* error isolation */ }
157
+ }
158
+ }
159
+ return chunk;
160
+ }
161
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Heuristic detection of long-running (server/watch) commands.
3
+ *
4
+ * The explicit `background: true` bash parameter always wins; this list is a
5
+ * convenience so dev servers don't block the agent. It only inspects the raw
6
+ * shell command (a technical process-type decision, not task classification).
7
+ */
8
+ const LONG_RUNNING_PATTERNS = [
9
+ /\b(npm|pnpm|yarn|bun|npx)\s+(run\s+)?(dev|start|serve|preview|watch|server)\b/i,
10
+ /\b(deno|node)\s+.*\b(run\s+)?(dev|serve|watch|server)\b/i,
11
+ /(^|\s)--watch\b/i,
12
+ /(^|\s)-w\b/i,
13
+ /\bnodemon\b/i,
14
+ /\btsx watch\b/i,
15
+ /\b(ts-node|tsc)\s+.*--watch\b/i,
16
+ /\bvite(?!\s+(build|create))\b/i,
17
+ /\bwebpack(-dev-server|\s+serve)\b/i,
18
+ /\bastro dev\b/i,
19
+ /\bnext (dev|start)\b/i,
20
+ /\bnuxt (dev|start)\b/i,
21
+ /\bsvelte-kit (dev|preview)\b/i,
22
+ /\bgatsby develop\b/i,
23
+ /\bdocker(-compose)?\s+.*\bup\b/i,
24
+ /\buvicorn\b|\bgunicorn\b/i,
25
+ /\bpython\s+-m\s+http\.server\b/i,
26
+ /\bflask run\b/i,
27
+ /\bphp artisan serve\b/i,
28
+ /\brails (server|s)\b/i,
29
+ /\bvitest watch\b/i,
30
+ /\bjest --watch\b/i,
31
+ ];
32
+ export function isLongRunningCommand(command) {
33
+ return LONG_RUNNING_PATTERNS.some((pattern) => pattern.test(command));
34
+ }
@@ -0,0 +1,3 @@
1
+ export { runCommand, killByCallId } from "./runner";
2
+ export { processRegistry } from "./registry";
3
+ export { isLongRunningCommand } from "./detect";
@@ -0,0 +1,148 @@
1
+ import { spawn } from "child_process";
2
+ import { platform } from "os";
3
+ const MAX_LOG_LINES = 300;
4
+ const MAX_KEPT_PROCESSES = 20;
5
+ let seq = 0;
6
+ function killTree(child) {
7
+ const pid = child.pid;
8
+ if (!pid)
9
+ return;
10
+ if (platform() === "win32") {
11
+ spawn("taskkill", ["/pid", String(pid), "/T", "/F"], {
12
+ windowsHide: true,
13
+ stdio: "ignore",
14
+ });
15
+ return;
16
+ }
17
+ try {
18
+ process.kill(-pid, "SIGTERM");
19
+ }
20
+ catch {
21
+ try {
22
+ child.kill("SIGKILL");
23
+ }
24
+ catch {
25
+ /* already dead */
26
+ }
27
+ }
28
+ }
29
+ class ProcessRegistry {
30
+ procs = new Map();
31
+ children = new Map();
32
+ /**
33
+ * Start a background process. Returns immediately with the entry; output is
34
+ * buffered into `entry.log` as it arrives.
35
+ */
36
+ start(command, cwd, sessionId) {
37
+ const id = `proc_${Date.now()}_${++seq}`;
38
+ const entry = {
39
+ id,
40
+ pid: 0,
41
+ command,
42
+ cwd,
43
+ sessionId,
44
+ status: "running",
45
+ exitCode: null,
46
+ startedAt: new Date().toISOString(),
47
+ log: [],
48
+ };
49
+ this.trimOldEntries();
50
+ this.procs.set(id, entry);
51
+ const child = spawn(command, {
52
+ cwd,
53
+ shell: true,
54
+ windowsHide: true,
55
+ detached: platform() !== "win32",
56
+ stdio: ["ignore", "pipe", "pipe"],
57
+ });
58
+ this.children.set(id, child);
59
+ entry.pid = child.pid ?? 0;
60
+ let partial = "";
61
+ const append = (chunk) => {
62
+ const text = partial + chunk.toString();
63
+ const lines = text.split(/\r?\n/);
64
+ partial = lines.pop() ?? "";
65
+ for (const line of lines) {
66
+ if (entry.log.length >= MAX_LOG_LINES) {
67
+ entry.log.shift();
68
+ }
69
+ entry.log.push(line);
70
+ }
71
+ };
72
+ child.stdout?.on("data", append);
73
+ child.stderr?.on("data", append);
74
+ child.on("error", (err) => {
75
+ if (entry.status === "running") {
76
+ entry.status = "killed";
77
+ }
78
+ append(Buffer.from(`[process error] ${err.message}`));
79
+ });
80
+ child.on("close", (code) => {
81
+ if (entry.status === "running") {
82
+ entry.status = "exited";
83
+ }
84
+ entry.exitCode = code;
85
+ this.children.delete(id);
86
+ if (partial) {
87
+ entry.log.push(partial);
88
+ partial = "";
89
+ }
90
+ });
91
+ return entry;
92
+ }
93
+ list(sessionId) {
94
+ const all = Array.from(this.procs.values());
95
+ if (!sessionId)
96
+ return all;
97
+ return all.filter((p) => p.sessionId === sessionId);
98
+ }
99
+ get(id) {
100
+ return this.procs.get(id);
101
+ }
102
+ getLog(id, tail) {
103
+ const entry = this.procs.get(id);
104
+ if (!entry)
105
+ return "";
106
+ const lines = tail && tail > 0 ? entry.log.slice(-tail) : entry.log;
107
+ return lines.join("\n");
108
+ }
109
+ /** Kill a background process tree. Returns false when not found. */
110
+ kill(id) {
111
+ const entry = this.procs.get(id);
112
+ if (!entry)
113
+ return false;
114
+ if (entry.status === "running") {
115
+ entry.status = "killed";
116
+ const child = this.children.get(id);
117
+ if (child) {
118
+ killTree(child);
119
+ }
120
+ }
121
+ return true;
122
+ }
123
+ /** Kill every managed background process (used on agent shutdown). */
124
+ killAll(sessionId) {
125
+ const targets = this.list(sessionId);
126
+ for (const entry of targets) {
127
+ this.kill(entry.id);
128
+ }
129
+ return targets.length;
130
+ }
131
+ trimOldEntries() {
132
+ if (this.procs.size < MAX_KEPT_PROCESSES)
133
+ return;
134
+ const sorted = Array.from(this.procs.values()).sort((a, b) => a.startedAt.localeCompare(b.startedAt));
135
+ const toRemove = sorted.slice(0, sorted.length - MAX_KEPT_PROCESSES + 1);
136
+ for (const entry of toRemove) {
137
+ if (entry.status === "running") {
138
+ const child = this.children.get(entry.id);
139
+ if (child) {
140
+ killTree(child);
141
+ }
142
+ }
143
+ this.procs.delete(entry.id);
144
+ this.children.delete(entry.id);
145
+ }
146
+ }
147
+ }
148
+ export const processRegistry = new ProcessRegistry();
@@ -0,0 +1,124 @@
1
+ import { spawn } from "child_process";
2
+ import { platform } from "os";
3
+ const activeChildren = new Map();
4
+ /**
5
+ * Kill a foreground command previously registered by `callId` (used by the
6
+ * tool executor when the execution timeout fires). Returns true when a child
7
+ * was found and killed.
8
+ */
9
+ export function killByCallId(callId) {
10
+ const entry = activeChildren.get(callId);
11
+ if (!entry)
12
+ return false;
13
+ activeChildren.delete(callId);
14
+ try {
15
+ entry.kill();
16
+ }
17
+ catch {
18
+ /* already dead */
19
+ }
20
+ return true;
21
+ }
22
+ function killTree(child) {
23
+ const pid = child.pid;
24
+ if (!pid)
25
+ return;
26
+ if (platform() === "win32") {
27
+ spawn("taskkill", ["/pid", String(pid), "/T", "/F"], {
28
+ windowsHide: true,
29
+ stdio: "ignore",
30
+ });
31
+ return;
32
+ }
33
+ try {
34
+ // `detached: true` makes the child a process-group leader on POSIX,
35
+ // so killing -pid terminates the whole tree.
36
+ process.kill(-pid, "SIGTERM");
37
+ }
38
+ catch {
39
+ try {
40
+ child.kill("SIGKILL");
41
+ }
42
+ catch {
43
+ /* already dead */
44
+ }
45
+ }
46
+ }
47
+ /**
48
+ * Run a shell command asynchronously (non-blocking event loop) and collect
49
+ * stdout/stderr. The child is killed on timeout and can be killed externally
50
+ * via `killByCallId` (used by the tool executor timeout).
51
+ */
52
+ export function runCommand(command, options = {}) {
53
+ const { cwd, callId, timeoutMs = 120_000, maxBuffer = 10 * 1024 * 1024, } = options;
54
+ return new Promise((resolve) => {
55
+ let timedOut = false;
56
+ let stdout = "";
57
+ let stderr = "";
58
+ const child = spawn(command, {
59
+ cwd,
60
+ shell: true,
61
+ windowsHide: true,
62
+ detached: platform() !== "win32",
63
+ stdio: ["ignore", "pipe", "pipe"],
64
+ });
65
+ const stdoutRef = { value: stdout };
66
+ const stderrRef = { value: stderr };
67
+ // On Windows, child processes spawned via cmd.exe output in the OEM code
68
+ // page (typically CP866 for Russian), NOT UTF-8 — even when the parent
69
+ // console reports code page 65001 via `chcp`. Always decode with ibm866
70
+ // so that Cyrillic and other non-ASCII text is not garbled.
71
+ const decoder = platform() === "win32" ? new TextDecoder("ibm866") : null;
72
+ const decodeChunk = (chunk) => decoder ? decoder.decode(chunk, { stream: true }) : chunk.toString("utf-8");
73
+ child.stdout?.on("data", (chunk) => {
74
+ const text = decodeChunk(chunk);
75
+ const next = stdoutRef.value.length + text.length;
76
+ if (next > maxBuffer) {
77
+ stdoutRef.value =
78
+ stdoutRef.value.slice(stdoutRef.value.length + text.length - maxBuffer) + text;
79
+ }
80
+ else {
81
+ stdoutRef.value = stdoutRef.value + text;
82
+ }
83
+ });
84
+ child.stderr?.on("data", (chunk) => {
85
+ const text = decodeChunk(chunk);
86
+ const next = stderrRef.value.length + text.length;
87
+ if (next > maxBuffer) {
88
+ stderrRef.value =
89
+ stderrRef.value.slice(stderrRef.value.length + text.length - maxBuffer) + text;
90
+ }
91
+ else {
92
+ stderrRef.value = stderrRef.value + text;
93
+ }
94
+ });
95
+ const entry = {
96
+ kill: () => killTree(child),
97
+ };
98
+ if (callId) {
99
+ activeChildren.set(callId, entry);
100
+ }
101
+ const timer = setTimeout(() => {
102
+ timedOut = true;
103
+ killTree(child);
104
+ }, timeoutMs);
105
+ child.on("error", () => {
106
+ clearTimeout(timer);
107
+ if (callId)
108
+ activeChildren.delete(callId);
109
+ resolve({
110
+ stdout: stdoutRef.value,
111
+ stderr: stderrRef.value,
112
+ code: null,
113
+ signal: null,
114
+ timedOut,
115
+ });
116
+ });
117
+ child.on("close", (code, signal) => {
118
+ clearTimeout(timer);
119
+ if (callId)
120
+ activeChildren.delete(callId);
121
+ resolve({ stdout: stdoutRef.value, stderr: stderrRef.value, code, signal, timedOut });
122
+ });
123
+ });
124
+ }
@@ -0,0 +1,45 @@
1
+ export class ModuleRegistry {
2
+ modules = new Map();
3
+ register(mod) {
4
+ this.modules.set(mod.name, mod);
5
+ }
6
+ get(name) {
7
+ return this.modules.get(name);
8
+ }
9
+ listModules() {
10
+ return Array.from(this.modules.keys()).sort();
11
+ }
12
+ collectPromptBlocks() {
13
+ const blocks = [];
14
+ for (const mod of this.modules.values()) {
15
+ if (mod.getSystemPromptBlock) {
16
+ const block = mod.getSystemPromptBlock();
17
+ if (block)
18
+ blocks.push(block);
19
+ }
20
+ }
21
+ return blocks;
22
+ }
23
+ collectToolDefinitions() {
24
+ const tools = [];
25
+ for (const mod of this.modules.values()) {
26
+ if (mod.getToolDefinitions) {
27
+ const defs = mod.getToolDefinitions();
28
+ if (defs)
29
+ tools.push(...defs);
30
+ }
31
+ }
32
+ return tools;
33
+ }
34
+ collectPlugins() {
35
+ const plugins = [];
36
+ for (const mod of this.modules.values()) {
37
+ if (mod.getPlugin) {
38
+ const plugin = mod.getPlugin();
39
+ if (plugin)
40
+ plugins.push(plugin);
41
+ }
42
+ }
43
+ return plugins;
44
+ }
45
+ }
@@ -0,0 +1,116 @@
1
+ import { existsSync, mkdirSync, appendFileSync } from "fs";
2
+ import { resolve } from "path";
3
+ import { homedir } from "os";
4
+ import { globalAuditNotifier } from "./audit-notifier";
5
+ /**
6
+ * Directory and file path for audit logs
7
+ */
8
+ const AUDIT_LOG_DIR = resolve(homedir(), ".mma", "logs");
9
+ const AUDIT_LOG_PATH = resolve(AUDIT_LOG_DIR, "audit.jsonl");
10
+ /**
11
+ * Ensure audit log directory exists
12
+ */
13
+ function ensureAuditLogDir() {
14
+ if (!existsSync(AUDIT_LOG_DIR)) {
15
+ mkdirSync(AUDIT_LOG_DIR, { recursive: true, mode: 0o700 });
16
+ }
17
+ }
18
+ /**
19
+ * Write an audit log entry
20
+ */
21
+ export function logAudit(entry) {
22
+ ensureAuditLogDir();
23
+ try {
24
+ const logEntry = JSON.stringify(entry);
25
+ appendFileSync(AUDIT_LOG_PATH, logEntry + "\n", "utf8");
26
+ }
27
+ catch {
28
+ // Silently fail if we can't write to audit log
29
+ // This shouldn't break the agent
30
+ }
31
+ // Notify audit notifier if enabled
32
+ try {
33
+ globalAuditNotifier.notify(entry);
34
+ }
35
+ catch {
36
+ // Silently fail if notification fails
37
+ }
38
+ }
39
+ /**
40
+ * Log a tool call
41
+ */
42
+ export function logToolCall(sessionId, tool, success, details) {
43
+ logAudit({
44
+ timestamp: new Date().toISOString(),
45
+ sessionId,
46
+ action: "tool_call",
47
+ tool,
48
+ success,
49
+ details,
50
+ });
51
+ }
52
+ /**
53
+ * Log a file write operation
54
+ */
55
+ export function logFileWrite(sessionId, path, success, details) {
56
+ logAudit({
57
+ timestamp: new Date().toISOString(),
58
+ sessionId,
59
+ action: "file_write",
60
+ path,
61
+ success,
62
+ details,
63
+ });
64
+ }
65
+ /**
66
+ * Log a file delete operation
67
+ */
68
+ export function logFileDelete(sessionId, path, success, details) {
69
+ logAudit({
70
+ timestamp: new Date().toISOString(),
71
+ sessionId,
72
+ action: "file_delete",
73
+ path,
74
+ success,
75
+ details,
76
+ });
77
+ }
78
+ /**
79
+ * Log a bash command execution
80
+ */
81
+ export function logBashCommand(sessionId, command, success, details) {
82
+ logAudit({
83
+ timestamp: new Date().toISOString(),
84
+ sessionId,
85
+ action: "bash_command",
86
+ command,
87
+ success,
88
+ details,
89
+ });
90
+ }
91
+ /**
92
+ * Log a network request
93
+ */
94
+ export function logNetworkRequest(sessionId, url, success, details) {
95
+ logAudit({
96
+ timestamp: new Date().toISOString(),
97
+ sessionId,
98
+ action: "network_request",
99
+ url,
100
+ success,
101
+ details,
102
+ });
103
+ }
104
+ /**
105
+ * Log a security block event
106
+ */
107
+ export function logSecurityBlock(sessionId, action, reason, details) {
108
+ logAudit({
109
+ timestamp: new Date().toISOString(),
110
+ sessionId,
111
+ action: "security_block",
112
+ success: false,
113
+ reason,
114
+ details,
115
+ });
116
+ }