magi-ai 0.1.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 (300) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +377 -0
  3. package/README.md +377 -0
  4. package/dist/bin/magi-benchmark.d.ts +14 -0
  5. package/dist/bin/magi-benchmark.js +93 -0
  6. package/dist/bin/magi-mcp.d.ts +8 -0
  7. package/dist/bin/magi-mcp.js +28 -0
  8. package/dist/bin/magi.d.ts +2 -0
  9. package/dist/bin/magi.js +634 -0
  10. package/dist/src/adapters/base.d.ts +34 -0
  11. package/dist/src/adapters/base.js +149 -0
  12. package/dist/src/adapters/claude.d.ts +29 -0
  13. package/dist/src/adapters/claude.js +65 -0
  14. package/dist/src/adapters/codex.d.ts +21 -0
  15. package/dist/src/adapters/codex.js +41 -0
  16. package/dist/src/adapters/gemini.d.ts +18 -0
  17. package/dist/src/adapters/gemini.js +31 -0
  18. package/dist/src/adapters/registry.d.ts +19 -0
  19. package/dist/src/adapters/registry.js +59 -0
  20. package/dist/src/audit/hash-chain.d.ts +21 -0
  21. package/dist/src/audit/hash-chain.js +70 -0
  22. package/dist/src/audit/types.d.ts +25 -0
  23. package/dist/src/audit/types.js +1 -0
  24. package/dist/src/audit/writer.d.ts +18 -0
  25. package/dist/src/audit/writer.js +100 -0
  26. package/dist/src/benchmark/golden-tasks.d.ts +9 -0
  27. package/dist/src/benchmark/golden-tasks.js +476 -0
  28. package/dist/src/benchmark/reporter.d.ts +5 -0
  29. package/dist/src/benchmark/reporter.js +107 -0
  30. package/dist/src/benchmark/runner.d.ts +30 -0
  31. package/dist/src/benchmark/runner.js +224 -0
  32. package/dist/src/benchmark/scorer.d.ts +12 -0
  33. package/dist/src/benchmark/scorer.js +124 -0
  34. package/dist/src/benchmark/types.d.ts +54 -0
  35. package/dist/src/benchmark/types.js +1 -0
  36. package/dist/src/cache/deliberation-cache.d.ts +49 -0
  37. package/dist/src/cache/deliberation-cache.js +127 -0
  38. package/dist/src/cli/commands/config-cmd.d.ts +11 -0
  39. package/dist/src/cli/commands/config-cmd.js +190 -0
  40. package/dist/src/cli/commands/demo.d.ts +12 -0
  41. package/dist/src/cli/commands/demo.js +66 -0
  42. package/dist/src/cli/commands/setup.d.ts +7 -0
  43. package/dist/src/cli/commands/setup.js +182 -0
  44. package/dist/src/cli/i18n.d.ts +89 -0
  45. package/dist/src/cli/i18n.js +176 -0
  46. package/dist/src/cli/interactive-select.d.ts +27 -0
  47. package/dist/src/cli/interactive-select.js +130 -0
  48. package/dist/src/cli/tui-setup.d.ts +24 -0
  49. package/dist/src/cli/tui-setup.js +42 -0
  50. package/dist/src/config/cli-detector.d.ts +37 -0
  51. package/dist/src/config/cli-detector.js +99 -0
  52. package/dist/src/config/user-config.d.ts +81 -0
  53. package/dist/src/config/user-config.js +134 -0
  54. package/dist/src/context/auto-collector.d.ts +43 -0
  55. package/dist/src/context/auto-collector.js +337 -0
  56. package/dist/src/context/manager.d.ts +35 -0
  57. package/dist/src/context/manager.js +162 -0
  58. package/dist/src/context/serializer.d.ts +20 -0
  59. package/dist/src/context/serializer.js +52 -0
  60. package/dist/src/demo/recorded-deliberation.d.ts +13 -0
  61. package/dist/src/demo/recorded-deliberation.js +277 -0
  62. package/dist/src/engine/angel-detector.d.ts +83 -0
  63. package/dist/src/engine/angel-detector.js +334 -0
  64. package/dist/src/engine/at-field.d.ts +40 -0
  65. package/dist/src/engine/at-field.js +195 -0
  66. package/dist/src/engine/berserk-orchestrator.d.ts +66 -0
  67. package/dist/src/engine/berserk-orchestrator.js +378 -0
  68. package/dist/src/engine/change-metrics.d.ts +56 -0
  69. package/dist/src/engine/change-metrics.js +214 -0
  70. package/dist/src/engine/consensus.d.ts +20 -0
  71. package/dist/src/engine/consensus.js +146 -0
  72. package/dist/src/engine/dead-sea-scrolls.d.ts +132 -0
  73. package/dist/src/engine/dead-sea-scrolls.js +610 -0
  74. package/dist/src/engine/drift-detector.d.ts +39 -0
  75. package/dist/src/engine/drift-detector.js +225 -0
  76. package/dist/src/engine/dummy-plug.d.ts +44 -0
  77. package/dist/src/engine/dummy-plug.js +190 -0
  78. package/dist/src/engine/engram-manager.d.ts +55 -0
  79. package/dist/src/engine/engram-manager.js +306 -0
  80. package/dist/src/engine/events.d.ts +130 -0
  81. package/dist/src/engine/events.js +44 -0
  82. package/dist/src/engine/gospel.d.ts +30 -0
  83. package/dist/src/engine/gospel.js +129 -0
  84. package/dist/src/engine/hallucination-detector.d.ts +33 -0
  85. package/dist/src/engine/hallucination-detector.js +215 -0
  86. package/dist/src/engine/human-resolver.d.ts +19 -0
  87. package/dist/src/engine/human-resolver.js +89 -0
  88. package/dist/src/engine/instrumentality.d.ts +64 -0
  89. package/dist/src/engine/instrumentality.js +297 -0
  90. package/dist/src/engine/iruel-battle.d.ts +79 -0
  91. package/dist/src/engine/iruel-battle.js +319 -0
  92. package/dist/src/engine/kernel/deliberation-kernel.d.ts +12 -0
  93. package/dist/src/engine/kernel/deliberation-kernel.js +303 -0
  94. package/dist/src/engine/kernel/index.d.ts +8 -0
  95. package/dist/src/engine/kernel/index.js +7 -0
  96. package/dist/src/engine/kernel/phase-runner.d.ts +10 -0
  97. package/dist/src/engine/kernel/phase-runner.js +155 -0
  98. package/dist/src/engine/kernel/post-processor.d.ts +17 -0
  99. package/dist/src/engine/kernel/post-processor.js +131 -0
  100. package/dist/src/engine/kernel/types.d.ts +107 -0
  101. package/dist/src/engine/kernel/types.js +1 -0
  102. package/dist/src/engine/kernel/unit-executor.d.ts +6 -0
  103. package/dist/src/engine/kernel/unit-executor.js +132 -0
  104. package/dist/src/engine/lcl-manager.d.ts +44 -0
  105. package/dist/src/engine/lcl-manager.js +143 -0
  106. package/dist/src/engine/middleware/cache.d.ts +7 -0
  107. package/dist/src/engine/middleware/cache.js +29 -0
  108. package/dist/src/engine/middleware/chain.d.ts +18 -0
  109. package/dist/src/engine/middleware/chain.js +45 -0
  110. package/dist/src/engine/middleware/firewall.d.ts +8 -0
  111. package/dist/src/engine/middleware/firewall.js +24 -0
  112. package/dist/src/engine/middleware/index.d.ts +4 -0
  113. package/dist/src/engine/middleware/index.js +3 -0
  114. package/dist/src/engine/middleware/types.d.ts +43 -0
  115. package/dist/src/engine/middleware/types.js +1 -0
  116. package/dist/src/engine/nebuchadnezzar-key.d.ts +61 -0
  117. package/dist/src/engine/nebuchadnezzar-key.js +203 -0
  118. package/dist/src/engine/neon-genesis.d.ts +52 -0
  119. package/dist/src/engine/neon-genesis.js +203 -0
  120. package/dist/src/engine/objective-judge.d.ts +53 -0
  121. package/dist/src/engine/objective-judge.js +214 -0
  122. package/dist/src/engine/offline-mode.d.ts +18 -0
  123. package/dist/src/engine/offline-mode.js +46 -0
  124. package/dist/src/engine/orchestrator.d.ts +79 -0
  125. package/dist/src/engine/orchestrator.js +58 -0
  126. package/dist/src/engine/secret-cipher.d.ts +26 -0
  127. package/dist/src/engine/secret-cipher.js +114 -0
  128. package/dist/src/engine/seele-council.d.ts +90 -0
  129. package/dist/src/engine/seele-council.js +482 -0
  130. package/dist/src/engine/self-destruct.d.ts +61 -0
  131. package/dist/src/engine/self-destruct.js +231 -0
  132. package/dist/src/engine/self-evolution.d.ts +64 -0
  133. package/dist/src/engine/self-evolution.js +368 -0
  134. package/dist/src/engine/sync-rate.d.ts +45 -0
  135. package/dist/src/engine/sync-rate.js +151 -0
  136. package/dist/src/engine/type666-firewall.d.ts +76 -0
  137. package/dist/src/engine/type666-firewall.js +343 -0
  138. package/dist/src/engine/umbilical-cable.d.ts +41 -0
  139. package/dist/src/engine/umbilical-cable.js +192 -0
  140. package/dist/src/index.d.ts +106 -0
  141. package/dist/src/index.js +426 -0
  142. package/dist/src/mcp/server.d.ts +38 -0
  143. package/dist/src/mcp/server.js +196 -0
  144. package/dist/src/metrics/token-tracker.d.ts +38 -0
  145. package/dist/src/metrics/token-tracker.js +112 -0
  146. package/dist/src/parsers/json-extractor.d.ts +9 -0
  147. package/dist/src/parsers/json-extractor.js +239 -0
  148. package/dist/src/parsers/opinion-schema.d.ts +81 -0
  149. package/dist/src/parsers/opinion-schema.js +147 -0
  150. package/dist/src/parsers/unstructured-parser.d.ts +20 -0
  151. package/dist/src/parsers/unstructured-parser.js +122 -0
  152. package/dist/src/pipelines/architecture.d.ts +10 -0
  153. package/dist/src/pipelines/architecture.js +9 -0
  154. package/dist/src/pipelines/bug-analysis.d.ts +9 -0
  155. package/dist/src/pipelines/bug-analysis.js +8 -0
  156. package/dist/src/pipelines/code-review.d.ts +10 -0
  157. package/dist/src/pipelines/code-review.js +30 -0
  158. package/dist/src/pipelines/custom.d.ts +14 -0
  159. package/dist/src/pipelines/custom.js +29 -0
  160. package/dist/src/pipelines/registry.d.ts +9 -0
  161. package/dist/src/pipelines/registry.js +20 -0
  162. package/dist/src/prompts/personas.d.ts +6 -0
  163. package/dist/src/prompts/personas.js +44 -0
  164. package/dist/src/prompts/schemas.d.ts +4 -0
  165. package/dist/src/prompts/schemas.js +24 -0
  166. package/dist/src/prompts/templates.d.ts +6 -0
  167. package/dist/src/prompts/templates.js +91 -0
  168. package/dist/src/repl/accessibility.d.ts +23 -0
  169. package/dist/src/repl/accessibility.js +46 -0
  170. package/dist/src/repl/banner.d.ts +4 -0
  171. package/dist/src/repl/banner.js +28 -0
  172. package/dist/src/repl/boot-animation.d.ts +13 -0
  173. package/dist/src/repl/boot-animation.js +143 -0
  174. package/dist/src/repl/completer.d.ts +21 -0
  175. package/dist/src/repl/completer.js +168 -0
  176. package/dist/src/repl/context.d.ts +24 -0
  177. package/dist/src/repl/context.js +42 -0
  178. package/dist/src/repl/display-utils.d.ts +13 -0
  179. package/dist/src/repl/display-utils.js +65 -0
  180. package/dist/src/repl/event-listener.d.ts +18 -0
  181. package/dist/src/repl/event-listener.js +112 -0
  182. package/dist/src/repl/export-formatter.d.ts +8 -0
  183. package/dist/src/repl/export-formatter.js +73 -0
  184. package/dist/src/repl/ghost-text.d.ts +31 -0
  185. package/dist/src/repl/ghost-text.js +119 -0
  186. package/dist/src/repl/handoff-animation.d.ts +15 -0
  187. package/dist/src/repl/handoff-animation.js +65 -0
  188. package/dist/src/repl/history.d.ts +16 -0
  189. package/dist/src/repl/history.js +130 -0
  190. package/dist/src/repl/job-registry.d.ts +26 -0
  191. package/dist/src/repl/job-registry.js +80 -0
  192. package/dist/src/repl/magi-repl.d.ts +72 -0
  193. package/dist/src/repl/magi-repl.js +1008 -0
  194. package/dist/src/repl/multiline-input.d.ts +45 -0
  195. package/dist/src/repl/multiline-input.js +78 -0
  196. package/dist/src/repl/prompt-builder.d.ts +19 -0
  197. package/dist/src/repl/prompt-builder.js +36 -0
  198. package/dist/src/repl/repl-state.d.ts +5 -0
  199. package/dist/src/repl/repl-state.js +19 -0
  200. package/dist/src/repl/result-display.d.ts +8 -0
  201. package/dist/src/repl/result-display.js +195 -0
  202. package/dist/src/repl/session-stats.d.ts +26 -0
  203. package/dist/src/repl/session-stats.js +119 -0
  204. package/dist/src/repl/slash-commands.d.ts +60 -0
  205. package/dist/src/repl/slash-commands.js +725 -0
  206. package/dist/src/repl/terminal-sanitize.d.ts +14 -0
  207. package/dist/src/repl/terminal-sanitize.js +19 -0
  208. package/dist/src/reporters/console.d.ts +7 -0
  209. package/dist/src/reporters/console.js +78 -0
  210. package/dist/src/reporters/json.d.ts +2 -0
  211. package/dist/src/reporters/json.js +3 -0
  212. package/dist/src/reporters/markdown.d.ts +2 -0
  213. package/dist/src/reporters/markdown.js +65 -0
  214. package/dist/src/reporters/streaming.d.ts +20 -0
  215. package/dist/src/reporters/streaming.js +178 -0
  216. package/dist/src/tui/activity-log.d.ts +23 -0
  217. package/dist/src/tui/activity-log.js +67 -0
  218. package/dist/src/tui/animations.d.ts +39 -0
  219. package/dist/src/tui/animations.js +167 -0
  220. package/dist/src/tui/ansi.d.ts +28 -0
  221. package/dist/src/tui/ansi.js +51 -0
  222. package/dist/src/tui/boot-sequence.d.ts +11 -0
  223. package/dist/src/tui/boot-sequence.js +98 -0
  224. package/dist/src/tui/colors.d.ts +101 -0
  225. package/dist/src/tui/colors.js +71 -0
  226. package/dist/src/tui/header.d.ts +24 -0
  227. package/dist/src/tui/header.js +122 -0
  228. package/dist/src/tui/index.d.ts +3 -0
  229. package/dist/src/tui/index.js +3 -0
  230. package/dist/src/tui/keypress.d.ts +25 -0
  231. package/dist/src/tui/keypress.js +95 -0
  232. package/dist/src/tui/layout.d.ts +74 -0
  233. package/dist/src/tui/layout.js +171 -0
  234. package/dist/src/tui/magi-tui.d.ts +101 -0
  235. package/dist/src/tui/magi-tui.js +754 -0
  236. package/dist/src/tui/panel.d.ts +45 -0
  237. package/dist/src/tui/panel.js +292 -0
  238. package/dist/src/tui/screen-buffer.d.ts +54 -0
  239. package/dist/src/tui/screen-buffer.js +262 -0
  240. package/dist/src/tui/status-bar.d.ts +25 -0
  241. package/dist/src/tui/status-bar.js +124 -0
  242. package/dist/src/tui/terminal-detect.d.ts +26 -0
  243. package/dist/src/tui/terminal-detect.js +44 -0
  244. package/dist/src/tui/tui-helpers.d.ts +12 -0
  245. package/dist/src/tui/tui-helpers.js +37 -0
  246. package/dist/src/types/adapter.d.ts +75 -0
  247. package/dist/src/types/adapter.js +36 -0
  248. package/dist/src/types/config.d.ts +108 -0
  249. package/dist/src/types/config.js +85 -0
  250. package/dist/src/types/consensus.d.ts +55 -0
  251. package/dist/src/types/consensus.js +17 -0
  252. package/dist/src/types/core.d.ts +178 -0
  253. package/dist/src/types/core.js +85 -0
  254. package/dist/src/types/magi-api.d.ts +62 -0
  255. package/dist/src/types/magi-api.js +7 -0
  256. package/dist/src/types/phase-h.d.ts +142 -0
  257. package/dist/src/types/phase-h.js +7 -0
  258. package/dist/src/types/phase-i.d.ts +186 -0
  259. package/dist/src/types/phase-i.js +6 -0
  260. package/dist/src/types/phase-k.d.ts +259 -0
  261. package/dist/src/types/phase-k.js +6 -0
  262. package/dist/src/types/phase-l.d.ts +199 -0
  263. package/dist/src/types/phase-l.js +6 -0
  264. package/dist/src/types/pipeline.d.ts +37 -0
  265. package/dist/src/types/pipeline.js +2 -0
  266. package/dist/src/utils/abstain-factory.d.ts +2 -0
  267. package/dist/src/utils/abstain-factory.js +18 -0
  268. package/dist/src/utils/errors.d.ts +34 -0
  269. package/dist/src/utils/errors.js +59 -0
  270. package/dist/src/utils/file-validator.d.ts +50 -0
  271. package/dist/src/utils/file-validator.js +124 -0
  272. package/dist/src/utils/fire-and-forget.d.ts +5 -0
  273. package/dist/src/utils/fire-and-forget.js +10 -0
  274. package/dist/src/utils/flag-validator.d.ts +21 -0
  275. package/dist/src/utils/flag-validator.js +79 -0
  276. package/dist/src/utils/freeze.d.ts +8 -0
  277. package/dist/src/utils/freeze.js +16 -0
  278. package/dist/src/utils/language-detector.d.ts +16 -0
  279. package/dist/src/utils/language-detector.js +159 -0
  280. package/dist/src/utils/latency-tracker.d.ts +45 -0
  281. package/dist/src/utils/latency-tracker.js +100 -0
  282. package/dist/src/utils/logger.d.ts +33 -0
  283. package/dist/src/utils/logger.js +112 -0
  284. package/dist/src/utils/process.d.ts +40 -0
  285. package/dist/src/utils/process.js +253 -0
  286. package/dist/src/utils/retry.d.ts +12 -0
  287. package/dist/src/utils/retry.js +30 -0
  288. package/dist/src/utils/safe-fs.d.ts +38 -0
  289. package/dist/src/utils/safe-fs.js +56 -0
  290. package/dist/src/utils/safe-json-parse.d.ts +15 -0
  291. package/dist/src/utils/safe-json-parse.js +49 -0
  292. package/dist/src/utils/sanitize.d.ts +14 -0
  293. package/dist/src/utils/sanitize.js +186 -0
  294. package/dist/src/utils/semaphore.d.ts +22 -0
  295. package/dist/src/utils/semaphore.js +57 -0
  296. package/dist/src/utils/shutdown.d.ts +6 -0
  297. package/dist/src/utils/shutdown.js +51 -0
  298. package/dist/src/utils/tty.d.ts +5 -0
  299. package/dist/src/utils/tty.js +7 -0
  300. package/package.json +82 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * File validator — shared by CLI, REPL, and MCP for input file validation.
3
+ *
4
+ * Extracted from slash-commands.ts validateWorkspaceFile() to provide
5
+ * a unified, structured result interface usable across all entry points.
6
+ */
7
+ import { lstat, realpath, open } from 'node:fs/promises';
8
+ import { dirname, resolve, relative } from 'node:path';
9
+ import { isBinaryExtension } from './language-detector.js';
10
+ // ── Public API ────────────────────────────────────────────────
11
+ /**
12
+ * Validate a file path for safe access.
13
+ *
14
+ * Checks (in order):
15
+ * 1. Null bytes / empty path
16
+ * 2. Path traversal (relative path escapes basedir)
17
+ * 3. Realpath traversal (symlink parent escape)
18
+ * 4. Symlink target
19
+ * 5. Regular file check
20
+ * 6. Size limit
21
+ * 7. Binary extension check
22
+ * 8. Binary null-byte check (fallback)
23
+ */
24
+ export async function validateFile(inputPath, options) {
25
+ // ── 1. Basic path validation ──
26
+ if (!inputPath || typeof inputPath !== 'string') {
27
+ return fail('INVALID_PATH', 'Invalid file path.');
28
+ }
29
+ if (inputPath.includes('\0')) {
30
+ return fail('INVALID_PATH', 'File path contains null bytes.');
31
+ }
32
+ const basedir = options?.basedir ?? process.cwd();
33
+ const absolutePath = resolve(basedir, inputPath);
34
+ const relativePath = relative(basedir, absolutePath);
35
+ // ── 2. Path traversal check ──
36
+ if (relativePath.startsWith('..') || resolve(relativePath) === relativePath) {
37
+ return fail('PATH_TRAVERSAL', 'Path traversal denied.');
38
+ }
39
+ // ── 3. Realpath traversal check ──
40
+ try {
41
+ const [realBase, realParent] = await Promise.all([
42
+ realpath(basedir),
43
+ realpath(dirname(absolutePath)),
44
+ ]);
45
+ if (!realParent.startsWith(`${realBase}/`) && realParent !== realBase) {
46
+ return fail('PATH_TRAVERSAL', 'Path traversal denied (symlink escape).');
47
+ }
48
+ }
49
+ catch (error) {
50
+ return mapErrnoError(error, inputPath);
51
+ }
52
+ // ── 4-6. Stat checks ──
53
+ let fileStats;
54
+ try {
55
+ fileStats = await lstat(absolutePath);
56
+ }
57
+ catch (error) {
58
+ return mapErrnoError(error, inputPath);
59
+ }
60
+ if (fileStats.isSymbolicLink()) {
61
+ return fail('SYMLINK', 'Symlink targets are not allowed.');
62
+ }
63
+ if (!fileStats.isFile()) {
64
+ return fail('NOT_FILE', `Not a regular file: ${inputPath}`);
65
+ }
66
+ if (options?.maxBytes !== undefined && fileStats.size > options.maxBytes) {
67
+ const sizeMB = (fileStats.size / 1024 / 1024).toFixed(1);
68
+ const limitKB = Math.round(options.maxBytes / 1024);
69
+ return fail('TOO_LARGE', `File too large: ${sizeMB} MB (max ${limitKB} KB).`);
70
+ }
71
+ // ── 7-8. Binary checks ──
72
+ if (options?.rejectBinary) {
73
+ if (isBinaryExtension(absolutePath)) {
74
+ return fail('BINARY', `Binary file detected (by extension): ${inputPath}`);
75
+ }
76
+ if (await containsNullBytes(absolutePath)) {
77
+ return fail('BINARY', `Binary file detected (null bytes): ${inputPath}`);
78
+ }
79
+ }
80
+ return {
81
+ ok: true,
82
+ file: {
83
+ absolutePath,
84
+ relativePath,
85
+ sizeBytes: fileStats.size,
86
+ },
87
+ };
88
+ }
89
+ /**
90
+ * Check if the first N bytes of a file contain null bytes.
91
+ * Returns true if null bytes are found (likely binary file).
92
+ */
93
+ export async function containsNullBytes(filePath, checkBytes = 512) {
94
+ let fh;
95
+ try {
96
+ fh = await open(filePath, 'r');
97
+ const buf = Buffer.alloc(checkBytes);
98
+ const { bytesRead } = await fh.read(buf, 0, checkBytes, 0);
99
+ for (let i = 0; i < bytesRead; i++) {
100
+ if (buf[i] === 0)
101
+ return true;
102
+ }
103
+ return false;
104
+ }
105
+ catch {
106
+ // If we can't read, don't block — let the caller handle read errors
107
+ return false;
108
+ }
109
+ finally {
110
+ await fh?.close();
111
+ }
112
+ }
113
+ // ── Helpers ───────────────────────────────────────────────────
114
+ function fail(code, message) {
115
+ return { ok: false, error: { code, message } };
116
+ }
117
+ function mapErrnoError(error, inputPath) {
118
+ const code = error.code;
119
+ if (code === 'ENOENT')
120
+ return fail('ENOENT', `File not found: ${inputPath}`);
121
+ if (code === 'EACCES')
122
+ return fail('EACCES', `Permission denied: ${inputPath}`);
123
+ return fail('ENOENT', `Cannot access: ${inputPath}`);
124
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Execute a promise without awaiting, logging any errors at debug level.
3
+ * Prevents unhandled rejection warnings while keeping the error trail.
4
+ */
5
+ export declare function fireAndForget(promise: Promise<unknown>, label: string): void;
@@ -0,0 +1,10 @@
1
+ import { logger } from './logger.js';
2
+ /**
3
+ * Execute a promise without awaiting, logging any errors at debug level.
4
+ * Prevents unhandled rejection warnings while keeping the error trail.
5
+ */
6
+ export function fireAndForget(promise, label) {
7
+ promise.catch((err) => {
8
+ logger.debug(`[fire-and-forget:${label}] ${String(err)}`);
9
+ });
10
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * CLI flag safety validation for MAGI system.
3
+ *
4
+ * Prevents unsafe flags (e.g. permission bypasses, sandbox disabling)
5
+ * from being passed to AI CLI tools, with tool-specific overrides
6
+ * where a flag is required for normal operation.
7
+ */
8
+ import { MagiError } from './errors.js';
9
+ export declare class UnsafeFlagError extends MagiError {
10
+ readonly flags: string[];
11
+ readonly tool: string;
12
+ constructor(flags: string[], tool: string);
13
+ }
14
+ /**
15
+ * Assert that no unsafe flags are present for the given tool.
16
+ *
17
+ * @param flags - The flags array to validate
18
+ * @param tool - The CLI tool name (e.g. "claude", "codex", "gemini")
19
+ * @throws {UnsafeFlagError} if any unsafe flags are detected
20
+ */
21
+ export declare function assertNoUnsafeFlags(flags: string[], tool: string): void;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * CLI flag safety validation for MAGI system.
3
+ *
4
+ * Prevents unsafe flags (e.g. permission bypasses, sandbox disabling)
5
+ * from being passed to AI CLI tools, with tool-specific overrides
6
+ * where a flag is required for normal operation.
7
+ */
8
+ import { MagiError } from './errors.js';
9
+ // ---------------------------------------------------------------------------
10
+ // Error
11
+ // ---------------------------------------------------------------------------
12
+ export class UnsafeFlagError extends MagiError {
13
+ flags;
14
+ tool;
15
+ constructor(flags, tool) {
16
+ super(`Unsafe flags detected for ${tool}: ${flags.join(', ')}`, 'UNSAFE_FLAG');
17
+ this.flags = flags;
18
+ this.tool = tool;
19
+ }
20
+ }
21
+ // ---------------------------------------------------------------------------
22
+ // Constants
23
+ // ---------------------------------------------------------------------------
24
+ /** Flags that bypass safety measures in AI CLIs. */
25
+ const UNSAFE_FLAGS = [
26
+ '--dangerously-skip-permissions', // Claude
27
+ '--no-verify', // Various
28
+ '--approval-mode=full-auto', // Gemini (bypasses plan mode)
29
+ '--full-auto', // But codex MUST use full-auto, so context matters
30
+ '--yolo', // Codex unofficial
31
+ '--no-sandbox', // Codex
32
+ '--sandbox=off', // Codex
33
+ ];
34
+ /**
35
+ * Flags that are always allowed despite looking unsafe.
36
+ * Codex requires --full-auto for non-interactive exec.
37
+ */
38
+ const ALLOWED_OVERRIDES = {
39
+ codex: ['--full-auto'],
40
+ };
41
+ // ---------------------------------------------------------------------------
42
+ // Validation
43
+ // ---------------------------------------------------------------------------
44
+ /**
45
+ * Assert that no unsafe flags are present for the given tool.
46
+ *
47
+ * @param flags - The flags array to validate
48
+ * @param tool - The CLI tool name (e.g. "claude", "codex", "gemini")
49
+ * @throws {UnsafeFlagError} if any unsafe flags are detected
50
+ */
51
+ export function assertNoUnsafeFlags(flags, tool) {
52
+ const overrides = ALLOWED_OVERRIDES[tool] ?? [];
53
+ const detected = [];
54
+ for (let i = 0; i < flags.length; i++) {
55
+ const flag = flags[i];
56
+ // Direct match (existing logic)
57
+ if (UNSAFE_FLAGS.includes(flag) &&
58
+ !overrides.includes(flag)) {
59
+ detected.push(flag);
60
+ continue;
61
+ }
62
+ // Split form: '--approval-mode=full-auto' → ['--approval-mode', 'full-auto']
63
+ for (const unsafe of UNSAFE_FLAGS) {
64
+ if (!unsafe.includes('='))
65
+ continue;
66
+ const eqIdx = unsafe.indexOf('=');
67
+ const name = unsafe.slice(0, eqIdx);
68
+ const value = unsafe.slice(eqIdx + 1);
69
+ if (flag === name && flags[i + 1] === value && !overrides.includes(unsafe)) {
70
+ detected.push(`${flag}=${flags[i + 1]}`);
71
+ i++;
72
+ break;
73
+ }
74
+ }
75
+ }
76
+ if (detected.length > 0) {
77
+ throw new UnsafeFlagError(detected, tool);
78
+ }
79
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Deep freeze utility for immutable configuration objects.
3
+ *
4
+ * Recursively applies Object.freeze() to an object and all nested
5
+ * objects/arrays. Already-frozen objects are skipped to prevent
6
+ * infinite recursion with circular references.
7
+ */
8
+ export declare function deepFreeze<T extends object>(obj: T): Readonly<T>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Deep freeze utility for immutable configuration objects.
3
+ *
4
+ * Recursively applies Object.freeze() to an object and all nested
5
+ * objects/arrays. Already-frozen objects are skipped to prevent
6
+ * infinite recursion with circular references.
7
+ */
8
+ export function deepFreeze(obj) {
9
+ Object.freeze(obj);
10
+ for (const value of Object.values(obj)) {
11
+ if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {
12
+ deepFreeze(value);
13
+ }
14
+ }
15
+ return obj;
16
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Language detector — maps file extensions to programming language names.
3
+ *
4
+ * Used by file-validator, auto-collector, and CLI/REPL/MCP to set
5
+ * `artifact.language` automatically.
6
+ */
7
+ /**
8
+ * Detect programming language from file path extension.
9
+ * Case-insensitive. Returns undefined for unknown extensions.
10
+ */
11
+ export declare function detectLanguage(filePath: string): string | undefined;
12
+ /**
13
+ * Check if a file extension indicates a binary file.
14
+ * Case-insensitive. Returns false for unknown extensions.
15
+ */
16
+ export declare function isBinaryExtension(filePath: string): boolean;
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Language detector — maps file extensions to programming language names.
3
+ *
4
+ * Used by file-validator, auto-collector, and CLI/REPL/MCP to set
5
+ * `artifact.language` automatically.
6
+ */
7
+ /** Extension → language mapping (lowercase, without dot) */
8
+ const EXTENSION_MAP = {
9
+ // JavaScript / TypeScript
10
+ js: 'javascript',
11
+ jsx: 'javascript',
12
+ mjs: 'javascript',
13
+ cjs: 'javascript',
14
+ ts: 'typescript',
15
+ tsx: 'typescript',
16
+ mts: 'typescript',
17
+ cts: 'typescript',
18
+ // Web
19
+ html: 'html',
20
+ htm: 'html',
21
+ css: 'css',
22
+ scss: 'scss',
23
+ sass: 'sass',
24
+ less: 'less',
25
+ svg: 'svg',
26
+ vue: 'vue',
27
+ svelte: 'svelte',
28
+ // Systems
29
+ c: 'c',
30
+ h: 'c',
31
+ cpp: 'cpp',
32
+ cxx: 'cpp',
33
+ cc: 'cpp',
34
+ hpp: 'cpp',
35
+ rs: 'rust',
36
+ go: 'go',
37
+ zig: 'zig',
38
+ // JVM
39
+ java: 'java',
40
+ kt: 'kotlin',
41
+ kts: 'kotlin',
42
+ scala: 'scala',
43
+ groovy: 'groovy',
44
+ // Scripting
45
+ py: 'python',
46
+ pyi: 'python',
47
+ rb: 'ruby',
48
+ php: 'php',
49
+ pl: 'perl',
50
+ pm: 'perl',
51
+ lua: 'lua',
52
+ r: 'r',
53
+ jl: 'julia',
54
+ // Shell
55
+ sh: 'shell',
56
+ bash: 'shell',
57
+ zsh: 'shell',
58
+ fish: 'shell',
59
+ ps1: 'powershell',
60
+ // Data / Config
61
+ json: 'json',
62
+ jsonc: 'json',
63
+ yaml: 'yaml',
64
+ yml: 'yaml',
65
+ toml: 'toml',
66
+ xml: 'xml',
67
+ csv: 'csv',
68
+ ini: 'ini',
69
+ env: 'dotenv',
70
+ // Markup / Docs
71
+ md: 'markdown',
72
+ mdx: 'markdown',
73
+ rst: 'restructuredtext',
74
+ tex: 'latex',
75
+ // DevOps
76
+ dockerfile: 'dockerfile',
77
+ tf: 'terraform',
78
+ hcl: 'hcl',
79
+ // Database
80
+ sql: 'sql',
81
+ graphql: 'graphql',
82
+ gql: 'graphql',
83
+ prisma: 'prisma',
84
+ // Mobile
85
+ swift: 'swift',
86
+ m: 'objective-c',
87
+ dart: 'dart',
88
+ // Other
89
+ proto: 'protobuf',
90
+ ex: 'elixir',
91
+ exs: 'elixir',
92
+ erl: 'erlang',
93
+ hs: 'haskell',
94
+ ml: 'ocaml',
95
+ fs: 'fsharp',
96
+ cs: 'csharp',
97
+ vb: 'vb',
98
+ clj: 'clojure',
99
+ lisp: 'lisp',
100
+ el: 'elisp',
101
+ vim: 'vim',
102
+ nix: 'nix',
103
+ };
104
+ /** Binary file extensions (not programming languages) */
105
+ const BINARY_EXTENSIONS = new Set([
106
+ // Images
107
+ 'png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'webp', 'tiff', 'tif', 'avif',
108
+ // Fonts
109
+ 'woff', 'woff2', 'ttf', 'otf', 'eot',
110
+ // Archives
111
+ 'zip', 'tar', 'gz', 'bz2', 'xz', 'rar', '7z', 'zst',
112
+ // Executables / Libraries
113
+ 'exe', 'dll', 'so', 'dylib', 'a', 'lib', 'o', 'obj',
114
+ // Media
115
+ 'mp3', 'mp4', 'wav', 'ogg', 'flac', 'avi', 'mkv', 'mov', 'webm',
116
+ // Documents
117
+ 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
118
+ // Binary data
119
+ 'wasm', 'bin', 'dat', 'db', 'sqlite', 'sqlite3',
120
+ // Package
121
+ 'jar', 'war', 'apk', 'ipa', 'dmg', 'iso', 'img',
122
+ ]);
123
+ /**
124
+ * Detect programming language from file path extension.
125
+ * Case-insensitive. Returns undefined for unknown extensions.
126
+ */
127
+ export function detectLanguage(filePath) {
128
+ const ext = extractExtension(filePath);
129
+ if (!ext)
130
+ return undefined;
131
+ return EXTENSION_MAP[ext];
132
+ }
133
+ /**
134
+ * Check if a file extension indicates a binary file.
135
+ * Case-insensitive. Returns false for unknown extensions.
136
+ */
137
+ export function isBinaryExtension(filePath) {
138
+ const ext = extractExtension(filePath);
139
+ if (!ext)
140
+ return false;
141
+ return BINARY_EXTENSIONS.has(ext);
142
+ }
143
+ /** Extract lowercase extension from a file path (without dot). */
144
+ function extractExtension(filePath) {
145
+ // Handle filenames like "Dockerfile" (no extension)
146
+ const basename = filePath.split(/[\\/]/).pop() ?? filePath;
147
+ const lowerBasename = basename.toLowerCase();
148
+ // Special filenames
149
+ if (lowerBasename === 'dockerfile' || lowerBasename.startsWith('dockerfile.')) {
150
+ return 'dockerfile';
151
+ }
152
+ if (lowerBasename === 'makefile' || lowerBasename === 'gnumakefile') {
153
+ return 'makefile'; // not in EXTENSION_MAP — returns undefined for detectLanguage
154
+ }
155
+ const dotIdx = basename.lastIndexOf('.');
156
+ if (dotIdx === -1 || dotIdx === basename.length - 1)
157
+ return undefined;
158
+ return basename.slice(dotIdx + 1).toLowerCase();
159
+ }
@@ -0,0 +1,45 @@
1
+ import type { MagiUnit } from '../types/core.js';
2
+ interface LatencyStats {
3
+ count: number;
4
+ p50: number;
5
+ p90: number;
6
+ p99: number;
7
+ mean: number;
8
+ min: number;
9
+ max: number;
10
+ }
11
+ /**
12
+ * Tracks adapter latency and provides dynamic timeout recommendations.
13
+ *
14
+ * Uses P90-based dynamic timeouts with a margin multiplier and sliding window.
15
+ * Only successful executions are counted for timeout calculation.
16
+ */
17
+ export declare class LatencyTracker {
18
+ private readonly windowSize;
19
+ private readonly marginMultiplier;
20
+ private readonly minTimeoutMs;
21
+ private readonly maxTimeoutMs;
22
+ private records;
23
+ constructor(windowSize?: number, marginMultiplier?: number, minTimeoutMs?: number, maxTimeoutMs?: number);
24
+ /**
25
+ * Record a latency observation.
26
+ */
27
+ record(unit: MagiUnit, durationMs: number, success: boolean): void;
28
+ /**
29
+ * Get a recommended timeout for a given unit.
30
+ * Returns P90 * marginMultiplier, clamped to [minTimeoutMs, maxTimeoutMs].
31
+ * Falls back to fallbackMs when there are insufficient samples.
32
+ */
33
+ getTimeout(unit: MagiUnit, fallbackMs: number): number;
34
+ /**
35
+ * Get latency statistics for a given unit.
36
+ * Returns null if no records exist.
37
+ */
38
+ getStats(unit: MagiUnit): LatencyStats | null;
39
+ /**
40
+ * Clear all records, or records for a specific unit.
41
+ */
42
+ clear(unit?: MagiUnit): void;
43
+ private percentile;
44
+ }
45
+ export {};
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Tracks adapter latency and provides dynamic timeout recommendations.
3
+ *
4
+ * Uses P90-based dynamic timeouts with a margin multiplier and sliding window.
5
+ * Only successful executions are counted for timeout calculation.
6
+ */
7
+ export class LatencyTracker {
8
+ windowSize;
9
+ marginMultiplier;
10
+ minTimeoutMs;
11
+ maxTimeoutMs;
12
+ records = [];
13
+ constructor(windowSize = 20, marginMultiplier = 1.5, minTimeoutMs = 10_000, maxTimeoutMs = 600_000) {
14
+ this.windowSize = windowSize;
15
+ this.marginMultiplier = marginMultiplier;
16
+ this.minTimeoutMs = minTimeoutMs;
17
+ this.maxTimeoutMs = maxTimeoutMs;
18
+ }
19
+ /**
20
+ * Record a latency observation.
21
+ */
22
+ record(unit, durationMs, success) {
23
+ this.records.push({
24
+ unit,
25
+ durationMs,
26
+ success,
27
+ recordedAt: Date.now(),
28
+ });
29
+ // Evict oldest records beyond window size (per unit)
30
+ const unitRecords = this.records.filter(r => r.unit === unit);
31
+ if (unitRecords.length > this.windowSize) {
32
+ // Find the oldest record for this unit and remove it
33
+ const oldestIndex = this.records.findIndex(r => r.unit === unit);
34
+ if (oldestIndex !== -1) {
35
+ this.records.splice(oldestIndex, 1);
36
+ }
37
+ }
38
+ }
39
+ /**
40
+ * Get a recommended timeout for a given unit.
41
+ * Returns P90 * marginMultiplier, clamped to [minTimeoutMs, maxTimeoutMs].
42
+ * Falls back to fallbackMs when there are insufficient samples.
43
+ */
44
+ getTimeout(unit, fallbackMs) {
45
+ const successRecords = this.records
46
+ .filter(r => r.unit === unit && r.success)
47
+ .map(r => r.durationMs)
48
+ .sort((a, b) => a - b);
49
+ // Need at least 3 successful samples to calculate P90
50
+ if (successRecords.length < 3) {
51
+ return fallbackMs;
52
+ }
53
+ const p90 = this.percentile(successRecords, 0.90);
54
+ const recommended = Math.round(p90 * this.marginMultiplier);
55
+ return Math.max(this.minTimeoutMs, Math.min(this.maxTimeoutMs, recommended));
56
+ }
57
+ /**
58
+ * Get latency statistics for a given unit.
59
+ * Returns null if no records exist.
60
+ */
61
+ getStats(unit) {
62
+ const durations = this.records
63
+ .filter(r => r.unit === unit && r.success)
64
+ .map(r => r.durationMs)
65
+ .sort((a, b) => a - b);
66
+ if (durations.length === 0)
67
+ return null;
68
+ return {
69
+ count: durations.length,
70
+ p50: this.percentile(durations, 0.50),
71
+ p90: this.percentile(durations, 0.90),
72
+ p99: this.percentile(durations, 0.99),
73
+ mean: durations.reduce((sum, d) => sum + d, 0) / durations.length,
74
+ min: durations[0],
75
+ max: durations[durations.length - 1],
76
+ };
77
+ }
78
+ /**
79
+ * Clear all records, or records for a specific unit.
80
+ */
81
+ clear(unit) {
82
+ if (unit) {
83
+ this.records = this.records.filter(r => r.unit !== unit);
84
+ }
85
+ else {
86
+ this.records = [];
87
+ }
88
+ }
89
+ percentile(sorted, p) {
90
+ if (sorted.length === 0)
91
+ return 0;
92
+ if (sorted.length === 1)
93
+ return sorted[0];
94
+ const index = p * (sorted.length - 1);
95
+ const lower = Math.floor(index);
96
+ const upper = Math.ceil(index);
97
+ const weight = index - lower;
98
+ return sorted[lower] * (1 - weight) + sorted[upper] * weight;
99
+ }
100
+ }
@@ -0,0 +1,33 @@
1
+ import type { AuditEvent, AuditLogConfig } from '../audit/types.js';
2
+ import { AuditLogWriter } from '../audit/writer.js';
3
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
4
+ /**
5
+ * Suppress console output during TUI mode.
6
+ * Logs are buffered and flushed when TUI mode is disabled.
7
+ */
8
+ export declare function setTuiMode(enabled: boolean): void;
9
+ export declare function setLogLevel(level: LogLevel): void;
10
+ /**
11
+ * Initialize audit logging. Must be called before logger.audit() will write entries.
12
+ * If not called, audit() calls are silently ignored.
13
+ */
14
+ export declare function initAuditLog(config: AuditLogConfig): Promise<void>;
15
+ /**
16
+ * Get the current audit writer (for testing purposes).
17
+ */
18
+ export declare function getAuditWriter(): AuditLogWriter | null;
19
+ /**
20
+ * Reset audit writer (for testing teardown).
21
+ */
22
+ export declare function resetAuditWriter(): void;
23
+ export declare const logger: {
24
+ debug(message: string, data?: Record<string, unknown>): void;
25
+ info(message: string, data?: Record<string, unknown>): void;
26
+ warn(message: string, data?: Record<string, unknown>): void;
27
+ error(message: string, data?: Record<string, unknown>): void;
28
+ /**
29
+ * Write an audit log entry. Fire-and-forget -- never blocks the caller.
30
+ * If audit logging is not initialized, the call is silently ignored.
31
+ */
32
+ audit(event: AuditEvent, deliberationId: string, data: Record<string, unknown>): void;
33
+ };