micro-models-agent 0.44.0 → 0.45.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 (188) hide show
  1. package/README.md +312 -312
  2. package/bin/mma.mjs +41 -41
  3. package/dist/main.js +241 -56
  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 -333
  8. package/dist/cli/completer.js +0 -168
  9. package/dist/cli/index.js +0 -2
  10. package/dist/cli/main.js +0 -140
  11. package/dist/cli/repl-commands.js +0 -633
  12. package/dist/cli/repl.js +0 -486
  13. package/dist/cli/security-commands.js +0 -166
  14. package/dist/cli/setup.js +0 -249
  15. package/dist/config/config.js +0 -202
  16. package/dist/config/defaults.js +0 -100
  17. package/dist/config/experts.js +0 -15
  18. package/dist/config/index.js +0 -3
  19. package/dist/config/security.js +0 -200
  20. package/dist/config/types.js +0 -1
  21. package/dist/core/agent-moe.js +0 -110
  22. package/dist/core/agent.js +0 -695
  23. package/dist/core/bootstrap.js +0 -337
  24. package/dist/core/index.js +0 -2
  25. package/dist/core/prompt-builder.js +0 -55
  26. package/dist/core/session-logger.js +0 -155
  27. package/dist/core/types.js +0 -1
  28. package/dist/core/workspace.js +0 -76
  29. package/dist/i18n/en.json +0 -525
  30. package/dist/i18n/index.js +0 -46
  31. package/dist/i18n/ru.json +0 -525
  32. package/dist/index.js +0 -22
  33. package/dist/llm/image-utils.js +0 -144
  34. package/dist/llm/index.js +0 -4
  35. package/dist/llm/model-loader.js +0 -78
  36. package/dist/llm/openai-compat.js +0 -353
  37. package/dist/llm/orchestrator.js +0 -194
  38. package/dist/llm/provider.js +0 -10
  39. package/dist/llm/response.js +0 -39
  40. package/dist/llm/token-counter.js +0 -39
  41. package/dist/llm/types.js +0 -1
  42. package/dist/logger/app-logger.js +0 -143
  43. package/dist/logger/file-log.js +0 -151
  44. package/dist/logger/index.js +0 -1
  45. package/dist/migration/backup.js +0 -45
  46. package/dist/migration/detect.js +0 -50
  47. package/dist/migration/index.js +0 -2
  48. package/dist/modules/browser/actions.js +0 -46
  49. package/dist/modules/browser/cookie-store.js +0 -24
  50. package/dist/modules/browser/index.js +0 -5
  51. package/dist/modules/browser/module.js +0 -28
  52. package/dist/modules/browser/session.js +0 -335
  53. package/dist/modules/browser/snapshot.js +0 -114
  54. package/dist/modules/browser/types.js +0 -9
  55. package/dist/modules/certification/cli.js +0 -176
  56. package/dist/modules/certification/fact-checker.js +0 -84
  57. package/dist/modules/certification/loader.js +0 -111
  58. package/dist/modules/certification/manifest.js +0 -50
  59. package/dist/modules/certification/runner.js +0 -162
  60. package/dist/modules/certification/scenarios.js +0 -124
  61. package/dist/modules/certification/types.js +0 -1
  62. package/dist/modules/context/index.js +0 -1
  63. package/dist/modules/context/manager.js +0 -349
  64. package/dist/modules/execution/auditor.js +0 -66
  65. package/dist/modules/execution/index.js +0 -8
  66. package/dist/modules/execution/module.js +0 -779
  67. package/dist/modules/execution/moe-executor.js +0 -266
  68. package/dist/modules/execution/plan-coverage.js +0 -68
  69. package/dist/modules/execution/plan-persister.js +0 -46
  70. package/dist/modules/execution/plan-store.js +0 -159
  71. package/dist/modules/execution/plan-validator.js +0 -153
  72. package/dist/modules/execution/planner.js +0 -85
  73. package/dist/modules/execution/stuck-detector.js +0 -347
  74. package/dist/modules/execution/tracker.js +0 -67
  75. package/dist/modules/execution/types.js +0 -1
  76. package/dist/modules/execution/verifier.js +0 -178
  77. package/dist/modules/hallucination/confidence.js +0 -59
  78. package/dist/modules/hallucination/consistency.js +0 -26
  79. package/dist/modules/hallucination/detector.js +0 -46
  80. package/dist/modules/hallucination/factual.js +0 -190
  81. package/dist/modules/hallucination/index.js +0 -5
  82. package/dist/modules/hallucination/js-identifiers.js +0 -72
  83. package/dist/modules/hallucination/llm-judge.js +0 -103
  84. package/dist/modules/index.js +0 -5
  85. package/dist/modules/indexer/cache.js +0 -38
  86. package/dist/modules/indexer/index.js +0 -3
  87. package/dist/modules/indexer/module.js +0 -192
  88. package/dist/modules/indexer/walker.js +0 -101
  89. package/dist/modules/lsp/client.js +0 -235
  90. package/dist/modules/lsp/config.js +0 -81
  91. package/dist/modules/lsp/index.js +0 -3
  92. package/dist/modules/lsp/module.js +0 -68
  93. package/dist/modules/lsp/types.js +0 -1
  94. package/dist/modules/mcp/client.js +0 -399
  95. package/dist/modules/mcp/index.js +0 -3
  96. package/dist/modules/mcp/module.js +0 -146
  97. package/dist/modules/mcp/registry.js +0 -15
  98. package/dist/modules/memory/index.js +0 -1
  99. package/dist/modules/memory/module.js +0 -48
  100. package/dist/modules/memory/search.js +0 -40
  101. package/dist/modules/memory/store.js +0 -69
  102. package/dist/modules/pipelines/engine.js +0 -60
  103. package/dist/modules/pipelines/index.js +0 -3
  104. package/dist/modules/pipelines/parser.js +0 -53
  105. package/dist/modules/pipelines/template.js +0 -14
  106. package/dist/modules/plugins/builtin/lint-on-write.js +0 -226
  107. package/dist/modules/plugins/builtin/notify.js +0 -8
  108. package/dist/modules/plugins/index.js +0 -1
  109. package/dist/modules/plugins/loader.js +0 -28
  110. package/dist/modules/plugins/manager.js +0 -161
  111. package/dist/modules/plugins/types.js +0 -1
  112. package/dist/modules/processes/index.js +0 -2
  113. package/dist/modules/processes/registry.js +0 -238
  114. package/dist/modules/processes/runner.js +0 -23
  115. package/dist/modules/registry.js +0 -45
  116. package/dist/modules/security/audit-log.js +0 -136
  117. package/dist/modules/security/audit-notifier.js +0 -292
  118. package/dist/modules/security/command-validator.js +0 -211
  119. package/dist/modules/security/content-scanner.js +0 -53
  120. package/dist/modules/security/data-sanitizer.js +0 -97
  121. package/dist/modules/security/encryption.js +0 -240
  122. package/dist/modules/security/index.js +0 -14
  123. package/dist/modules/security/network-validator.js +0 -79
  124. package/dist/modules/security/path-validator.js +0 -209
  125. package/dist/modules/security/rate-limiter.js +0 -119
  126. package/dist/modules/security/security-policies.js +0 -547
  127. package/dist/modules/security/session-encryption.js +0 -210
  128. package/dist/modules/security/session-isolation.js +0 -95
  129. package/dist/modules/session/index.js +0 -3
  130. package/dist/modules/session/manager.js +0 -172
  131. package/dist/modules/session/module.js +0 -24
  132. package/dist/modules/session/store.js +0 -228
  133. package/dist/modules/session/types.js +0 -1
  134. package/dist/modules/skills/index.js +0 -2
  135. package/dist/modules/skills/loader.js +0 -72
  136. package/dist/modules/skills/module.js +0 -130
  137. package/dist/modules/types.js +0 -1
  138. package/dist/modules/updater/checker.js +0 -32
  139. package/dist/modules/updater/index.js +0 -1
  140. package/dist/modules/user-profile/compressor.js +0 -16
  141. package/dist/modules/user-profile/index.js +0 -1
  142. package/dist/modules/user-profile/profile.js +0 -68
  143. package/dist/tools/approve.js +0 -32
  144. package/dist/tools/attach-image.js +0 -89
  145. package/dist/tools/bash.js +0 -337
  146. package/dist/tools/browser.js +0 -97
  147. package/dist/tools/create-dir.js +0 -55
  148. package/dist/tools/delete-file.js +0 -62
  149. package/dist/tools/edit-file.js +0 -79
  150. package/dist/tools/executor.js +0 -145
  151. package/dist/tools/file-info.js +0 -45
  152. package/dist/tools/filter-tools.js +0 -10
  153. package/dist/tools/glob-tool.js +0 -26
  154. package/dist/tools/grep-tool.js +0 -86
  155. package/dist/tools/index.js +0 -67
  156. package/dist/tools/list-dir.js +0 -47
  157. package/dist/tools/load-skill.js +0 -44
  158. package/dist/tools/mcp-call.js +0 -68
  159. package/dist/tools/move-file.js +0 -85
  160. package/dist/tools/path-utils.js +0 -51
  161. package/dist/tools/pipeline-run.js +0 -144
  162. package/dist/tools/preview.js +0 -2
  163. package/dist/tools/process-kill.js +0 -29
  164. package/dist/tools/process-list.js +0 -38
  165. package/dist/tools/process-log.js +0 -41
  166. package/dist/tools/question.js +0 -142
  167. package/dist/tools/read-file.js +0 -83
  168. package/dist/tools/recall.js +0 -110
  169. package/dist/tools/registry.js +0 -36
  170. package/dist/tools/remember.js +0 -67
  171. package/dist/tools/scope-check.js +0 -30
  172. package/dist/tools/search-history.js +0 -84
  173. package/dist/tools/subagent.js +0 -151
  174. package/dist/tools/types.js +0 -1
  175. package/dist/tools/user-input.js +0 -123
  176. package/dist/tools/web-browse.js +0 -86
  177. package/dist/tools/web-fetch.js +0 -98
  178. package/dist/tools/web-search.js +0 -78
  179. package/dist/tools/write-file.js +0 -83
  180. package/dist/ui/box.js +0 -81
  181. package/dist/ui/colors.js +0 -4
  182. package/dist/ui/diff.js +0 -178
  183. package/dist/ui/index.js +0 -6
  184. package/dist/ui/md-formatter.js +0 -212
  185. package/dist/ui/output.js +0 -13
  186. package/dist/ui/renderer.js +0 -204
  187. package/dist/ui/spinner.js +0 -70
  188. package/dist/ui/table.js +0 -144
@@ -1,176 +0,0 @@
1
- import { rmSync } from "fs";
2
- import { homedir } from "os";
3
- import { join, dirname } from "path";
4
- import { fileURLToPath } from "url";
5
- import { existsSync, readFileSync } from "fs";
6
- import { t } from "../../i18n/index";
7
- import { pc } from "../../ui/colors";
8
- import { loadScenarios, filterByTags } from "./loader";
9
- import { runScenario, findMmaRoot } from "./runner";
10
- import { readManifest, upsertCertification, removeCertification, } from "./manifest";
11
- const HERE = dirname(fileURLToPath(import.meta.url));
12
- const MMA_ROOT = findMmaRoot(HERE);
13
- const USER_SCENARIO_DIR = join(homedir(), ".mma", "certification", "scenarios");
14
- function readVersion() {
15
- const candidates = [join(MMA_ROOT, "package.json")];
16
- for (const p of candidates) {
17
- if (existsSync(p)) {
18
- try {
19
- const raw = JSON.parse(readFileSync(p, "utf-8"));
20
- if (raw.version)
21
- return raw.version;
22
- }
23
- catch {
24
- // Broken package.json — fall back to the default version
25
- }
26
- }
27
- }
28
- return "0.0.0";
29
- }
30
- export function parseTags(s) {
31
- return s
32
- .split(",")
33
- .map((x) => x.trim())
34
- .filter(Boolean);
35
- }
36
- export async function certify(opts) {
37
- const providerUrl = opts.providerUrl || opts.config.provider.baseUrl;
38
- if (opts.tags.includes("security") && !opts.config.security?.enabled) {
39
- console.error(pc.red(t("cli.cert_security_required")));
40
- process.exitCode = 1;
41
- return;
42
- }
43
- const { scenarios, errors } = loadScenarios(USER_SCENARIO_DIR);
44
- for (const e of errors)
45
- console.error(pc.yellow(` ${e}`));
46
- const selected = filterByTags(scenarios, opts.tags);
47
- if (selected.length === 0) {
48
- console.error(pc.red(t("cli.cert_no_scenarios", { tags: opts.tags.join(",") })));
49
- process.exitCode = 1;
50
- return;
51
- }
52
- const manifest = readManifest();
53
- const existing = manifest.certifications.find((e) => e.model === opts.name && e.providerUrl === providerUrl);
54
- if (existing && !opts.force) {
55
- console.error(pc.yellow(t("cli.cert_exists", { model: opts.name })));
56
- console.error(pc.yellow(t("cli.cert_exists_hint")));
57
- process.exitCode = 1;
58
- return;
59
- }
60
- console.log(t("cli.cert_started", { model: opts.name, provider: providerUrl }));
61
- const sandboxBase = join(process.cwd(), ".mma", "certification");
62
- const results = [];
63
- const total = selected.length;
64
- let idx = 0;
65
- for (const scenario of selected) {
66
- idx++;
67
- if (scenario.mode === "skip") {
68
- console.log(pc.dim(`[${idx}/${total}] ${scenario.id} ... skipped`));
69
- results.push({
70
- id: scenario.id,
71
- title: scenario.title,
72
- status: "skipped",
73
- passed: 0,
74
- of: 0,
75
- });
76
- continue;
77
- }
78
- const res = await runScenario(scenario, {
79
- model: opts.name,
80
- providerUrl,
81
- providerKey: opts.providerKey,
82
- contextWindow: opts.contextWindow,
83
- mmaRoot: MMA_ROOT,
84
- sandboxBase,
85
- defaultReps: opts.reps,
86
- defaultThreshold: 2,
87
- onRep: (id, rep, reps, passed, failures) => {
88
- const word = passed
89
- ? pc.green(t("cli.cert_rep_pass"))
90
- : pc.red(t("cli.cert_rep_fail"));
91
- console.log(`[${idx}/${total}] ${id} (${rep}/${reps})... ${word}`);
92
- if (!passed)
93
- console.log(` ${pc.dim(failures.join("; "))}`);
94
- },
95
- });
96
- results.push(res);
97
- }
98
- if (opts.clean) {
99
- try {
100
- rmSync(sandboxBase, { recursive: true, force: true });
101
- }
102
- catch {
103
- /* ignore */
104
- }
105
- }
106
- const suite = summarize(results);
107
- const entry = {
108
- model: opts.name,
109
- providerUrl,
110
- mmaVersion: readVersion(),
111
- certifiedAt: new Date().toISOString(),
112
- suite,
113
- results,
114
- };
115
- upsertCertification(entry);
116
- console.log(t("cli.cert_done", {
117
- passed: String(suite.passed),
118
- failed: String(suite.failed),
119
- skipped: String(suite.skipped),
120
- total: String(suite.total),
121
- }));
122
- printResults(results);
123
- }
124
- export async function certStatus(name, config) {
125
- const m = readManifest();
126
- const providerUrl = config.provider.baseUrl;
127
- const entry = m.certifications.find((e) => e.model === name && e.providerUrl === providerUrl);
128
- if (!entry) {
129
- console.log(t("cli.cert_not_found", { model: name }));
130
- return;
131
- }
132
- console.log(`${t("cli.cert_provider_col")}: ${entry.providerUrl}`);
133
- console.log(`${t("cli.cert_version_col")}: ${entry.mmaVersion} ${t("cli.cert_date_col")}: ${entry.certifiedAt.slice(0, 10)}`);
134
- console.log(`${t("cli.cert_suite_col")}: ${entry.suite.passed} pass / ${entry.suite.failed} fail / ${entry.suite.skipped} skipped`);
135
- printResults(entry.results);
136
- }
137
- export async function certList() {
138
- const m = readManifest();
139
- if (m.certifications.length === 0) {
140
- console.log(t("cli.cert_empty"));
141
- return;
142
- }
143
- for (const e of m.certifications) {
144
- console.log(` ${pc.green("✔")} ${e.model} ${pc.dim(e.providerUrl)} ${e.mmaVersion} ${e.certifiedAt.slice(0, 10)} ${e.suite.passed}/${e.suite.total} pass`);
145
- }
146
- }
147
- export async function uncertify(name, config) {
148
- const removed = removeCertification(name, config.provider.baseUrl);
149
- if (removed)
150
- console.log(t("cli.cert_uncertified", { model: name }));
151
- else
152
- console.log(t("cli.cert_not_found", { model: name }));
153
- }
154
- function summarize(results) {
155
- return {
156
- passed: results.filter((r) => r.status === "pass").length,
157
- failed: results.filter((r) => r.status === "fail").length,
158
- skipped: results.filter((r) => r.status === "skipped").length,
159
- total: results.length,
160
- };
161
- }
162
- function printResults(results) {
163
- for (const r of results) {
164
- const icon = r.status === "pass"
165
- ? pc.green("✔")
166
- : r.status === "fail"
167
- ? pc.red("✘")
168
- : r.status === "skipped"
169
- ? pc.dim("–")
170
- : pc.yellow("!");
171
- const detail = r.status === "skipped" ? pc.dim(r.title) : `${r.passed}/${r.of}`;
172
- console.log(` ${icon} ${r.id} ${detail}`);
173
- if (r.error)
174
- console.log(` ${pc.dim(r.error)}`);
175
- }
176
- }
@@ -1,84 +0,0 @@
1
- import { existsSync, readFileSync, statSync } from "fs";
2
- import { join } from "path";
3
- export function checkSandbox(sandboxDir, checks, exitCode, output) {
4
- const failures = [];
5
- for (const check of checks) {
6
- if (!runCheck(sandboxDir, check, exitCode, output)) {
7
- failures.push(describe(check));
8
- }
9
- }
10
- return { pass: failures.length === 0, failures };
11
- }
12
- function runCheck(sandboxDir, check, exitCode, output) {
13
- switch (check.type) {
14
- case "exitCode":
15
- return exitCode === (check.code ?? 0);
16
- case "outputContains":
17
- return output.includes(check.text);
18
- case "fileExists":
19
- return isFile(join(sandboxDir, check.path));
20
- case "fileNotExists":
21
- return !existsSync(join(sandboxDir, check.path));
22
- case "dirExists":
23
- return isDir(join(sandboxDir, check.path));
24
- case "fileContent": {
25
- const abs = join(sandboxDir, check.path);
26
- if (!isFile(abs))
27
- return false;
28
- const content = readFileSync(abs, "utf-8");
29
- if (check.contains !== undefined)
30
- return content.includes(check.contains);
31
- if (check.equals !== undefined)
32
- return content === check.equals;
33
- return false;
34
- }
35
- case "fileRegex": {
36
- const abs = join(sandboxDir, check.path);
37
- if (!isFile(abs))
38
- return false;
39
- return new RegExp(check.pattern).test(readFileSync(abs, "utf-8"));
40
- }
41
- default:
42
- return false;
43
- }
44
- }
45
- function isFile(p) {
46
- try {
47
- return existsSync(p) && statSync(p).isFile();
48
- }
49
- catch {
50
- return false;
51
- }
52
- }
53
- function isDir(p) {
54
- try {
55
- return existsSync(p) && statSync(p).isDirectory();
56
- }
57
- catch {
58
- return false;
59
- }
60
- }
61
- function describe(check) {
62
- switch (check.type) {
63
- case "exitCode":
64
- return `expected exit code ${check.code ?? 0}`;
65
- case "outputContains":
66
- return `output missing "${check.text}"`;
67
- case "fileExists":
68
- return `file missing: ${check.path}`;
69
- case "fileNotExists":
70
- return `file unexpectedly exists: ${check.path}`;
71
- case "dirExists":
72
- return `directory missing: ${check.path}`;
73
- case "fileContent": {
74
- const what = check.contains !== undefined
75
- ? `contains "${check.contains}"`
76
- : `equals "${check.equals}"`;
77
- return `${check.path} does not ${what}`;
78
- }
79
- case "fileRegex":
80
- return `${check.path} does not match ${check.pattern}`;
81
- default:
82
- return "unknown check";
83
- }
84
- }
@@ -1,111 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync } from "fs";
2
- import { join } from "path";
3
- import { parse as parseYaml } from "yaml";
4
- import { BUILTIN_SCENARIOS } from "./scenarios";
5
- const TAGS = ["core", "security", "image", "network", "browser"];
6
- const CHECK_TYPES = [
7
- "fileExists",
8
- "fileNotExists",
9
- "dirExists",
10
- "fileContent",
11
- "fileRegex",
12
- "exitCode",
13
- "outputContains",
14
- ];
15
- export function validateScenario(s) {
16
- const errors = [];
17
- const isSkip = s.mode === "skip";
18
- if (!s.id || !s.title || (!isSkip && !s.prompt)) {
19
- errors.push(`[${s.id ?? "?"}] missing id, title or prompt`);
20
- }
21
- if (!Array.isArray(s.tags) || s.tags.length === 0) {
22
- errors.push(`[${s.id}] tags required`);
23
- }
24
- for (const tag of s.tags) {
25
- if (!TAGS.includes(tag))
26
- errors.push(`[${s.id}] unknown tag: ${tag}`);
27
- }
28
- if (s.mode !== "run" && s.mode !== "skip") {
29
- errors.push(`[${s.id}] mode must be run or skip`);
30
- }
31
- if (!Array.isArray(s.checks) || (!isSkip && s.checks.length === 0)) {
32
- errors.push(`[${s.id}] at least one check required`);
33
- }
34
- for (const c of s.checks) {
35
- if (!CHECK_TYPES.includes(c.type)) {
36
- errors.push(`[${s.id}] unknown check type: ${c.type}`);
37
- }
38
- if (c.type === "fileContent" &&
39
- c.contains === undefined &&
40
- c.equals === undefined) {
41
- errors.push(`[${s.id}] fileContent needs contains or equals`);
42
- }
43
- }
44
- if (s.reps !== undefined && (!Number.isInteger(s.reps) || s.reps < 1)) {
45
- errors.push(`[${s.id}] reps must be a positive integer`);
46
- }
47
- if (s.passThreshold !== undefined &&
48
- (!Number.isInteger(s.passThreshold) || s.passThreshold < 1)) {
49
- errors.push(`[${s.id}] passThreshold must be a positive integer`);
50
- }
51
- if (s.passThreshold !== undefined &&
52
- s.reps !== undefined &&
53
- s.passThreshold > s.reps) {
54
- errors.push(`[${s.id}] passThreshold > reps (must be <= reps)`);
55
- }
56
- return errors;
57
- }
58
- export function loadScenarios(userDir) {
59
- const errors = [];
60
- const scenarios = [];
61
- for (const s of BUILTIN_SCENARIOS) {
62
- const errs = validateScenario(s);
63
- if (errs.length > 0)
64
- errors.push(...errs);
65
- else
66
- scenarios.push(s);
67
- }
68
- if (userDir && existsSync(userDir)) {
69
- for (const file of readdirSync(userDir)) {
70
- if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
71
- continue;
72
- try {
73
- const raw = readFileSync(join(userDir, file), "utf-8");
74
- const data = parseYaml(raw);
75
- const parsed = normalizeScenario(data, file);
76
- const errs = validateScenario(parsed);
77
- if (errs.length > 0)
78
- errors.push(...errs);
79
- else
80
- scenarios.push(parsed);
81
- }
82
- catch (e) {
83
- errors.push(`[${file}] failed to parse: ${e.message}`);
84
- }
85
- }
86
- }
87
- return { scenarios, errors };
88
- }
89
- function normalizeScenario(data, file) {
90
- const d = (data ?? {});
91
- return {
92
- id: String(d.id ?? file),
93
- title: String(d.title ?? d.id ?? file),
94
- tags: Array.isArray(d.tags) ? d.tags : [],
95
- mode: d.mode === "skip" ? "skip" : "run",
96
- prompt: String(d.prompt ?? ""),
97
- reps: typeof d.reps === "number" ? d.reps : undefined,
98
- passThreshold: typeof d.passThreshold === "number" ? d.passThreshold : undefined,
99
- fixtures: Array.isArray(d.fixtures)
100
- ? d.fixtures
101
- : undefined,
102
- checks: Array.isArray(d.checks) ? d.checks : [],
103
- skipReason: typeof d.skipReason === "string" ? d.skipReason : undefined,
104
- };
105
- }
106
- export function filterByTags(scenarios, tags) {
107
- if (!tags || tags.length === 0) {
108
- return scenarios.filter((s) => s.tags.includes("core"));
109
- }
110
- return scenarios.filter((s) => s.tags.some((t) => tags.includes(t)));
111
- }
@@ -1,50 +0,0 @@
1
- import { existsSync, readFileSync, mkdirSync, writeFileSync } from "fs";
2
- import { homedir } from "os";
3
- import { join } from "path";
4
- export const MANIFEST_PATH = join(homedir(), ".mma", "certifications.json");
5
- export function readManifest(path = MANIFEST_PATH) {
6
- try {
7
- if (existsSync(path)) {
8
- const raw = JSON.parse(readFileSync(path, "utf-8"));
9
- return { version: 1, certifications: raw.certifications ?? [] };
10
- }
11
- }
12
- catch {
13
- /* corrupted manifest → start fresh */
14
- }
15
- return { version: 1, certifications: [] };
16
- }
17
- export function saveManifest(m, path = MANIFEST_PATH) {
18
- mkdirSync(join(homedir(), ".mma"), { recursive: true });
19
- writeFileSync(path, JSON.stringify(m, null, 2), "utf-8");
20
- }
21
- export function upsertCertification(entry, path = MANIFEST_PATH) {
22
- const m = readManifest(path);
23
- const idx = m.certifications.findIndex((e) => e.model === entry.model && e.providerUrl === entry.providerUrl);
24
- if (idx >= 0)
25
- m.certifications[idx] = entry;
26
- else
27
- m.certifications.push(entry);
28
- saveManifest(m, path);
29
- return m;
30
- }
31
- export function removeCertification(model, providerUrl, path = MANIFEST_PATH) {
32
- const m = readManifest(path);
33
- const before = m.certifications.length;
34
- m.certifications = m.certifications.filter((e) => !(e.model === model && e.providerUrl === providerUrl));
35
- if (m.certifications.length !== before) {
36
- saveManifest(m, path);
37
- return true;
38
- }
39
- return false;
40
- }
41
- export function isStale(entry, currentVersion) {
42
- return entry.mmaVersion !== currentVersion;
43
- }
44
- export function getCertMark(model, providerUrl, currentVersion, path = MANIFEST_PATH) {
45
- const m = readManifest(path);
46
- const entry = m.certifications.find((e) => e.model === model && e.providerUrl === providerUrl);
47
- if (!entry)
48
- return "none";
49
- return isStale(entry, currentVersion) ? "stale" : "certified";
50
- }
@@ -1,162 +0,0 @@
1
- import { spawn } from "child_process";
2
- import { existsSync, mkdirSync, rmSync, cpSync } from "fs";
3
- import { platform } from "os";
4
- import { join, resolve, dirname } from "path";
5
- import { checkSandbox } from "./fact-checker";
6
- export async function runScenario(scenario, opts) {
7
- if (scenario.mode === "skip") {
8
- return {
9
- id: scenario.id,
10
- title: scenario.title,
11
- status: "skipped",
12
- passed: 0,
13
- of: 0,
14
- };
15
- }
16
- const reps = scenario.reps ?? opts.defaultReps;
17
- const threshold = Math.min(scenario.passThreshold ?? opts.defaultThreshold, reps);
18
- const runner = opts.runner ?? defaultRunner;
19
- const timeoutMs = opts.timeoutMs ?? 120_000;
20
- const entryPoint = resolveMmaEntry(opts.mmaRoot);
21
- let passed = 0;
22
- let firstError;
23
- for (let i = 1; i <= reps; i++) {
24
- const sandbox = join(opts.sandboxBase, `run-${scenario.id}-${i}`);
25
- let failures = [];
26
- let exitCode = -1;
27
- let output = "";
28
- try {
29
- prepareSandbox(sandbox, scenario, opts.mmaRoot);
30
- const args = [
31
- entryPoint,
32
- "--no-agents-md",
33
- "--exit-on-complete",
34
- "-d",
35
- sandbox,
36
- scenario.prompt,
37
- ];
38
- const env = {
39
- ...process.env,
40
- MMA_MODEL: opts.model,
41
- MMA_PROVIDER_BASEURL: opts.providerUrl,
42
- MMA_CONTEXT_WINDOW: String(opts.contextWindow ?? 32000),
43
- };
44
- if (opts.providerKey)
45
- env.MMA_PROVIDER_APIKEY = opts.providerKey;
46
- const res = await runner(env, opts.mmaRoot, args, timeoutMs);
47
- output = `${res.stdout}\n${res.stderr}`;
48
- exitCode = res.code ?? -1;
49
- const outcome = checkSandbox(sandbox, scenario.checks, exitCode, output);
50
- failures = outcome.failures;
51
- if (res.timedOut)
52
- failures.push(`rep ${i} timed out`);
53
- }
54
- catch (e) {
55
- return {
56
- id: scenario.id,
57
- title: scenario.title,
58
- status: "error",
59
- passed,
60
- of: reps,
61
- error: e.message,
62
- };
63
- }
64
- const pass = failures.length === 0;
65
- if (pass)
66
- passed++;
67
- else if (!firstError)
68
- firstError = failures.join("; ");
69
- opts.onRep?.(scenario.id, i, reps, pass, failures);
70
- }
71
- const status = passed >= threshold ? "pass" : "fail";
72
- return {
73
- id: scenario.id,
74
- title: scenario.title,
75
- status,
76
- passed,
77
- of: reps,
78
- error: status === "pass" ? undefined : firstError,
79
- };
80
- }
81
- function prepareSandbox(sandbox, scenario, mmaRoot) {
82
- rmSync(sandbox, { recursive: true, force: true });
83
- mkdirSync(sandbox, { recursive: true });
84
- for (const f of scenario.fixtures ?? []) {
85
- const src = join(mmaRoot, f.source);
86
- if (!existsSync(src)) {
87
- throw new Error(`fixture missing: ${f.source}`);
88
- }
89
- const dest = join(sandbox, f.dest);
90
- mkdirSync(dirname(dest), { recursive: true });
91
- cpSync(src, dest);
92
- }
93
- }
94
- export function resolveMmaEntry(mmaRoot) {
95
- const dev = join(mmaRoot, "src", "cli", "main.ts");
96
- if (existsSync(dev))
97
- return dev;
98
- return join(mmaRoot, "dist", "main.js");
99
- }
100
- export function findMmaRoot(fromDir) {
101
- const candidates = [
102
- resolve(fromDir, "..", "..", ".."),
103
- resolve(fromDir, ".."),
104
- ];
105
- for (const c of candidates) {
106
- if (existsSync(join(c, "package.json")))
107
- return c;
108
- }
109
- return process.cwd();
110
- }
111
- export const defaultRunner = (env, cwd, args, timeoutMs) => new Promise((resolvePromise) => {
112
- const child = spawn(process.execPath, args, {
113
- cwd,
114
- env,
115
- windowsHide: true,
116
- stdio: ["ignore", "pipe", "pipe"],
117
- });
118
- let stdout = "";
119
- let stderr = "";
120
- let timedOut = false;
121
- child.stdout?.on("data", (c) => {
122
- stdout += c.toString("utf-8");
123
- });
124
- child.stderr?.on("data", (c) => {
125
- stderr += c.toString("utf-8");
126
- });
127
- const timer = setTimeout(() => {
128
- timedOut = true;
129
- killTree(child);
130
- }, timeoutMs);
131
- child.on("close", (code) => {
132
- clearTimeout(timer);
133
- resolvePromise({ code, stdout, stderr, timedOut });
134
- });
135
- child.on("error", () => {
136
- clearTimeout(timer);
137
- resolvePromise({ code: null, stdout, stderr, timedOut });
138
- });
139
- });
140
- function killTree(child) {
141
- const pid = child.pid;
142
- if (!pid)
143
- return;
144
- if (platform() === "win32") {
145
- spawn("taskkill", ["/pid", String(pid), "/T", "/F"], {
146
- windowsHide: true,
147
- stdio: "ignore",
148
- });
149
- return;
150
- }
151
- try {
152
- process.kill(-pid, "SIGTERM");
153
- }
154
- catch {
155
- try {
156
- child.kill("SIGKILL");
157
- }
158
- catch {
159
- /* already dead */
160
- }
161
- }
162
- }