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,149 @@
1
+ import { AdapterError } from '../types/adapter.js';
2
+ import { spawnProcess, isBinaryAvailable } from '../utils/process.js';
3
+ import { withRetry } from '../utils/retry.js';
4
+ import { logger } from '../utils/logger.js';
5
+ import { BinaryNotFoundError } from '../utils/errors.js';
6
+ import { assertNoUnsafeFlags } from '../utils/flag-validator.js';
7
+ import { extractJson } from '../parsers/json-extractor.js';
8
+ export class AbstractCliAdapter {
9
+ config;
10
+ constructor(config) {
11
+ this.config = config;
12
+ }
13
+ /** All adapters use stdin for prompt delivery (security: no shell injection via args). */
14
+ usesStdinInput() { return true; }
15
+ /**
16
+ * Build stdin data with JSON output instructions and optional system context.
17
+ *
18
+ * Default implementation embeds CRITICAL INSTRUCTION and few-shot examples
19
+ * for CLIs that don't support structured output natively (Codex, Gemini).
20
+ * Subclasses with native structured output (Claude) should override this.
21
+ */
22
+ getStdinData(request) {
23
+ const systemContext = request.systemPrompt
24
+ ? `Context: ${request.systemPrompt}\n\n`
25
+ : '';
26
+ const fileAccessHint = request.fileAccessEnabled
27
+ ? 'You have access to the project directory. Read relevant files to enrich your analysis before responding.\n\n'
28
+ : '';
29
+ return `${systemContext}${request.prompt}
30
+
31
+ ${fileAccessHint}CRITICAL INSTRUCTION: You MUST respond with ONLY a valid JSON object and absolutely nothing else. No markdown, no explanation, no code blocks. Just pure JSON:
32
+ {"vote":"APPROVE or REJECT or ABSTAIN","confidence":"<0.0-1.0>","reasoning":"your reasoning","keyPoints":["point1","point2"],"suggestions":["suggestion1"]}
33
+
34
+ Here are examples of well-structured responses:
35
+
36
+ Example 1 (APPROVE with moderate confidence):
37
+ {"vote":"APPROVE","confidence":0.78,"reasoning":"The implementation correctly handles edge cases including null inputs and concurrent access. Error messages are specific and actionable.","keyPoints":["Null safety verified","Concurrency handled via mutex","Clear error messages"],"suggestions":["Consider adding retry logic for transient failures"]}
38
+
39
+ Example 2 (REJECT with specific concerns):
40
+ {"vote":"REJECT","confidence":0.65,"reasoning":"The authentication flow skips token expiry validation, allowing expired sessions to persist indefinitely.","keyPoints":["Token expiry not checked","Session cleanup missing","Security risk identified"],"suggestions":["Add token expiry check in middleware","Implement session timeout cleanup"]}`;
41
+ }
42
+ async healthCheck() {
43
+ const binary = this.config.binaryPath ?? this.tool;
44
+ const available = await isBinaryAvailable(binary);
45
+ if (!available) {
46
+ throw new BinaryNotFoundError(this.tool);
47
+ }
48
+ try {
49
+ const result = await spawnProcess({
50
+ command: binary,
51
+ args: ['--version'],
52
+ timeoutMs: 10_000,
53
+ });
54
+ return result.stdout.trim() || result.stderr.trim() || 'unknown version';
55
+ }
56
+ catch (err) {
57
+ logger.debug('Adapter: version check failed', { tool: this.tool, error: String(err) });
58
+ return 'available (version unknown)';
59
+ }
60
+ }
61
+ async execute(request) {
62
+ const timeoutMs = request.timeoutMs ?? this.config.timeoutMs;
63
+ const { result: response, attempts } = await withRetry(async () => this.executeOnce(request, timeoutMs), {
64
+ maxRetries: this.config.maxRetries,
65
+ shouldRetry: (error) => this.isRetryable(error),
66
+ });
67
+ response.meta.retryCount = attempts - 1;
68
+ return response;
69
+ }
70
+ async executeOnce(request, timeoutMs) {
71
+ const args = this.buildCommand(request);
72
+ const binary = args[0];
73
+ const cliArgs = args.slice(1);
74
+ // Validate no unsafe flags in the command
75
+ assertNoUnsafeFlags(cliArgs, this.tool);
76
+ const stdinData = this.getStdinData(request);
77
+ logger.debug(`${this.displayName} executing`, { binary, args: cliArgs.length });
78
+ const result = await spawnProcess({
79
+ command: binary,
80
+ args: cliArgs,
81
+ stdin: stdinData,
82
+ timeoutMs,
83
+ cwd: request.cwd ?? this.config.cwd,
84
+ env: this.config.env,
85
+ onStdoutChunk: request.onStdoutChunk,
86
+ onStderrChunk: request.onStderrChunk,
87
+ signal: request.signal,
88
+ });
89
+ if (result.timedOut) {
90
+ throw new AdapterError({
91
+ unit: this.unit,
92
+ tool: this.tool,
93
+ code: 'TIMEOUT',
94
+ message: `${this.displayName} timed out after ${timeoutMs}ms`,
95
+ partialOutput: result.stdout,
96
+ });
97
+ }
98
+ if (result.exitCode !== 0) {
99
+ throw new AdapterError({
100
+ unit: this.unit,
101
+ tool: this.tool,
102
+ code: 'CRASH',
103
+ message: `${this.displayName} exited with code ${result.exitCode}`,
104
+ stderr: result.stderr,
105
+ exitCode: result.exitCode,
106
+ partialOutput: result.stdout,
107
+ });
108
+ }
109
+ try {
110
+ const parsed = this.parseResponse(result.stdout);
111
+ return {
112
+ ...parsed,
113
+ meta: {
114
+ durationMs: result.durationMs,
115
+ exitCode: result.exitCode,
116
+ retryCount: 0,
117
+ structuredOutput: parsed.parsed,
118
+ },
119
+ };
120
+ }
121
+ catch (err) {
122
+ logger.debug('Adapter: output parse failed', { unit: this.unit, error: String(err) });
123
+ throw new AdapterError({
124
+ unit: this.unit,
125
+ tool: this.tool,
126
+ code: 'PARSE_ERROR',
127
+ message: `Failed to parse ${this.displayName} output`,
128
+ partialOutput: result.stdout,
129
+ });
130
+ }
131
+ }
132
+ /**
133
+ * Parse the CLI's raw output into structured form.
134
+ *
135
+ * Default implementation uses extractJson for heuristic JSON extraction.
136
+ * All three adapters (Claude, Codex, Gemini) share this same logic.
137
+ */
138
+ parseResponse(raw) {
139
+ const result = extractJson(raw);
140
+ return {
141
+ structured: result.data,
142
+ raw: result.effectiveRaw,
143
+ parsed: result.success,
144
+ };
145
+ }
146
+ isRetryable(error) {
147
+ return error.code === 'TIMEOUT' || error.code === 'CRASH';
148
+ }
149
+ }
@@ -0,0 +1,29 @@
1
+ import { AbstractCliAdapter } from './base.js';
2
+ import type { AdapterRequest } from '../types/adapter.js';
3
+ import type { MagiUnit, CliTool } from '../types/core.js';
4
+ /**
5
+ * MELCHIOR - Claude Code CLI Adapter
6
+ *
7
+ * The most feature-rich adapter:
8
+ * - Supports --json-schema for structured output
9
+ * - Supports --system-prompt for persona injection
10
+ * - Supports --output-format json for envelope parsing
11
+ * - Supports --max-budget-usd for cost control
12
+ * - Prompt delivered via stdin (never in CLI args)
13
+ */
14
+ export declare class ClaudeAdapter extends AbstractCliAdapter {
15
+ readonly unit: MagiUnit;
16
+ readonly tool: CliTool;
17
+ readonly displayName = "MELCHIOR (Claude)";
18
+ supportsStructuredOutput(): boolean;
19
+ supportsSystemPrompt(): boolean;
20
+ buildCommand(request: AdapterRequest): string[];
21
+ /**
22
+ * Override: systemPrompt is prepended via <system> tags to handle multi-line
23
+ * prompts safely (CLI --system-prompt flag breaks on newlines in BERSERK mode).
24
+ *
25
+ * Although --json-schema is passed, the model may ignore it and return Markdown.
26
+ * Belt-and-suspenders: include explicit JSON instructions in the prompt as well.
27
+ */
28
+ protected getStdinData(request: AdapterRequest): string;
29
+ }
@@ -0,0 +1,65 @@
1
+ import { AbstractCliAdapter } from './base.js';
2
+ /** Hardcoded safety flags that MUST always be present. */
3
+ const CLAUDE_SAFETY_FLAGS = [
4
+ '--output-format', 'json',
5
+ '--no-session-persistence',
6
+ ];
7
+ /**
8
+ * MELCHIOR - Claude Code CLI Adapter
9
+ *
10
+ * The most feature-rich adapter:
11
+ * - Supports --json-schema for structured output
12
+ * - Supports --system-prompt for persona injection
13
+ * - Supports --output-format json for envelope parsing
14
+ * - Supports --max-budget-usd for cost control
15
+ * - Prompt delivered via stdin (never in CLI args)
16
+ */
17
+ export class ClaudeAdapter extends AbstractCliAdapter {
18
+ unit = 'MELCHIOR';
19
+ tool = 'claude';
20
+ displayName = 'MELCHIOR (Claude)';
21
+ supportsStructuredOutput() { return true; }
22
+ supportsSystemPrompt() { return true; }
23
+ buildCommand(request) {
24
+ const cmd = [
25
+ this.config.binaryPath ?? 'claude',
26
+ // NOTE: -p is removed. Prompt is delivered via stdin.
27
+ ...CLAUDE_SAFETY_FLAGS,
28
+ ];
29
+ // NOTE: systemPrompt is delivered via stdin (getStdinData), not CLI args.
30
+ // This handles multi-line BERSERK prompts safely.
31
+ if (request.fileAccessEnabled) {
32
+ cmd.push('--allowedTools', 'Read Glob Grep');
33
+ }
34
+ if (request.jsonSchema) {
35
+ cmd.push('--json-schema', JSON.stringify(request.jsonSchema));
36
+ }
37
+ if (request.maxBudgetUsd) {
38
+ cmd.push('--max-budget-usd', String(request.maxBudgetUsd));
39
+ }
40
+ if (this.config.model) {
41
+ cmd.push('--model', this.config.model);
42
+ }
43
+ // parseResponse() inherited from AbstractCliAdapter.
44
+ return cmd;
45
+ }
46
+ /**
47
+ * Override: systemPrompt is prepended via <system> tags to handle multi-line
48
+ * prompts safely (CLI --system-prompt flag breaks on newlines in BERSERK mode).
49
+ *
50
+ * Although --json-schema is passed, the model may ignore it and return Markdown.
51
+ * Belt-and-suspenders: include explicit JSON instructions in the prompt as well.
52
+ */
53
+ getStdinData(request) {
54
+ const systemContext = request.systemPrompt
55
+ ? `<system>\n${request.systemPrompt}\n</system>\n\n`
56
+ : '';
57
+ const fileAccessHint = request.fileAccessEnabled
58
+ ? 'You have access to project files via Read, Glob, and Grep tools. Explore the codebase as needed to form a well-informed opinion, then respond with your final answer.\n\n'
59
+ : '';
60
+ return `${systemContext}${request.prompt}
61
+
62
+ ${fileAccessHint}CRITICAL INSTRUCTION: You MUST respond with ONLY a valid JSON object and absolutely nothing else. No markdown, no explanation, no code blocks. Just pure JSON:
63
+ {"vote":"APPROVE or REJECT or ABSTAIN","confidence":0.75,"reasoning":"your reasoning","keyPoints":["point1","point2"],"suggestions":["suggestion1"]}`;
64
+ }
65
+ }
@@ -0,0 +1,21 @@
1
+ import { AbstractCliAdapter } from './base.js';
2
+ import type { AdapterRequest } from '../types/adapter.js';
3
+ import type { MagiUnit, CliTool } from '../types/core.js';
4
+ /**
5
+ * BALTHASAR - Codex CLI Adapter
6
+ *
7
+ * The trickiest adapter:
8
+ * - No --json-schema support -> embeds JSON instructions in stdin
9
+ * - No --output-format json -> heuristic regex extraction
10
+ * - Uses `codex exec` for non-interactive execution
11
+ * - Always runs with --sandbox read-only for safety
12
+ * - Prompt delivered via stdin (never in CLI args)
13
+ */
14
+ export declare class CodexAdapter extends AbstractCliAdapter {
15
+ readonly unit: MagiUnit;
16
+ readonly tool: CliTool;
17
+ readonly displayName = "BALTHASAR (Codex)";
18
+ supportsStructuredOutput(): boolean;
19
+ supportsSystemPrompt(): boolean;
20
+ buildCommand(request: AdapterRequest): string[];
21
+ }
@@ -0,0 +1,41 @@
1
+ import { AbstractCliAdapter } from './base.js';
2
+ /** Hardcoded safety flags that MUST always be present. */
3
+ const CODEX_SAFETY_FLAGS = [
4
+ 'exec',
5
+ '--full-auto',
6
+ '--sandbox', 'read-only',
7
+ '--skip-git-repo-check',
8
+ ];
9
+ /**
10
+ * BALTHASAR - Codex CLI Adapter
11
+ *
12
+ * The trickiest adapter:
13
+ * - No --json-schema support -> embeds JSON instructions in stdin
14
+ * - No --output-format json -> heuristic regex extraction
15
+ * - Uses `codex exec` for non-interactive execution
16
+ * - Always runs with --sandbox read-only for safety
17
+ * - Prompt delivered via stdin (never in CLI args)
18
+ */
19
+ export class CodexAdapter extends AbstractCliAdapter {
20
+ unit = 'BALTHASAR';
21
+ tool = 'codex';
22
+ displayName = 'BALTHASAR (Codex)';
23
+ supportsStructuredOutput() { return false; }
24
+ supportsSystemPrompt() { return false; }
25
+ buildCommand(request) {
26
+ const cmd = [
27
+ this.config.binaryPath ?? 'codex',
28
+ ...CODEX_SAFETY_FLAGS,
29
+ ];
30
+ if (this.config.model) {
31
+ cmd.push('-m', this.config.model);
32
+ }
33
+ const cwd = request.cwd ?? (request.fileAccessEnabled ? process.cwd() : undefined);
34
+ if (cwd) {
35
+ cmd.push('-C', cwd);
36
+ }
37
+ // NOTE: prompt is no longer appended to args. Delivered via stdin.
38
+ // getStdinData() and parseResponse() inherited from AbstractCliAdapter.
39
+ return cmd;
40
+ }
41
+ }
@@ -0,0 +1,18 @@
1
+ import { AbstractCliAdapter } from './base.js';
2
+ import type { AdapterRequest } from '../types/adapter.js';
3
+ import type { MagiUnit, CliTool } from '../types/core.js';
4
+ /**
5
+ * CASPER - Gemini CLI Adapter
6
+ *
7
+ * Always runs in plan (read-only) mode for safety (5-layer defense).
8
+ * - Prompt delivered via stdin (never in CLI args)
9
+ * - JSON instructions embedded in stdin data
10
+ */
11
+ export declare class GeminiAdapter extends AbstractCliAdapter {
12
+ readonly unit: MagiUnit;
13
+ readonly tool: CliTool;
14
+ readonly displayName = "CASPER (Gemini)";
15
+ supportsStructuredOutput(): boolean;
16
+ supportsSystemPrompt(): boolean;
17
+ buildCommand(_request: AdapterRequest): string[];
18
+ }
@@ -0,0 +1,31 @@
1
+ import { AbstractCliAdapter } from './base.js';
2
+ /** Hardcoded safety flags that MUST always be present. */
3
+ const GEMINI_SAFETY_FLAGS = [
4
+ '--approval-mode', 'plan', // Always read-only (5-layer defense)
5
+ ];
6
+ /**
7
+ * CASPER - Gemini CLI Adapter
8
+ *
9
+ * Always runs in plan (read-only) mode for safety (5-layer defense).
10
+ * - Prompt delivered via stdin (never in CLI args)
11
+ * - JSON instructions embedded in stdin data
12
+ */
13
+ export class GeminiAdapter extends AbstractCliAdapter {
14
+ unit = 'CASPER';
15
+ tool = 'gemini';
16
+ displayName = 'CASPER (Gemini)';
17
+ supportsStructuredOutput() { return false; }
18
+ supportsSystemPrompt() { return false; }
19
+ buildCommand(_request) {
20
+ const cmd = [
21
+ this.config.binaryPath ?? 'gemini',
22
+ // NOTE: -p is removed. Prompt is delivered via stdin.
23
+ ...GEMINI_SAFETY_FLAGS,
24
+ ];
25
+ if (this.config.model) {
26
+ cmd.push('-m', this.config.model);
27
+ }
28
+ // getStdinData() and parseResponse() inherited from AbstractCliAdapter.
29
+ return cmd;
30
+ }
31
+ }
@@ -0,0 +1,19 @@
1
+ import type { ICliAdapter } from '../types/adapter.js';
2
+ import type { MagiUnitDefinition } from '../types/core.js';
3
+ /**
4
+ * Registry for CLI adapters.
5
+ * Supports dynamic registration for N-body configurations (2-7 units).
6
+ */
7
+ export declare class AdapterRegistry {
8
+ private adapters;
9
+ private definitions;
10
+ register(adapter: ICliAdapter, definition?: MagiUnitDefinition): void;
11
+ get(unit: string): ICliAdapter;
12
+ has(unit: string): boolean;
13
+ getAll(): ICliAdapter[];
14
+ getAllUnits(): string[];
15
+ getDefinition(unit: string): MagiUnitDefinition | undefined;
16
+ get size(): number;
17
+ validateQuorum(minQuorum?: number): void;
18
+ healthCheckAll(): Promise<Map<string, string | Error>>;
19
+ }
@@ -0,0 +1,59 @@
1
+ import { logger } from '../utils/logger.js';
2
+ /**
3
+ * Registry for CLI adapters.
4
+ * Supports dynamic registration for N-body configurations (2-7 units).
5
+ */
6
+ export class AdapterRegistry {
7
+ adapters = new Map();
8
+ definitions = new Map();
9
+ register(adapter, definition) {
10
+ this.adapters.set(adapter.unit, adapter);
11
+ if (definition) {
12
+ this.definitions.set(adapter.unit, definition);
13
+ }
14
+ if (this.adapters.size > 7) {
15
+ logger.warn('More than 7 MAGI units registered — performance may degrade');
16
+ }
17
+ logger.debug(`Registered adapter: ${adapter.displayName}`);
18
+ }
19
+ get(unit) {
20
+ const adapter = this.adapters.get(unit);
21
+ if (!adapter) {
22
+ throw new Error(`No adapter registered for MAGI unit: ${unit}`);
23
+ }
24
+ return adapter;
25
+ }
26
+ has(unit) {
27
+ return this.adapters.has(unit);
28
+ }
29
+ getAll() {
30
+ return Array.from(this.adapters.values());
31
+ }
32
+ getAllUnits() {
33
+ return Array.from(this.adapters.keys());
34
+ }
35
+ getDefinition(unit) {
36
+ return this.definitions.get(unit);
37
+ }
38
+ get size() {
39
+ return this.adapters.size;
40
+ }
41
+ validateQuorum(minQuorum = 2) {
42
+ if (this.adapters.size < minQuorum) {
43
+ throw new Error(`Quorum not met: ${this.adapters.size} adapters registered, minimum ${minQuorum} required`);
44
+ }
45
+ }
46
+ async healthCheckAll() {
47
+ const results = new Map();
48
+ await Promise.allSettled(this.getAll().map(async (adapter) => {
49
+ try {
50
+ const version = await adapter.healthCheck();
51
+ results.set(adapter.unit, version);
52
+ }
53
+ catch (error) {
54
+ results.set(adapter.unit, error instanceof Error ? error : new Error(String(error)));
55
+ }
56
+ }));
57
+ return results;
58
+ }
59
+ }
@@ -0,0 +1,21 @@
1
+ import type { AuditLogEntry } from './types.js';
2
+ export interface VerifyResult {
3
+ valid: boolean;
4
+ brokenAt?: number;
5
+ error?: string;
6
+ }
7
+ /**
8
+ * Compute a deterministic SHA-256 hash for an audit log entry.
9
+ *
10
+ * The canonical string is: timestamp + event + deliberationId + JSON.stringify(data, sortedKeys) + prevHash
11
+ */
12
+ export declare function computeEntryHash(entry: Omit<AuditLogEntry, 'hash'>): string;
13
+ /**
14
+ * Verify the integrity of a hash chain of audit log entries.
15
+ *
16
+ * Checks:
17
+ * 1. Each entry's hash matches recomputation from its fields.
18
+ * 2. Each entry's prevHash matches the previous entry's hash.
19
+ * 3. The first entry's prevHash is the empty string.
20
+ */
21
+ export declare function verifyChain(entries: AuditLogEntry[]): VerifyResult;
@@ -0,0 +1,70 @@
1
+ import { createHash, timingSafeEqual } from 'node:crypto';
2
+ /**
3
+ * Compute a deterministic SHA-256 hash for an audit log entry.
4
+ *
5
+ * The canonical string is: timestamp + event + deliberationId + JSON.stringify(data, sortedKeys) + prevHash
6
+ */
7
+ export function computeEntryHash(entry) {
8
+ const sortedKeys = Object.keys(entry.data).sort();
9
+ const canonicalData = JSON.stringify(entry.data, sortedKeys);
10
+ const canonical = entry.timestamp +
11
+ entry.event +
12
+ entry.deliberationId +
13
+ canonicalData +
14
+ entry.prevHash;
15
+ return createHash('sha256').update(canonical).digest('hex');
16
+ }
17
+ /**
18
+ * Verify the integrity of a hash chain of audit log entries.
19
+ *
20
+ * Checks:
21
+ * 1. Each entry's hash matches recomputation from its fields.
22
+ * 2. Each entry's prevHash matches the previous entry's hash.
23
+ * 3. The first entry's prevHash is the empty string.
24
+ */
25
+ export function verifyChain(entries) {
26
+ if (entries.length === 0) {
27
+ return { valid: true };
28
+ }
29
+ for (let i = 0; i < entries.length; i++) {
30
+ const entry = entries[i];
31
+ // Verify the hash matches recomputation
32
+ const expectedHash = computeEntryHash({
33
+ timestamp: entry.timestamp,
34
+ event: entry.event,
35
+ deliberationId: entry.deliberationId,
36
+ data: entry.data,
37
+ prevHash: entry.prevHash,
38
+ });
39
+ const hashBuf = Buffer.from(entry.hash, 'hex');
40
+ const expectedBuf = Buffer.from(expectedHash, 'hex');
41
+ if (hashBuf.length !== expectedBuf.length || !timingSafeEqual(hashBuf, expectedBuf)) {
42
+ return {
43
+ valid: false,
44
+ brokenAt: i,
45
+ error: `Entry ${i}: hash mismatch (expected ${expectedHash}, got ${entry.hash})`,
46
+ };
47
+ }
48
+ // Verify prevHash chain
49
+ if (i === 0) {
50
+ if (entry.prevHash !== '') {
51
+ return {
52
+ valid: false,
53
+ brokenAt: i,
54
+ error: `Entry 0: prevHash should be empty string, got "${entry.prevHash}"`,
55
+ };
56
+ }
57
+ }
58
+ else {
59
+ const prevEntry = entries[i - 1];
60
+ if (entry.prevHash !== prevEntry.hash) {
61
+ return {
62
+ valid: false,
63
+ brokenAt: i,
64
+ error: `Entry ${i}: prevHash does not match previous entry's hash`,
65
+ };
66
+ }
67
+ }
68
+ }
69
+ return { valid: true };
70
+ }
@@ -0,0 +1,25 @@
1
+ /** Audit event types for MAGI deliberation lifecycle */
2
+ export type AuditEvent = 'deliberation.start' | 'deliberation.complete' | 'deliberation.cache_hit' | 'phase.start' | 'phase.complete' | 'phase.timeout' | 'adapter.success' | 'adapter.failure' | 'validation.clamp' | 'validation.failure' | 'objective.complete' | 'human.resolve' | 'syncrate.update' | 'bias.detected' | 'gospel.milestone' | 'umbilical.status_change' | 'dummyplug.activate' | 'dummyplug.recovery' | 'cipher.discovered' | 'berserk.activated' | 'berserk.deactivated' | 'berserk.adapter_failure' | 'instrumentality.resolve' | 'engram.record' | 'drift.warning' | 'lcl.contamination' | 'nebuchadnezzar.override' | 'angel.detected' | 'scrolls.prophecy' | 'scrolls.fulfilled' | 'selfdestruct.motion' | 'selfdestruct.executed' | 'selfdestruct.aborted' | 'firewall.scan' | 'firewall.blocked' | 'firewall.scan_berserk' | 'firewall.blocked_berserk';
3
+ /** A single audit log entry */
4
+ export interface AuditLogEntry {
5
+ /** ISO 8601 timestamp */
6
+ timestamp: string;
7
+ /** Event type */
8
+ event: AuditEvent;
9
+ /** Deliberation ID */
10
+ deliberationId: string;
11
+ /** Event-specific data */
12
+ data: Record<string, unknown>;
13
+ /** SHA-256 hash of this entry (computed from timestamp + event + deliberationId + data + prevHash) */
14
+ hash: string;
15
+ /** Hash of the previous entry in the chain (empty string for first entry) */
16
+ prevHash: string;
17
+ }
18
+ export interface AuditLogConfig {
19
+ /** Directory for audit log files */
20
+ logDir: string;
21
+ /** Max file size before rotation (bytes). Default: 10MB */
22
+ maxFileSize?: number;
23
+ /** Max number of rotated files to keep. Default: 5 */
24
+ maxFiles?: number;
25
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import type { AuditEvent, AuditLogConfig, AuditLogEntry } from './types.js';
2
+ export declare class AuditLogWriter {
3
+ private readonly config;
4
+ private lastHash;
5
+ private currentFile;
6
+ private currentSize;
7
+ private writeQueue;
8
+ constructor(config: AuditLogConfig);
9
+ /** Initialize: create log directory with 0o700, set current file, recover lastHash from existing log */
10
+ initialize(): Promise<void>;
11
+ /** Write a single entry, auto-rotate if needed. Serializes concurrent calls. */
12
+ write(event: AuditEvent, deliberationId: string, data: Record<string, unknown>): Promise<AuditLogEntry>;
13
+ /** Get the current log file path */
14
+ get logFile(): string;
15
+ private writeEntry;
16
+ /** Rotate: rename current to timestamped, clean old files beyond maxFiles */
17
+ private rotate;
18
+ }