micro-models-agent 0.52.0 → 0.52.1

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 (231) hide show
  1. package/README.md +358 -358
  2. package/dist/main.js +244 -122
  3. package/dist/modules/browser/bridge-server.mjs +202 -202
  4. package/dist/skills/builtin/git.md +36 -36
  5. package/dist/skills/builtin/typescript.md +35 -35
  6. package/package.json +1 -1
  7. package/dist/cli/commands.js +0 -447
  8. package/dist/cli/completer.js +0 -167
  9. package/dist/cli/index.js +0 -2
  10. package/dist/cli/main.js +0 -153
  11. package/dist/cli/plugin-commands.js +0 -36
  12. package/dist/cli/repl-commands.js +0 -761
  13. package/dist/cli/repl.js +0 -702
  14. package/dist/cli/run-result.js +0 -33
  15. package/dist/cli/security-commands.js +0 -164
  16. package/dist/cli/setup.js +0 -237
  17. package/dist/config/config.js +0 -276
  18. package/dist/config/defaults.js +0 -141
  19. package/dist/config/domains.js +0 -179
  20. package/dist/config/experts.js +0 -15
  21. package/dist/config/index.js +0 -4
  22. package/dist/config/security.js +0 -213
  23. package/dist/config/types.js +0 -1
  24. package/dist/core/agent-moe.js +0 -102
  25. package/dist/core/agent.js +0 -1018
  26. package/dist/core/bootstrap.js +0 -481
  27. package/dist/core/crash-handler.js +0 -51
  28. package/dist/core/environment.js +0 -199
  29. package/dist/core/index.js +0 -2
  30. package/dist/core/prompt-builder.js +0 -76
  31. package/dist/core/session-logger.js +0 -251
  32. package/dist/core/types.js +0 -1
  33. package/dist/core/version.js +0 -26
  34. package/dist/core/workspace.js +0 -76
  35. package/dist/i18n/en.json +0 -679
  36. package/dist/i18n/index.js +0 -46
  37. package/dist/i18n/ru.json +0 -679
  38. package/dist/index.js +0 -22
  39. package/dist/llm/image-utils.js +0 -143
  40. package/dist/llm/index.js +0 -4
  41. package/dist/llm/model-loader.js +0 -78
  42. package/dist/llm/openai-compat.js +0 -497
  43. package/dist/llm/orchestrator.js +0 -200
  44. package/dist/llm/provider.js +0 -10
  45. package/dist/llm/response.js +0 -39
  46. package/dist/llm/token-counter.js +0 -39
  47. package/dist/llm/types.js +0 -1
  48. package/dist/logger/app-logger.js +0 -189
  49. package/dist/logger/file-log.js +0 -151
  50. package/dist/logger/index.js +0 -1
  51. package/dist/migration/backup.js +0 -45
  52. package/dist/migration/detect.js +0 -50
  53. package/dist/migration/index.js +0 -2
  54. package/dist/modules/artifacts/store.js +0 -61
  55. package/dist/modules/browser/actions.js +0 -76
  56. package/dist/modules/browser/bridge-client.js +0 -199
  57. package/dist/modules/browser/bridge-path.js +0 -10
  58. package/dist/modules/browser/cookie-store.js +0 -24
  59. package/dist/modules/browser/driver.js +0 -136
  60. package/dist/modules/browser/index.js +0 -7
  61. package/dist/modules/browser/module.js +0 -29
  62. package/dist/modules/browser/session.js +0 -342
  63. package/dist/modules/browser/snapshot.js +0 -148
  64. package/dist/modules/browser/types.js +0 -12
  65. package/dist/modules/certification/cli.js +0 -213
  66. package/dist/modules/certification/fact-checker.js +0 -82
  67. package/dist/modules/certification/loader.js +0 -106
  68. package/dist/modules/certification/manifest.js +0 -58
  69. package/dist/modules/certification/runner.js +0 -245
  70. package/dist/modules/certification/scenarios.js +0 -407
  71. package/dist/modules/certification/types.js +0 -1
  72. package/dist/modules/context/chunk-query.js +0 -100
  73. package/dist/modules/context/fact-extractor.js +0 -168
  74. package/dist/modules/context/history.js +0 -15
  75. package/dist/modules/context/index.js +0 -1
  76. package/dist/modules/context/manager.js +0 -440
  77. package/dist/modules/execution/audit-runners.js +0 -206
  78. package/dist/modules/execution/auditor.js +0 -218
  79. package/dist/modules/execution/execution-plugin.js +0 -431
  80. package/dist/modules/execution/index.js +0 -8
  81. package/dist/modules/execution/module.js +0 -625
  82. package/dist/modules/execution/moe-executor.js +0 -304
  83. package/dist/modules/execution/plan-coverage.js +0 -68
  84. package/dist/modules/execution/plan-persister.js +0 -46
  85. package/dist/modules/execution/plan-store.js +0 -196
  86. package/dist/modules/execution/plan-tool.js +0 -677
  87. package/dist/modules/execution/plan-validator.js +0 -153
  88. package/dist/modules/execution/planner.js +0 -94
  89. package/dist/modules/execution/stuck-detector.js +0 -746
  90. package/dist/modules/execution/tracker.js +0 -69
  91. package/dist/modules/execution/types.js +0 -1
  92. package/dist/modules/execution/verifier.js +0 -235
  93. package/dist/modules/execution/windows-commands.js +0 -41
  94. package/dist/modules/hallucination/confidence.js +0 -66
  95. package/dist/modules/hallucination/consistency.js +0 -26
  96. package/dist/modules/hallucination/detector.js +0 -47
  97. package/dist/modules/hallucination/factual.js +0 -169
  98. package/dist/modules/hallucination/index.js +0 -5
  99. package/dist/modules/hallucination/js-identifiers.js +0 -262
  100. package/dist/modules/hallucination/llm-judge.js +0 -101
  101. package/dist/modules/index.js +0 -5
  102. package/dist/modules/indexer/cache.js +0 -40
  103. package/dist/modules/indexer/index.js +0 -3
  104. package/dist/modules/indexer/module.js +0 -246
  105. package/dist/modules/indexer/project-profile.js +0 -183
  106. package/dist/modules/indexer/walker.js +0 -101
  107. package/dist/modules/lsp/check-tool.js +0 -58
  108. package/dist/modules/lsp/client.js +0 -389
  109. package/dist/modules/lsp/command.js +0 -60
  110. package/dist/modules/lsp/config.js +0 -135
  111. package/dist/modules/lsp/index.js +0 -3
  112. package/dist/modules/lsp/module.js +0 -260
  113. package/dist/modules/lsp/probe.js +0 -86
  114. package/dist/modules/lsp/project-root.js +0 -32
  115. package/dist/modules/lsp/startup-check.js +0 -144
  116. package/dist/modules/lsp/types.js +0 -1
  117. package/dist/modules/mcp/client.js +0 -399
  118. package/dist/modules/mcp/index.js +0 -3
  119. package/dist/modules/mcp/module.js +0 -142
  120. package/dist/modules/mcp/registry.js +0 -15
  121. package/dist/modules/memory/index.js +0 -1
  122. package/dist/modules/memory/module.js +0 -96
  123. package/dist/modules/memory/search.js +0 -42
  124. package/dist/modules/memory/store.js +0 -69
  125. package/dist/modules/pipelines/engine.js +0 -60
  126. package/dist/modules/pipelines/index.js +0 -3
  127. package/dist/modules/pipelines/parser.js +0 -56
  128. package/dist/modules/pipelines/template.js +0 -14
  129. package/dist/modules/plugins/builtin/lint-on-write.js +0 -334
  130. package/dist/modules/plugins/builtin/notify.js +0 -9
  131. package/dist/modules/plugins/index.js +0 -1
  132. package/dist/modules/plugins/loader.js +0 -70
  133. package/dist/modules/plugins/manager.js +0 -261
  134. package/dist/modules/plugins/types.js +0 -1
  135. package/dist/modules/pricing/index.js +0 -61
  136. package/dist/modules/pricing/prices.js +0 -129
  137. package/dist/modules/processes/detect.js +0 -34
  138. package/dist/modules/processes/index.js +0 -2
  139. package/dist/modules/processes/registry.js +0 -327
  140. package/dist/modules/processes/runner.js +0 -23
  141. package/dist/modules/providers/create.js +0 -22
  142. package/dist/modules/providers/fallback.js +0 -79
  143. package/dist/modules/providers/health.js +0 -46
  144. package/dist/modules/providers/index.js +0 -5
  145. package/dist/modules/providers/manager.js +0 -161
  146. package/dist/modules/providers/presets.js +0 -128
  147. package/dist/modules/providers/registry.js +0 -22
  148. package/dist/modules/providers/types.js +0 -1
  149. package/dist/modules/registry.js +0 -48
  150. package/dist/modules/security/audit-log.js +0 -136
  151. package/dist/modules/security/audit-notifier.js +0 -292
  152. package/dist/modules/security/command-validator.js +0 -219
  153. package/dist/modules/security/content-scanner.js +0 -53
  154. package/dist/modules/security/data-sanitizer.js +0 -89
  155. package/dist/modules/security/encryption.js +0 -242
  156. package/dist/modules/security/index.js +0 -14
  157. package/dist/modules/security/network-validator.js +0 -88
  158. package/dist/modules/security/path-validator.js +0 -203
  159. package/dist/modules/security/rate-limiter.js +0 -119
  160. package/dist/modules/security/security-policies.js +0 -531
  161. package/dist/modules/security/session-encryption.js +0 -210
  162. package/dist/modules/security/session-isolation.js +0 -95
  163. package/dist/modules/session/index.js +0 -3
  164. package/dist/modules/session/manager.js +0 -172
  165. package/dist/modules/session/module.js +0 -24
  166. package/dist/modules/session/store.js +0 -222
  167. package/dist/modules/session/types.js +0 -1
  168. package/dist/modules/skills/index.js +0 -2
  169. package/dist/modules/skills/loader.js +0 -72
  170. package/dist/modules/skills/matcher.js +0 -27
  171. package/dist/modules/skills/module.js +0 -129
  172. package/dist/modules/types.js +0 -1
  173. package/dist/modules/updater/checker.js +0 -96
  174. package/dist/modules/updater/index.js +0 -2
  175. package/dist/modules/updater/module.js +0 -116
  176. package/dist/modules/user-profile/compressor.js +0 -16
  177. package/dist/modules/user-profile/index.js +0 -1
  178. package/dist/modules/user-profile/profile.js +0 -68
  179. package/dist/tools/approve.js +0 -33
  180. package/dist/tools/attach-image.js +0 -101
  181. package/dist/tools/bash.js +0 -519
  182. package/dist/tools/browser.js +0 -115
  183. package/dist/tools/chunk-query.js +0 -100
  184. package/dist/tools/create-dir.js +0 -56
  185. package/dist/tools/delete-file.js +0 -63
  186. package/dist/tools/download-file.js +0 -117
  187. package/dist/tools/edit-file.js +0 -80
  188. package/dist/tools/enable-tools.js +0 -59
  189. package/dist/tools/executor.js +0 -154
  190. package/dist/tools/file-info.js +0 -47
  191. package/dist/tools/filter-tools.js +0 -17
  192. package/dist/tools/glob-tool.js +0 -27
  193. package/dist/tools/grep-tool.js +0 -125
  194. package/dist/tools/hidden-tools-block.js +0 -37
  195. package/dist/tools/index.js +0 -78
  196. package/dist/tools/list-dir.js +0 -49
  197. package/dist/tools/load-skill.js +0 -43
  198. package/dist/tools/mcp-call.js +0 -69
  199. package/dist/tools/move-file.js +0 -86
  200. package/dist/tools/path-utils.js +0 -101
  201. package/dist/tools/pipeline-run.js +0 -145
  202. package/dist/tools/preview.js +0 -2
  203. package/dist/tools/process-kill.js +0 -40
  204. package/dist/tools/process-list.js +0 -37
  205. package/dist/tools/process-log.js +0 -54
  206. package/dist/tools/question.js +0 -141
  207. package/dist/tools/read-file.js +0 -179
  208. package/dist/tools/recall.js +0 -118
  209. package/dist/tools/registry.js +0 -47
  210. package/dist/tools/remember.js +0 -68
  211. package/dist/tools/scope-check.js +0 -32
  212. package/dist/tools/search-history.js +0 -85
  213. package/dist/tools/subagent.js +0 -196
  214. package/dist/tools/types.js +0 -1
  215. package/dist/tools/user-input.js +0 -123
  216. package/dist/tools/web-browse.js +0 -87
  217. package/dist/tools/web-fetch.js +0 -119
  218. package/dist/tools/web-search.js +0 -105
  219. package/dist/tools/write-file.js +0 -82
  220. package/dist/ui/box.js +0 -77
  221. package/dist/ui/colors.js +0 -4
  222. package/dist/ui/diff.js +0 -178
  223. package/dist/ui/index.js +0 -6
  224. package/dist/ui/line-editor.js +0 -822
  225. package/dist/ui/line-math.js +0 -73
  226. package/dist/ui/md-formatter.js +0 -212
  227. package/dist/ui/output.js +0 -13
  228. package/dist/ui/plan-view.js +0 -103
  229. package/dist/ui/renderer.js +0 -259
  230. package/dist/ui/spinner.js +0 -70
  231. package/dist/ui/table.js +0 -144
@@ -1,179 +0,0 @@
1
- import { readFileSync, existsSync, statSync, openSync, readSync, closeSync } from "fs";
2
- import { extname } from "path";
3
- import { t } from "../i18n/index";
4
- import { isPathInScope } from "../modules/security/path-validator";
5
- import { DEFAULT_SECURITY_CONFIG } from "../config/security";
6
- import { logSecurityBlock } from "../modules/security/audit-log";
7
- import { safeResolvePath } from "./path-utils";
8
- /**
9
- * Default lines per read_file call. Deliberately much larger than
10
- * MAX_PREVIEW_LINES (15): a 15-line default forces the model to issue 10-17
11
- * calls per file, which floods the context and drives compaction every 15
12
- * iterations (observed: 96 read_file calls, App.tsx read 23x, then 11
13
- * compactions in ~20 min that deleted the user's task mid-turn). 300 lines
14
- * covers typical component files in one call while still capping output.
15
- */
16
- const DEFAULT_LIMIT = 300;
17
- /**
18
- * Files larger than this are read through a bounded streaming path instead of
19
- * readFileSync (which loads the whole file into memory just to slice a few
20
- * hundred lines).
21
- */
22
- const LARGE_FILE_BYTES = 1024 * 1024;
23
- const STREAM_CHUNK = 256 * 1024;
24
- /**
25
- * Bounded line-slice read for large files: streams the file in chunks,
26
- * collects only the requested [offset, offset+limit) window, then counts the
27
- * remaining newlines raw (no decode) to produce the total. Memory stays
28
- * bounded regardless of file size.
29
- */
30
- function readLineSlice(path, offset, limit) {
31
- const fd = openSync(path, "r");
32
- try {
33
- const buf = Buffer.alloc(STREAM_CHUNK);
34
- let pos = 0;
35
- let carry = "";
36
- let lineNumber = 1;
37
- let total = 0;
38
- const collected = [];
39
- const endLine = offset + limit; // exclusive
40
- while (true) {
41
- const bytes = readSync(fd, buf, 0, STREAM_CHUNK, pos);
42
- if (bytes === 0)
43
- break;
44
- pos += bytes;
45
- const chunk = carry + buf.toString("utf-8", 0, bytes);
46
- const parts = chunk.split("\n");
47
- carry = parts.pop() ?? "";
48
- for (const line of parts) {
49
- total++;
50
- if (lineNumber >= offset && lineNumber < endLine)
51
- collected.push(line);
52
- lineNumber++;
53
- }
54
- if (lineNumber >= endLine && collected.length >= limit) {
55
- // Window complete — count remaining newlines without decoding.
56
- const rest = [];
57
- while (true) {
58
- const n = readSync(fd, buf, 0, STREAM_CHUNK, pos);
59
- if (n === 0)
60
- break;
61
- pos += n;
62
- rest.push(Buffer.from(buf.subarray(0, n)));
63
- }
64
- const tail = Buffer.concat([...rest, Buffer.from(carry, "utf-8")]);
65
- for (let i = 0; i < tail.length; i++)
66
- if (tail[i] === 0x0a)
67
- total++;
68
- total++; // last line has no trailing newline
69
- return { selected: collected.join("\n"), total };
70
- }
71
- }
72
- if (carry.length > 0 || total === 0) {
73
- total++;
74
- if (lineNumber >= offset && lineNumber < endLine)
75
- collected.push(carry);
76
- }
77
- return { selected: collected.join("\n"), total };
78
- }
79
- finally {
80
- closeSync(fd);
81
- }
82
- }
83
- export const readFileTool = {
84
- name: "read_file",
85
- icon: "👀",
86
- description: `Read a file from the filesystem. Reads up to ${DEFAULT_LIMIT} lines at a time by default; use offset to page through large files.`,
87
- tags: ["file", "code"],
88
- boundedOutput: true,
89
- parameters: {
90
- type: "object",
91
- properties: {
92
- path: { type: "string", description: "File path to read" },
93
- offset: {
94
- type: "number",
95
- description: `Starting line (1-indexed). Use offset=<next> from a truncated result to continue reading.`,
96
- },
97
- limit: {
98
- type: "number",
99
- description: `Number of lines to read (default ${DEFAULT_LIMIT})`,
100
- },
101
- },
102
- required: ["path"],
103
- },
104
- handler: async (ctx, args) => {
105
- const path = String(args.path);
106
- const resolved = safeResolvePath(ctx.baseDir, path);
107
- const securityPaths = ctx.config?.security?.paths || DEFAULT_SECURITY_CONFIG.paths;
108
- const scopeCheck = isPathInScope(ctx.baseDir, resolved, ctx.scope, securityPaths);
109
- if (!scopeCheck.allowed) {
110
- const pathStr = path;
111
- logSecurityBlock(ctx.sessionId || undefined, "file_read", scopeCheck.reason || "Path not allowed", pathStr);
112
- return {
113
- success: false,
114
- output: t("file.path_not_allowed", {
115
- path: `${path} — ${scopeCheck.reason}`,
116
- }),
117
- };
118
- }
119
- if (!existsSync(resolved)) {
120
- // For relative paths, tell the model WHERE the path resolved — the
121
- // "not found" alone is confusing (it cannot tell baseDir from cwd).
122
- const output = resolved !== path
123
- ? t("file.notfound_resolved", {
124
- path,
125
- resolved,
126
- })
127
- : t("file.notfound", { path });
128
- return { success: false, output };
129
- }
130
- const offset = args.offset || 1;
131
- const limit = args.limit || DEFAULT_LIMIT;
132
- let lines;
133
- let total;
134
- let selected;
135
- if (statSync(resolved).size > LARGE_FILE_BYTES) {
136
- const slice = readLineSlice(resolved, offset, limit);
137
- total = slice.total;
138
- selected = slice.selected;
139
- }
140
- else {
141
- const content = readFileSync(resolved, "utf-8");
142
- lines = content.split("\n");
143
- total = lines.length;
144
- const end = Math.min(offset - 1 + limit, total);
145
- selected = lines.slice(offset - 1, end).join("\n");
146
- }
147
- const end = Math.min(offset - 1 + limit, total);
148
- const ext = extname(resolved) || "(no extension)";
149
- const header = t("file.read_header", {
150
- path,
151
- ext,
152
- total: String(total),
153
- from: String(offset),
154
- to: String(end),
155
- });
156
- const truncated = end < total
157
- ? t("file.read_truncated", {
158
- remaining: String(total - end),
159
- next: String(end + 1),
160
- })
161
- : "";
162
- // Compact user-facing display: the tool header already shows the path,
163
- // so only a partial read needs extra info (the range within the file).
164
- // A full read adds nothing — keeps the card to two lines.
165
- const fullRead = offset === 1 && end >= total;
166
- const display = fullRead
167
- ? ""
168
- : t("file.read_range", {
169
- from: String(offset),
170
- to: String(end),
171
- total: String(total),
172
- });
173
- return {
174
- success: true,
175
- output: `${header}\n${selected}${truncated}`,
176
- display,
177
- };
178
- },
179
- };
@@ -1,118 +0,0 @@
1
- import { homedir } from "os";
2
- import { join } from "path";
3
- import { t } from "../i18n/index";
4
- import { MemoryStore } from "../modules/memory/store";
5
- const CATEGORIES = ["preferences", "conventions", "decisions", "errors", "facts"];
6
- export const recallTool = {
7
- name: "recall",
8
- icon: "💾",
9
- description: "Recall stored information from memory. Search across preferences, facts, conventions, decisions, errors.",
10
- tags: ["memory"],
11
- parameters: {
12
- type: "object",
13
- properties: {
14
- query: {
15
- type: "string",
16
- description: "Search query (full-text search across all memory)",
17
- },
18
- category: {
19
- type: "string",
20
- description: "Limit to specific category: preferences, conventions, decisions, errors, facts",
21
- enum: CATEGORIES,
22
- },
23
- },
24
- },
25
- handler: async (_ctx, args) => {
26
- const query = args.query ? String(args.query) : "";
27
- const category = args.category ? String(args.category) : "";
28
- const memoryDir = join(homedir(), ".mma", "memory");
29
- const store = new MemoryStore(memoryDir);
30
- try {
31
- // No query, no category → show everything
32
- if (!query && !category) {
33
- return { success: true, output: formatAll(store) };
34
- }
35
- // Category only → show that category
36
- if (!query && category) {
37
- return { success: true, output: formatCategory(store, category) };
38
- }
39
- // Query with optional category → search
40
- if (category) {
41
- const results = searchCategory(store, category, query);
42
- if (results.length === 0) {
43
- return { success: true, output: t("tool.recall.empty", { query }) };
44
- }
45
- return {
46
- success: true,
47
- output: t("tool.recall.search_results", { category, results: results.join("\n") }),
48
- };
49
- }
50
- // Query across all
51
- const results = store.search(query);
52
- if (results.length === 0) {
53
- return { success: true, output: t("tool.recall.empty", { query }) };
54
- }
55
- const formatted = results.map((r) => `[${r.file}] ${r.match}`).join("\n");
56
- return {
57
- success: true,
58
- output: t("tool.recall.search_results", { category: "all", results: formatted }),
59
- };
60
- }
61
- catch (err) {
62
- return { success: true, output: t("tool.memory_error", { error: String(err) }) };
63
- }
64
- },
65
- };
66
- function formatAll(store) {
67
- const parts = [];
68
- const prefs = store.getPreferences();
69
- if (Object.keys(prefs).length > 0) {
70
- parts.push("Preferences:");
71
- for (const [k, v] of Object.entries(prefs)) {
72
- parts.push(` ${k} = ${v}`);
73
- }
74
- }
75
- for (const cat of ["facts", "conventions", "decisions", "errors"]) {
76
- const content = store.read(cat);
77
- const entries = content.split("\n").filter((l) => l.startsWith("- "));
78
- if (entries.length > 0) {
79
- parts.push(`\n${cat.charAt(0).toUpperCase() + cat.slice(1)} (last 5):`);
80
- for (const e of entries.slice(-5)) {
81
- parts.push(` ${e}`);
82
- }
83
- }
84
- }
85
- return parts.length > 0 ? parts.join("\n") : t("tool.recall.no_memory");
86
- }
87
- function formatCategory(store, category) {
88
- if (category === "preferences") {
89
- const prefs = store.getPreferences();
90
- if (Object.keys(prefs).length === 0)
91
- return t("tool.recall.no_memory");
92
- const lines = ["Preferences:"];
93
- for (const [k, v] of Object.entries(prefs)) {
94
- lines.push(` ${k} = ${v}`);
95
- }
96
- return lines.join("\n");
97
- }
98
- const content = store.read(category);
99
- const entries = content.split("\n").filter((l) => l.startsWith("- "));
100
- if (entries.length === 0)
101
- return t("tool.recall.no_memory");
102
- return `${category.charAt(0).toUpperCase() + category.slice(1)} (${entries.length} entries):\n${entries.join("\n")}`;
103
- }
104
- function searchCategory(store, category, query) {
105
- if (category === "preferences") {
106
- const prefs = store.getPreferences();
107
- const lower = query.toLowerCase();
108
- return Object.entries(prefs)
109
- .filter(([k, v]) => k.toLowerCase().includes(lower) || v.toLowerCase().includes(lower))
110
- .map(([k, v]) => ` ${k} = ${v}`);
111
- }
112
- const content = store.read(category);
113
- const lower = query.toLowerCase();
114
- return content
115
- .split("\n")
116
- .filter((l) => l.startsWith("- ") && l.toLowerCase().includes(lower))
117
- .map((l) => ` ${l}`);
118
- }
@@ -1,47 +0,0 @@
1
- export class ToolRegistry {
2
- tools = new Map();
3
- register(tool) {
4
- if (this.tools.has(tool.name)) {
5
- throw new Error(`Tool already registered: ${tool.name}`);
6
- }
7
- this.tools.set(tool.name, tool);
8
- }
9
- get(name) {
10
- return this.tools.get(name);
11
- }
12
- has(name) {
13
- return this.tools.has(name);
14
- }
15
- list() {
16
- return Array.from(this.tools.keys()).sort();
17
- }
18
- getAll() {
19
- return Array.from(this.tools.values());
20
- }
21
- getByTags(tags) {
22
- return this.getAll().filter((t) => {
23
- if (!t.tags || t.tags.length === 0)
24
- return false;
25
- return tags.some((tag) => t.tags.includes(tag));
26
- });
27
- }
28
- /**
29
- * Tools to expose to the LLM. Always includes `alwaysOn` tools, then tools
30
- * matching the given tags. When no tags are given, returns every registered
31
- * tool (backward-compatible). Duplicates are impossible because the
32
- * alwaysOn + tagged sets are unioned by name.
33
- */
34
- getAllForLLM(tags) {
35
- const all = this.getAll();
36
- const tagSet = new Set(tags ?? []);
37
- const selected = tagSet.size === 0
38
- ? all
39
- : all.filter((t) => t.alwaysOn || (t.tags && t.tags.some((tag) => tagSet.has(tag))));
40
- return selected.map((t) => ({
41
- name: t.name,
42
- description: t.description,
43
- parameters: t.parameters,
44
- boundedOutput: t.boundedOutput,
45
- }));
46
- }
47
- }
@@ -1,68 +0,0 @@
1
- import { homedir } from "os";
2
- import { join } from "path";
3
- import { t } from "../i18n/index";
4
- import { MemoryStore } from "../modules/memory/store";
5
- const CATEGORIES = ["preferences", "conventions", "decisions", "errors", "facts"];
6
- export const rememberTool = {
7
- name: "remember",
8
- icon: "🧠",
9
- description: "Remember information across sessions. Use for user preferences, facts, conventions, decisions, or errors.",
10
- tags: ["memory"],
11
- parameters: {
12
- type: "object",
13
- properties: {
14
- category: {
15
- type: "string",
16
- description: "Memory category: preferences, conventions, decisions, errors, facts",
17
- enum: CATEGORIES,
18
- },
19
- key: {
20
- type: "string",
21
- description: 'Key name (required for preferences, e.g. "color", "language")',
22
- },
23
- value: {
24
- type: "string",
25
- description: "Value to store (required for preferences)",
26
- },
27
- entry: {
28
- type: "string",
29
- description: "Text entry to append (for conventions, decisions, errors, facts)",
30
- },
31
- },
32
- required: ["category"],
33
- },
34
- handler: async (_ctx, args) => {
35
- const category = String(args.category || "");
36
- if (!CATEGORIES.includes(category)) {
37
- return { success: false, output: t("tool.invalid_params") };
38
- }
39
- const memoryDir = join(homedir(), ".mma", "memory");
40
- const store = new MemoryStore(memoryDir);
41
- try {
42
- if (category === "preferences") {
43
- const key = String(args.key || "");
44
- const value = String(args.value || "");
45
- if (!key) {
46
- return { success: false, output: t("tool.remember.key_required") };
47
- }
48
- store.setPreference(key, value);
49
- return {
50
- success: true,
51
- output: t("tool.remember.preference", { key, value }),
52
- };
53
- }
54
- const entry = String(args.entry || "");
55
- if (!entry) {
56
- return { success: false, output: t("tool.remember.entry_required") };
57
- }
58
- store.append(category, entry);
59
- return {
60
- success: true,
61
- output: t("tool.remember.entry", { category, entry }),
62
- };
63
- }
64
- catch (err) {
65
- return { success: true, output: t("tool.memory_error", { error: String(err) }) };
66
- }
67
- },
68
- };
@@ -1,32 +0,0 @@
1
- import { resolve, normalize } from "path";
2
- import { isInsideDir, matchesScopeEntry } from "./path-utils";
3
- export function isPathInScope(baseDir, targetPath, scope) {
4
- const baseResolved = resolve(baseDir);
5
- const targetResolved = resolve(baseDir, normalize(targetPath));
6
- // Separator- and case-safe containment (sibling prefixes cannot pass).
7
- if (!isInsideDir(targetResolved, baseResolved)) {
8
- return { allowed: false, reason: "Path is outside the base working directory" };
9
- }
10
- if (!scope)
11
- return { allowed: true };
12
- const isRead = scope.read_only_files.some((f) => matchesScopeEntry(targetResolved, resolve(baseDir, f)));
13
- const isWrite = scope.allowed_files.some((f) => matchesScopeEntry(targetResolved, resolve(baseDir, f)));
14
- if (isWrite)
15
- return { allowed: true };
16
- if (isRead)
17
- return { allowed: true, reason: "Read-only file" };
18
- return { allowed: false, reason: "Path is not within the allowed scope for this sub-agent" };
19
- }
20
- export function isPathWritable(baseDir, targetPath, scope) {
21
- const baseResolved = resolve(baseDir);
22
- const targetResolved = resolve(baseDir, normalize(targetPath));
23
- if (!isInsideDir(targetResolved, baseResolved)) {
24
- return { allowed: false, reason: "Path is outside the base working directory" };
25
- }
26
- if (!scope)
27
- return { allowed: true };
28
- const isWrite = scope.allowed_files.some((f) => matchesScopeEntry(targetResolved, resolve(baseDir, f)));
29
- if (isWrite)
30
- return { allowed: true };
31
- return { allowed: false, reason: "Path is not within the writable scope for this sub-agent" };
32
- }
@@ -1,85 +0,0 @@
1
- import * as fs from "fs";
2
- import { join } from "path";
3
- import { homedir } from "os";
4
- import { t } from "../i18n/index";
5
- function searchFile(filePath, query, maxResults, results) {
6
- if (!fs.existsSync(filePath))
7
- return;
8
- const content = fs.readFileSync(filePath, "utf-8");
9
- for (const line of content.split("\n").filter(Boolean)) {
10
- if (line.toLowerCase().includes(query)) {
11
- try {
12
- const entry = JSON.parse(line);
13
- results.push(`[${filePath}] ${JSON.stringify(entry).slice(0, 200)}`);
14
- }
15
- catch {
16
- /* skip */
17
- }
18
- }
19
- if (results.length >= maxResults)
20
- break;
21
- }
22
- }
23
- export const searchHistoryTool = {
24
- name: "search_history",
25
- icon: "🕵️",
26
- description: "Search through session history for past interactions across all sessions.",
27
- tags: ["research"],
28
- parameters: {
29
- type: "object",
30
- properties: {
31
- query: { type: "string", description: "Search query" },
32
- maxResults: {
33
- type: "number",
34
- description: "Maximum results (default 5)",
35
- },
36
- sessionId: {
37
- type: "string",
38
- description: "Optional: limit search to a specific session",
39
- },
40
- },
41
- required: ["query"],
42
- },
43
- handler: async (_ctx, args) => {
44
- const query = String(args.query || "").toLowerCase();
45
- const maxResults = Number(args.maxResults) || 5;
46
- const sessionId = args.sessionId ? String(args.sessionId) : null;
47
- const sessionDir = join(homedir(), ".mma", "sessions");
48
- const results = [];
49
- try {
50
- if (!fs.existsSync(sessionDir)) {
51
- return {
52
- success: true,
53
- output: t("tool.no_sessions_dir", { dir: sessionDir }),
54
- };
55
- }
56
- const entries = fs.readdirSync(sessionDir, { withFileTypes: true });
57
- for (const entry of entries) {
58
- if (!entry.isDirectory())
59
- continue;
60
- if (sessionId && entry.name !== sessionId)
61
- continue;
62
- const historyFile = join(sessionDir, entry.name, "history.jsonl");
63
- searchFile(historyFile, query, maxResults, results);
64
- if (results.length >= maxResults)
65
- break;
66
- }
67
- if (results.length === 0) {
68
- return { success: true, output: t("tool.no_history", { query }) };
69
- }
70
- return {
71
- success: true,
72
- output: t("tool.history_results", {
73
- query,
74
- results: results.join("\n"),
75
- }),
76
- };
77
- }
78
- catch (err) {
79
- return {
80
- success: false,
81
- output: t("tool.history_error", { error: String(err) }),
82
- };
83
- }
84
- },
85
- };